mirror of
https://github.com/songquanpeng/one-api.git
synced 2025-11-12 11:23:42 +08:00
254 lines
8.1 KiB
JavaScript
254 lines
8.1 KiB
JavaScript
import React, { useEffect, useState } from 'react';
|
|
import {
|
|
Button,
|
|
Form,
|
|
Grid,
|
|
Header,
|
|
Card,
|
|
Statistic,
|
|
Divider,
|
|
} from 'semantic-ui-react';
|
|
import { API, showError, showInfo, showSuccess } from '../../helpers';
|
|
import { renderQuota } from '../../helpers/render';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
const TopUp = () => {
|
|
const { t } = useTranslation();
|
|
const [redemptionCode, setRedemptionCode] = useState('');
|
|
const [topUpLink, setTopUpLink] = useState('');
|
|
const [userQuota, setUserQuota] = useState(0);
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [user, setUser] = useState({});
|
|
|
|
const topUp = async () => {
|
|
if (redemptionCode === '') {
|
|
showInfo(t('topup.redeem_code.empty_code'));
|
|
return;
|
|
}
|
|
setIsSubmitting(true);
|
|
try {
|
|
const res = await API.post('/api/user/topup', {
|
|
key: redemptionCode,
|
|
});
|
|
const { success, message, data } = res.data;
|
|
if (success) {
|
|
showSuccess(t('topup.redeem_code.success'));
|
|
setUserQuota((quota) => {
|
|
return quota + data;
|
|
});
|
|
setRedemptionCode('');
|
|
} else {
|
|
showError(message);
|
|
}
|
|
} catch (err) {
|
|
showError(t('topup.redeem_code.request_failed'));
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
const openTopUpLink = () => {
|
|
if (!topUpLink) {
|
|
showError(t('topup.redeem_code.no_link'));
|
|
return;
|
|
}
|
|
let url = new URL(topUpLink);
|
|
let username = user.username;
|
|
let user_id = user.id;
|
|
url.searchParams.append('username', username);
|
|
url.searchParams.append('user_id', user_id);
|
|
url.searchParams.append('transaction_id', crypto.randomUUID());
|
|
window.open(url.toString(), '_blank');
|
|
};
|
|
|
|
const getUserQuota = async () => {
|
|
let res = await API.get(`/api/user/self`);
|
|
const { success, message, data } = res.data;
|
|
if (success) {
|
|
setUserQuota(data.quota);
|
|
setUser(data);
|
|
} else {
|
|
showError(message);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
let status = localStorage.getItem('status');
|
|
if (status) {
|
|
status = JSON.parse(status);
|
|
if (status.top_up_link) {
|
|
setTopUpLink(status.top_up_link);
|
|
}
|
|
}
|
|
getUserQuota().then();
|
|
}, []);
|
|
|
|
return (
|
|
<div className='dashboard-container'>
|
|
<Card fluid className='chart-card'>
|
|
<Card.Content>
|
|
<Card.Header>
|
|
<Header as='h2'>{t('topup.title')}</Header>
|
|
</Card.Header>
|
|
|
|
<Grid columns={2} stackable>
|
|
<Grid.Column>
|
|
<Card
|
|
fluid
|
|
style={{
|
|
height: '100%',
|
|
boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
|
|
}}
|
|
>
|
|
<Card.Content
|
|
style={{
|
|
height: '100%',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
}}
|
|
>
|
|
<Card.Header>
|
|
<Header as='h3' style={{ color: '#2185d0', margin: '1em' }}>
|
|
<i className='credit card icon'></i>
|
|
{t('topup.get_code.title')}
|
|
</Header>
|
|
</Card.Header>
|
|
<Card.Description
|
|
style={{
|
|
flex: 1,
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
flex: 1,
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
justifyContent: 'space-between',
|
|
}}
|
|
>
|
|
<div style={{ textAlign: 'center', paddingTop: '1em' }}>
|
|
<Statistic>
|
|
<Statistic.Value style={{ color: '#2185d0' }}>
|
|
{renderQuota(userQuota)}
|
|
</Statistic.Value>
|
|
<Statistic.Label>
|
|
{t('topup.get_code.current_quota')}
|
|
</Statistic.Label>
|
|
</Statistic>
|
|
</div>
|
|
|
|
<div
|
|
style={{ textAlign: 'center', paddingBottom: '1em' }}
|
|
>
|
|
<Button
|
|
primary
|
|
size='large'
|
|
onClick={openTopUpLink}
|
|
style={{ width: '80%' }}
|
|
>
|
|
{t('topup.get_code.button')}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</Card.Description>
|
|
</Card.Content>
|
|
</Card>
|
|
</Grid.Column>
|
|
|
|
<Grid.Column>
|
|
<Card
|
|
fluid
|
|
style={{
|
|
height: '100%',
|
|
boxShadow: '0 1px 3px rgba(0,0,0,0.1)',
|
|
}}
|
|
>
|
|
<Card.Content
|
|
style={{
|
|
height: '100%',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
}}
|
|
>
|
|
<Card.Header>
|
|
<Header as='h3' style={{ color: '#21ba45', margin: '1em' }}>
|
|
<i className='ticket alternate icon'></i>
|
|
{t('topup.redeem_code.title')}
|
|
</Header>
|
|
</Card.Header>
|
|
<Card.Description
|
|
style={{
|
|
flex: 1,
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
}}
|
|
>
|
|
<div
|
|
style={{
|
|
flex: 1,
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
justifyContent: 'space-between',
|
|
}}
|
|
>
|
|
<Form.Input
|
|
fluid
|
|
icon='key'
|
|
iconPosition='left'
|
|
placeholder={t('topup.redeem_code.placeholder')}
|
|
value={redemptionCode}
|
|
onChange={(e) => {
|
|
setRedemptionCode(e.target.value);
|
|
}}
|
|
onPaste={(e) => {
|
|
e.preventDefault();
|
|
const pastedText = e.clipboardData.getData('text');
|
|
setRedemptionCode(pastedText.trim());
|
|
}}
|
|
action={
|
|
<Button
|
|
icon='paste'
|
|
content={t('topup.redeem_code.paste')}
|
|
onClick={async () => {
|
|
try {
|
|
const text =
|
|
await navigator.clipboard.readText();
|
|
setRedemptionCode(text.trim());
|
|
} catch (err) {
|
|
showError(t('topup.redeem_code.paste_error'));
|
|
}
|
|
}}
|
|
/>
|
|
}
|
|
/>
|
|
|
|
<div style={{ paddingBottom: '1em' }}>
|
|
<Button
|
|
color='green'
|
|
fluid
|
|
size='large'
|
|
onClick={topUp}
|
|
loading={isSubmitting}
|
|
disabled={isSubmitting}
|
|
>
|
|
{isSubmitting
|
|
? t('topup.redeem_code.submitting')
|
|
: t('topup.redeem_code.submit')}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</Card.Description>
|
|
</Card.Content>
|
|
</Card>
|
|
</Grid.Column>
|
|
</Grid>
|
|
</Card.Content>
|
|
</Card>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default TopUp;
|