| | import React, { useContext, useEffect, useRef, useState } from 'react'; |
| | import { |
| | Banner, |
| | Button, |
| | Col, |
| | Form, |
| | Row, |
| | Modal, |
| | Space, |
| | Card, |
| | } from '@douyinfe/semi-ui'; |
| | import { API, showError, showSuccess, timestamp2string } from '../../helpers'; |
| | import { marked } from 'marked'; |
| | import { useTranslation } from 'react-i18next'; |
| | import { StatusContext } from '../../context/Status/index.js'; |
| | import Text from '@douyinfe/semi-ui/lib/es/typography/text'; |
| |
|
| | const OtherSetting = () => { |
| | const { t } = useTranslation(); |
| | let [inputs, setInputs] = useState({ |
| | Notice: '', |
| | SystemName: '', |
| | Logo: '', |
| | Footer: '', |
| | About: '', |
| | HomePageContent: '', |
| | }); |
| | let [loading, setLoading] = useState(false); |
| | const [showUpdateModal, setShowUpdateModal] = useState(false); |
| | const [statusState, statusDispatch] = useContext(StatusContext); |
| | const [updateData, setUpdateData] = useState({ |
| | tag_name: '', |
| | content: '', |
| | }); |
| |
|
| | const updateOption = async (key, value) => { |
| | setLoading(true); |
| | 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 [loadingInput, setLoadingInput] = useState({ |
| | Notice: false, |
| | SystemName: false, |
| | Logo: false, |
| | HomePageContent: false, |
| | About: false, |
| | Footer: false, |
| | CheckUpdate: false, |
| | }); |
| | const handleInputChange = async (value, e) => { |
| | const name = e.target.id; |
| | setInputs((inputs) => ({ ...inputs, [name]: value })); |
| | }; |
| |
|
| | |
| | const formAPISettingGeneral = useRef(); |
| | |
| | const submitNotice = async () => { |
| | try { |
| | setLoadingInput((loadingInput) => ({ ...loadingInput, Notice: true })); |
| | await updateOption('Notice', inputs.Notice); |
| | showSuccess(t('公告已更新')); |
| | } catch (error) { |
| | console.error(t('公告更新失败'), error); |
| | showError(t('公告更新失败')); |
| | } finally { |
| | setLoadingInput((loadingInput) => ({ ...loadingInput, Notice: false })); |
| | } |
| | }; |
| | |
| | const formAPIPersonalization = useRef(); |
| | |
| | const submitSystemName = async () => { |
| | try { |
| | setLoadingInput((loadingInput) => ({ |
| | ...loadingInput, |
| | SystemName: true, |
| | })); |
| | await updateOption('SystemName', inputs.SystemName); |
| | showSuccess(t('系统名称已更新')); |
| | } catch (error) { |
| | console.error(t('系统名称更新失败'), error); |
| | showError(t('系统名称更新失败')); |
| | } finally { |
| | setLoadingInput((loadingInput) => ({ |
| | ...loadingInput, |
| | SystemName: false, |
| | })); |
| | } |
| | }; |
| |
|
| | |
| | const submitLogo = async () => { |
| | try { |
| | setLoadingInput((loadingInput) => ({ ...loadingInput, Logo: true })); |
| | await updateOption('Logo', inputs.Logo); |
| | showSuccess('Logo 已更新'); |
| | } catch (error) { |
| | console.error('Logo 更新失败', error); |
| | showError('Logo 更新失败'); |
| | } finally { |
| | setLoadingInput((loadingInput) => ({ ...loadingInput, Logo: false })); |
| | } |
| | }; |
| | |
| | const submitOption = async (key) => { |
| | try { |
| | setLoadingInput((loadingInput) => ({ |
| | ...loadingInput, |
| | HomePageContent: true, |
| | })); |
| | await updateOption(key, inputs[key]); |
| | showSuccess('首页内容已更新'); |
| | } catch (error) { |
| | console.error('首页内容更新失败', error); |
| | showError('首页内容更新失败'); |
| | } finally { |
| | setLoadingInput((loadingInput) => ({ |
| | ...loadingInput, |
| | HomePageContent: false, |
| | })); |
| | } |
| | }; |
| | |
| | const submitAbout = async () => { |
| | try { |
| | setLoadingInput((loadingInput) => ({ ...loadingInput, About: true })); |
| | await updateOption('About', inputs.About); |
| | showSuccess('关于内容已更新'); |
| | } catch (error) { |
| | console.error('关于内容更新失败', error); |
| | showError('关于内容更新失败'); |
| | } finally { |
| | setLoadingInput((loadingInput) => ({ ...loadingInput, About: false })); |
| | } |
| | }; |
| | |
| | const submitFooter = async () => { |
| | try { |
| | setLoadingInput((loadingInput) => ({ ...loadingInput, Footer: true })); |
| | await updateOption('Footer', inputs.Footer); |
| | showSuccess('页脚内容已更新'); |
| | } catch (error) { |
| | console.error('页脚内容更新失败', error); |
| | showError('页脚内容更新失败'); |
| | } finally { |
| | setLoadingInput((loadingInput) => ({ ...loadingInput, Footer: false })); |
| | } |
| | }; |
| |
|
| | const checkUpdate = async () => { |
| | try { |
| | setLoadingInput((loadingInput) => ({ |
| | ...loadingInput, |
| | CheckUpdate: true, |
| | })); |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | const res = await fetch( |
| | 'https://api.github.com/repos/Calcium-Ion/new-api/releases/latest', |
| | { |
| | headers: { |
| | Accept: 'application/json', |
| | 'Content-Type': 'application/json', |
| | |
| | 'User-Agent': 'new-api-update-checker', |
| | }, |
| | }, |
| | ).then((response) => response.json()); |
| |
|
| | |
| | |
| | |
| |
|
| | const { tag_name, body } = res; |
| | if (tag_name === statusState?.status?.version) { |
| | showSuccess(`已是最新版本:${tag_name}`); |
| | } else { |
| | setUpdateData({ |
| | tag_name: tag_name, |
| | content: marked.parse(body), |
| | }); |
| | setShowUpdateModal(true); |
| | } |
| | } catch (error) { |
| | console.error('Failed to check for updates:', error); |
| | showError('检查更新失败,请稍后再试'); |
| | } finally { |
| | setLoadingInput((loadingInput) => ({ |
| | ...loadingInput, |
| | CheckUpdate: false, |
| | })); |
| | } |
| | }; |
| | const getOptions = async () => { |
| | const res = await API.get('/api/option/'); |
| | const { success, message, data } = res.data; |
| | if (success) { |
| | let newInputs = {}; |
| | data.forEach((item) => { |
| | if (item.key in inputs) { |
| | newInputs[item.key] = item.value; |
| | } |
| | }); |
| | setInputs(newInputs); |
| | formAPISettingGeneral.current.setValues(newInputs); |
| | formAPIPersonalization.current.setValues(newInputs); |
| | } else { |
| | showError(message); |
| | } |
| | }; |
| |
|
| | useEffect(() => { |
| | getOptions(); |
| | }, []); |
| |
|
| | |
| | const openGitHubRelease = () => { |
| | window.open( |
| | `https://github.com/Calcium-Ion/new-api/releases/tag/${updateData.tag_name}`, |
| | '_blank', |
| | ); |
| | }; |
| |
|
| | const getStartTimeString = () => { |
| | const timestamp = statusState?.status?.start_time; |
| | return statusState.status ? timestamp2string(timestamp) : ''; |
| | }; |
| |
|
| | return ( |
| | <Row> |
| | <Col |
| | span={24} |
| | style={{ |
| | marginTop: '10px', |
| | display: 'flex', |
| | flexDirection: 'column', |
| | gap: '10px', |
| | }} |
| | > |
| | {/* 版本信息 */} |
| | <Form> |
| | <Card> |
| | <Form.Section text={t('系统信息')}> |
| | <Row> |
| | <Col span={16}> |
| | <Space> |
| | <Text> |
| | {t('当前版本')}: |
| | {statusState?.status?.version || t('未知')} |
| | </Text> |
| | <Button |
| | type='primary' |
| | onClick={checkUpdate} |
| | loading={loadingInput['CheckUpdate']} |
| | > |
| | {t('检查更新')} |
| | </Button> |
| | </Space> |
| | </Col> |
| | </Row> |
| | <Row> |
| | <Col span={16}> |
| | <Text> |
| | {t('启动时间')}:{getStartTimeString()} |
| | </Text> |
| | </Col> |
| | </Row> |
| | </Form.Section> |
| | </Card> |
| | </Form> |
| | {/* 通用设置 */} |
| | <Form |
| | values={inputs} |
| | getFormApi={(formAPI) => (formAPISettingGeneral.current = formAPI)} |
| | > |
| | <Card> |
| | <Form.Section text={t('通用设置')}> |
| | <Form.TextArea |
| | label={t('公告')} |
| | placeholder={t( |
| | '在此输入新的公告内容,支持 Markdown & HTML 代码', |
| | )} |
| | field={'Notice'} |
| | onChange={handleInputChange} |
| | style={{ fontFamily: 'JetBrains Mono, Consolas' }} |
| | autosize={{ minRows: 6, maxRows: 12 }} |
| | /> |
| | <Button onClick={submitNotice} loading={loadingInput['Notice']}> |
| | {t('设置公告')} |
| | </Button> |
| | </Form.Section> |
| | </Card> |
| | </Form> |
| | {/* 个性化设置 */} |
| | <Form |
| | values={inputs} |
| | getFormApi={(formAPI) => (formAPIPersonalization.current = formAPI)} |
| | > |
| | <Card> |
| | <Form.Section text={t('个性化设置')}> |
| | <Form.Input |
| | label={t('系统名称')} |
| | placeholder={t('在此输入系统名称')} |
| | field={'SystemName'} |
| | onChange={handleInputChange} |
| | /> |
| | <Button |
| | onClick={submitSystemName} |
| | loading={loadingInput['SystemName']} |
| | > |
| | {t('设置系统名称')} |
| | </Button> |
| | <Form.Input |
| | label={t('Logo 图片地址')} |
| | placeholder={t('在此输入 Logo 图片地址')} |
| | field={'Logo'} |
| | onChange={handleInputChange} |
| | /> |
| | <Button onClick={submitLogo} loading={loadingInput['Logo']}> |
| | {t('设置 Logo')} |
| | </Button> |
| | <Form.TextArea |
| | label={t('首页内容')} |
| | placeholder={t( |
| | '在此输入首页内容,支持 Markdown & HTML 代码,设置后首页的状态信息将不再显示。如果输入的是一个链接,则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为首页', |
| | )} |
| | field={'HomePageContent'} |
| | onChange={handleInputChange} |
| | style={{ fontFamily: 'JetBrains Mono, Consolas' }} |
| | autosize={{ minRows: 6, maxRows: 12 }} |
| | /> |
| | <Button |
| | onClick={() => submitOption('HomePageContent')} |
| | loading={loadingInput['HomePageContent']} |
| | > |
| | {t('设置首页内容')} |
| | </Button> |
| | <Form.TextArea |
| | label={t('关于')} |
| | placeholder={t( |
| | '在此输入新的关于内容,支持 Markdown & HTML 代码。如果输入的是一个链接,则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为关于页面', |
| | )} |
| | field={'About'} |
| | onChange={handleInputChange} |
| | style={{ fontFamily: 'JetBrains Mono, Consolas' }} |
| | autosize={{ minRows: 6, maxRows: 12 }} |
| | /> |
| | <Button onClick={submitAbout} loading={loadingInput['About']}> |
| | {t('设置关于')} |
| | </Button> |
| | {/* */} |
| | <Banner |
| | fullMode={false} |
| | type='info' |
| | description={t( |
| | '移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目', |
| | )} |
| | closeIcon={null} |
| | style={{ marginTop: 15 }} |
| | /> |
| | <Form.Input |
| | label={t('页脚')} |
| | placeholder={t( |
| | '在此输入新的页脚,留空则使用默认页脚,支持 HTML 代码', |
| | )} |
| | field={'Footer'} |
| | onChange={handleInputChange} |
| | /> |
| | <Button onClick={submitFooter} loading={loadingInput['Footer']}> |
| | {t('设置页脚')} |
| | </Button> |
| | </Form.Section> |
| | </Card> |
| | </Form> |
| | </Col> |
| | <Modal |
| | title={t('新版本') + ':' + updateData.tag_name} |
| | visible={showUpdateModal} |
| | onCancel={() => setShowUpdateModal(false)} |
| | footer={[ |
| | <Button |
| | key='details' |
| | type='primary' |
| | onClick={() => { |
| | setShowUpdateModal(false); |
| | openGitHubRelease(); |
| | }} |
| | > |
| | {t('详情')} |
| | </Button>, |
| | ]} |
| | > |
| | <div dangerouslySetInnerHTML={{ __html: updateData.content }}></div> |
| | </Modal> |
| | </Row> |
| | ); |
| | }; |
| |
|
| | export default OtherSetting; |
| |
|