| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import React, { useEffect, useState, useRef, useMemo } from 'react'; |
| import { |
| Banner, |
| Button, |
| Col, |
| Form, |
| Row, |
| Spin, |
| Modal, |
| Select, |
| InputGroup, |
| Input, |
| } from '@douyinfe/semi-ui'; |
| import { |
| compareObjects, |
| API, |
| showError, |
| showSuccess, |
| showWarning, |
| } from '../../../helpers'; |
| import { useTranslation } from 'react-i18next'; |
|
|
| export default function GeneralSettings(props) { |
| const { t } = useTranslation(); |
| const [loading, setLoading] = useState(false); |
| const [showQuotaWarning, setShowQuotaWarning] = useState(false); |
| const [inputs, setInputs] = useState({ |
| TopUpLink: '', |
| 'general_setting.docs_link': '', |
| 'general_setting.quota_display_type': 'USD', |
| 'general_setting.custom_currency_symbol': '¤', |
| 'general_setting.custom_currency_exchange_rate': '', |
| QuotaPerUnit: '', |
| RetryTimes: '', |
| USDExchangeRate: '', |
| DisplayTokenStatEnabled: false, |
| DefaultCollapseSidebar: false, |
| DemoSiteEnabled: false, |
| SelfUseModeEnabled: false, |
| }); |
| const refForm = useRef(); |
| const [inputsRow, setInputsRow] = useState(inputs); |
|
|
| function handleFieldChange(fieldName) { |
| return (value) => { |
| setInputs((inputs) => ({ ...inputs, [fieldName]: value })); |
| }; |
| } |
|
|
| function onSubmit() { |
| const updateArray = compareObjects(inputs, inputsRow); |
| if (!updateArray.length) return showWarning(t('你似乎并没有修改什么')); |
| 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(t('部分保存失败,请重试')); |
| } |
| showSuccess(t('保存成功')); |
| props.refresh(); |
| }) |
| .catch(() => { |
| showError(t('保存失败,请重试')); |
| }) |
| .finally(() => { |
| setLoading(false); |
| }); |
| } |
|
|
| |
| const combinedRate = useMemo(() => { |
| const type = inputs['general_setting.quota_display_type']; |
| if (type === 'USD') return '1'; |
| if (type === 'CNY') return String(inputs['USDExchangeRate'] || ''); |
| if (type === 'TOKENS') return String(inputs['QuotaPerUnit'] || ''); |
| if (type === 'CUSTOM') |
| return String( |
| inputs['general_setting.custom_currency_exchange_rate'] || '', |
| ); |
| return ''; |
| }, [inputs]); |
|
|
| const onCombinedRateChange = (val) => { |
| const type = inputs['general_setting.quota_display_type']; |
| if (type === 'CNY') { |
| handleFieldChange('USDExchangeRate')(val); |
| } else if (type === 'TOKENS') { |
| handleFieldChange('QuotaPerUnit')(val); |
| } else if (type === 'CUSTOM') { |
| handleFieldChange('general_setting.custom_currency_exchange_rate')(val); |
| } |
| }; |
|
|
| useEffect(() => { |
| const currentInputs = {}; |
| for (let key in props.options) { |
| if (Object.keys(inputs).includes(key)) { |
| currentInputs[key] = props.options[key]; |
| } |
| } |
| |
| if ( |
| currentInputs['general_setting.quota_display_type'] === undefined && |
| props.options?.DisplayInCurrencyEnabled !== undefined |
| ) { |
| currentInputs['general_setting.quota_display_type'] = props.options |
| .DisplayInCurrencyEnabled |
| ? 'USD' |
| : 'TOKENS'; |
| } |
| |
| if (props.options['general_setting.custom_currency_symbol'] !== undefined) { |
| currentInputs['general_setting.custom_currency_symbol'] = |
| props.options['general_setting.custom_currency_symbol']; |
| } |
| if ( |
| props.options['general_setting.custom_currency_exchange_rate'] !== |
| undefined |
| ) { |
| currentInputs['general_setting.custom_currency_exchange_rate'] = |
| props.options['general_setting.custom_currency_exchange_rate']; |
| } |
| 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={t('通用设置')}> |
| <Row gutter={16}> |
| <Col xs={24} sm={12} md={8} lg={8} xl={8}> |
| <Form.Input |
| field={'TopUpLink'} |
| label={t('充值链接')} |
| initValue={''} |
| placeholder={t('例如发卡网站的购买链接')} |
| onChange={handleFieldChange('TopUpLink')} |
| showClear |
| /> |
| </Col> |
| <Col xs={24} sm={12} md={8} lg={8} xl={8}> |
| <Form.Input |
| field={'general_setting.docs_link'} |
| label={t('文档地址')} |
| initValue={''} |
| placeholder={t('例如 https://docs.newapi.pro')} |
| onChange={handleFieldChange('general_setting.docs_link')} |
| showClear |
| /> |
| </Col> |
| {/* 单位美元额度已合入汇率组合控件(TOKENS 模式下编辑),不再单独展示 */} |
| <Col xs={24} sm={12} md={8} lg={8} xl={8}> |
| <Form.Input |
| field={'RetryTimes'} |
| label={t('失败重试次数')} |
| initValue={''} |
| placeholder={t('失败重试次数')} |
| onChange={handleFieldChange('RetryTimes')} |
| showClear |
| /> |
| </Col> |
| <Col xs={24} sm={12} md={8} lg={8} xl={8}> |
| <Form.Slot label={t('站点额度展示类型及汇率')}> |
| <InputGroup style={{ width: '100%' }}> |
| <Input |
| prefix={'1 USD = '} |
| style={{ width: '50%' }} |
| value={combinedRate} |
| onChange={onCombinedRateChange} |
| disabled={ |
| inputs['general_setting.quota_display_type'] === 'USD' |
| } |
| /> |
| <Select |
| style={{ width: '50%' }} |
| value={inputs['general_setting.quota_display_type']} |
| onChange={handleFieldChange( |
| 'general_setting.quota_display_type', |
| )} |
| > |
| <Select.Option value='USD'>USD ($)</Select.Option> |
| <Select.Option value='CNY'>CNY (¥)</Select.Option> |
| <Select.Option value='TOKENS'>Tokens</Select.Option> |
| <Select.Option value='CUSTOM'> |
| {t('自定义货币')} |
| </Select.Option> |
| </Select> |
| </InputGroup> |
| </Form.Slot> |
| </Col> |
| <Col xs={24} sm={12} md={8} lg={8} xl={8}> |
| <Form.Input |
| field={'general_setting.custom_currency_symbol'} |
| label={t('自定义货币符号')} |
| placeholder={t('例如 €, £, Rp, ₩, ₹...')} |
| onChange={handleFieldChange( |
| 'general_setting.custom_currency_symbol', |
| )} |
| showClear |
| disabled={ |
| inputs['general_setting.quota_display_type'] !== 'CUSTOM' |
| } |
| /> |
| </Col> |
| </Row> |
| <Row gutter={16}> |
| <Col xs={24} sm={12} md={8} lg={8} xl={8}> |
| <Form.Switch |
| field={'DisplayTokenStatEnabled'} |
| label={t('额度查询接口返回令牌额度而非用户额度')} |
| size='default' |
| checkedText='|' |
| uncheckedText='〇' |
| onChange={handleFieldChange('DisplayTokenStatEnabled')} |
| /> |
| </Col> |
| <Col xs={24} sm={12} md={8} lg={8} xl={8}> |
| <Form.Switch |
| field={'DefaultCollapseSidebar'} |
| label={t('默认折叠侧边栏')} |
| size='default' |
| checkedText='|' |
| uncheckedText='〇' |
| onChange={handleFieldChange('DefaultCollapseSidebar')} |
| /> |
| </Col> |
| <Col xs={24} sm={12} md={8} lg={8} xl={8}> |
| <Form.Switch |
| field={'DemoSiteEnabled'} |
| label={t('演示站点模式')} |
| size='default' |
| checkedText='|' |
| uncheckedText='〇' |
| onChange={handleFieldChange('DemoSiteEnabled')} |
| /> |
| </Col> |
| <Col xs={24} sm={12} md={8} lg={8} xl={8}> |
| <Form.Switch |
| field={'SelfUseModeEnabled'} |
| label={t('自用模式')} |
| extraText={t('开启后不限制:必须设置模型倍率')} |
| size='default' |
| checkedText='|' |
| uncheckedText='〇' |
| onChange={handleFieldChange('SelfUseModeEnabled')} |
| /> |
| </Col> |
| </Row> |
| <Row> |
| <Button size='default' onClick={onSubmit}> |
| {t('保存通用设置')} |
| </Button> |
| </Row> |
| </Form.Section> |
| </Form> |
| </Spin> |
| |
| <Modal |
| title={t('警告')} |
| visible={showQuotaWarning} |
| onOk={() => setShowQuotaWarning(false)} |
| onCancel={() => setShowQuotaWarning(false)} |
| closeOnEsc={true} |
| width={500} |
| > |
| <Banner |
| type='warning' |
| description={t( |
| '此设置用于系统内部计算,默认值500000是为了精确到6位小数点设计,不推荐修改。', |
| )} |
| bordered |
| fullMode={false} |
| closeIcon={null} |
| /> |
| </Modal> |
| </> |
| ); |
| } |
|
|