mirror of
				https://github.com/songquanpeng/one-api.git
				synced 2025-11-04 15:53:42 +08:00 
			
		
		
		
	@@ -21,6 +21,7 @@ import EditToken from './pages/Token/EditToken';
 | 
			
		||||
import EditChannel from './pages/Channel/EditChannel';
 | 
			
		||||
import Redemption from './pages/Redemption';
 | 
			
		||||
import EditRedemption from './pages/Redemption/EditRedemption';
 | 
			
		||||
import TopUp from './pages/TopUp';
 | 
			
		||||
 | 
			
		||||
const Home = lazy(() => import('./pages/Home'));
 | 
			
		||||
const About = lazy(() => import('./pages/About'));
 | 
			
		||||
@@ -239,6 +240,16 @@ function App() {
 | 
			
		||||
          </PrivateRoute>
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
      <Route
 | 
			
		||||
        path='/topup'
 | 
			
		||||
        element={
 | 
			
		||||
        <PrivateRoute>
 | 
			
		||||
          <Suspense fallback={<Loading></Loading>}>
 | 
			
		||||
            <TopUp />
 | 
			
		||||
          </Suspense>
 | 
			
		||||
        </PrivateRoute>
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
      <Route
 | 
			
		||||
        path='/about'
 | 
			
		||||
        element={
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,12 @@ const headerButtons = [
 | 
			
		||||
    icon: 'dollar sign',
 | 
			
		||||
    admin: true,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: '充值',
 | 
			
		||||
    to: '/topup',
 | 
			
		||||
    icon: 'cart',
 | 
			
		||||
    admin: true,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    name: '用户',
 | 
			
		||||
    to: '/user',
 | 
			
		||||
 
 | 
			
		||||
@@ -36,8 +36,6 @@ const TokensTable = () => {
 | 
			
		||||
  const [searching, setSearching] = useState(false);
 | 
			
		||||
  const [showTopUpModal, setShowTopUpModal] = useState(false);
 | 
			
		||||
  const [targetTokenIdx, setTargetTokenIdx] = useState(0);
 | 
			
		||||
  const [redemptionCode, setRedemptionCode] = useState('');
 | 
			
		||||
  const [topUpLink, setTopUpLink] = useState('');
 | 
			
		||||
 | 
			
		||||
  const loadTokens = async (startIdx) => {
 | 
			
		||||
    const res = await API.get(`/api/token/?p=${startIdx}`);
 | 
			
		||||
@@ -77,13 +75,6 @@ const TokensTable = () => {
 | 
			
		||||
      .catch((reason) => {
 | 
			
		||||
        showError(reason);
 | 
			
		||||
      });
 | 
			
		||||
    let status = localStorage.getItem('status');
 | 
			
		||||
    if (status) {
 | 
			
		||||
      status = JSON.parse(status);
 | 
			
		||||
      if (status.top_up_link) {
 | 
			
		||||
        setTopUpLink(status.top_up_link);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  const manageToken = async (id, action, idx) => {
 | 
			
		||||
@@ -156,28 +147,6 @@ const TokensTable = () => {
 | 
			
		||||
    setLoading(false);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const topUp = async () => {
 | 
			
		||||
    if (redemptionCode === '') {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const res = await API.post('/api/token/topup/', {
 | 
			
		||||
      id: tokens[targetTokenIdx].id,
 | 
			
		||||
      key: redemptionCode
 | 
			
		||||
    });
 | 
			
		||||
    const { success, message, data } = res.data;
 | 
			
		||||
    if (success) {
 | 
			
		||||
      showSuccess('充值成功!');
 | 
			
		||||
      let newTokens = [...tokens];
 | 
			
		||||
      let realIdx = (activePage - 1) * ITEMS_PER_PAGE + targetTokenIdx;
 | 
			
		||||
      newTokens[realIdx].remain_quota += data;
 | 
			
		||||
      setTokens(newTokens);
 | 
			
		||||
      setRedemptionCode('');
 | 
			
		||||
      setShowTopUpModal(false);
 | 
			
		||||
    } else {
 | 
			
		||||
      showError(message);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <Form onSubmit={searchTokens}>
 | 
			
		||||
@@ -279,15 +248,6 @@ const TokensTable = () => {
 | 
			
		||||
                      >
 | 
			
		||||
                        复制
 | 
			
		||||
                      </Button>
 | 
			
		||||
                      <Button
 | 
			
		||||
                        size={'small'}
 | 
			
		||||
                        color={'yellow'}
 | 
			
		||||
                        onClick={() => {
 | 
			
		||||
                          setTargetTokenIdx(idx);
 | 
			
		||||
                          setShowTopUpModal(true);
 | 
			
		||||
                        }}>
 | 
			
		||||
                        充值
 | 
			
		||||
                      </Button>
 | 
			
		||||
                      <Popup
 | 
			
		||||
                        trigger={
 | 
			
		||||
                          <Button size='small' negative>
 | 
			
		||||
@@ -355,39 +315,6 @@ const TokensTable = () => {
 | 
			
		||||
          </Table.Row>
 | 
			
		||||
        </Table.Footer>
 | 
			
		||||
      </Table>
 | 
			
		||||
 | 
			
		||||
      <Modal
 | 
			
		||||
        onClose={() => setShowTopUpModal(false)}
 | 
			
		||||
        onOpen={() => setShowTopUpModal(true)}
 | 
			
		||||
        open={showTopUpModal}
 | 
			
		||||
        size={'mini'}
 | 
			
		||||
      >
 | 
			
		||||
        <Modal.Header>通过兑换码为令牌「{tokens[targetTokenIdx]?.name}」充值</Modal.Header>
 | 
			
		||||
        <Modal.Content>
 | 
			
		||||
          <Modal.Description>
 | 
			
		||||
            {/*<Image src={status.wechat_qrcode} fluid />*/}
 | 
			
		||||
            {
 | 
			
		||||
              topUpLink && <p>
 | 
			
		||||
                  <a target='_blank' href={topUpLink}>点击此处获取兑换码</a>
 | 
			
		||||
              </p>
 | 
			
		||||
            }
 | 
			
		||||
            <Form size='large'>
 | 
			
		||||
              <Form.Input
 | 
			
		||||
                fluid
 | 
			
		||||
                placeholder='兑换码'
 | 
			
		||||
                name='redemptionCode'
 | 
			
		||||
                value={redemptionCode}
 | 
			
		||||
                onChange={(e) => {
 | 
			
		||||
                  setRedemptionCode(e.target.value);
 | 
			
		||||
                }}
 | 
			
		||||
              />
 | 
			
		||||
              <Button color='' fluid size='large' onClick={topUp}>
 | 
			
		||||
                充值
 | 
			
		||||
              </Button>
 | 
			
		||||
            </Form>
 | 
			
		||||
          </Modal.Description>
 | 
			
		||||
        </Modal.Content>
 | 
			
		||||
      </Modal>
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import React, { useEffect, useState } from 'react';
 | 
			
		||||
import { Button, Form, Header, Segment } from 'semantic-ui-react';
 | 
			
		||||
import { useParams } from 'react-router-dom';
 | 
			
		||||
import { API, isAdmin, showError, showSuccess, timestamp2string } from '../../helpers';
 | 
			
		||||
import { API, showError, showSuccess, timestamp2string } from '../../helpers';
 | 
			
		||||
 | 
			
		||||
const EditToken = () => {
 | 
			
		||||
  const params = useParams();
 | 
			
		||||
@@ -14,7 +14,6 @@ const EditToken = () => {
 | 
			
		||||
    expired_time: -1,
 | 
			
		||||
    unlimited_quota: false
 | 
			
		||||
  };
 | 
			
		||||
  const isAdminUser = isAdmin();
 | 
			
		||||
  const [inputs, setInputs] = useState(originInputs);
 | 
			
		||||
  const { name, remain_quota, expired_time, unlimited_quota } = inputs;
 | 
			
		||||
 | 
			
		||||
@@ -107,25 +106,21 @@ const EditToken = () => {
 | 
			
		||||
              required={!isEdit}
 | 
			
		||||
            />
 | 
			
		||||
          </Form.Field>
 | 
			
		||||
          {
 | 
			
		||||
            isAdminUser && <>
 | 
			
		||||
              <Form.Field>
 | 
			
		||||
                <Form.Input
 | 
			
		||||
                  label='额度'
 | 
			
		||||
                  name='remain_quota'
 | 
			
		||||
                  placeholder={'请输入额度'}
 | 
			
		||||
                  onChange={handleInputChange}
 | 
			
		||||
                  value={remain_quota}
 | 
			
		||||
                  autoComplete='new-password'
 | 
			
		||||
                  type='number'
 | 
			
		||||
                  disabled={unlimited_quota}
 | 
			
		||||
                />
 | 
			
		||||
              </Form.Field>
 | 
			
		||||
              <Button type={'button'} style={{marginBottom: '14px'}} onClick={() => {
 | 
			
		||||
                setUnlimitedQuota();
 | 
			
		||||
              }}>{unlimited_quota ? '取消无限额度' : '设置为无限额度'}</Button>
 | 
			
		||||
            </>
 | 
			
		||||
          }
 | 
			
		||||
          <Form.Field>
 | 
			
		||||
            <Form.Input
 | 
			
		||||
              label='额度'
 | 
			
		||||
              name='remain_quota'
 | 
			
		||||
              placeholder={'请输入额度'}
 | 
			
		||||
              onChange={handleInputChange}
 | 
			
		||||
              value={remain_quota}
 | 
			
		||||
              autoComplete='new-password'
 | 
			
		||||
              type='number'
 | 
			
		||||
              disabled={unlimited_quota}
 | 
			
		||||
            />
 | 
			
		||||
          </Form.Field>
 | 
			
		||||
          <Button type={'button'} style={{ marginBottom: '14px' }} onClick={() => {
 | 
			
		||||
            setUnlimitedQuota();
 | 
			
		||||
          }}>{unlimited_quota ? '取消无限额度' : '设置为无限额度'}</Button>
 | 
			
		||||
          <Form.Field>
 | 
			
		||||
            <Form.Input
 | 
			
		||||
              label='过期时间'
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										94
									
								
								web/src/pages/TopUp/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								web/src/pages/TopUp/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
import React, { useEffect, useState } from 'react';
 | 
			
		||||
import { Button, Form, Grid, Header, Segment, Statistic } from 'semantic-ui-react';
 | 
			
		||||
import { API, showError, showSuccess } from '../../helpers';
 | 
			
		||||
 | 
			
		||||
const TopUp = () => {
 | 
			
		||||
  const [redemptionCode, setRedemptionCode] = useState('');
 | 
			
		||||
  const [topUpLink, setTopUpLink] = useState('');
 | 
			
		||||
  const [userQuota, setUserQuota] = useState(0);
 | 
			
		||||
 | 
			
		||||
  const topUp = async () => {
 | 
			
		||||
    if (redemptionCode === '') {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const res = await API.post('/api/user/topup', {
 | 
			
		||||
      key: redemptionCode
 | 
			
		||||
    });
 | 
			
		||||
    const { success, message, data } = res.data;
 | 
			
		||||
    if (success) {
 | 
			
		||||
      showSuccess('充值成功!');
 | 
			
		||||
      setUserQuota((quota) => {
 | 
			
		||||
        return quota + data;
 | 
			
		||||
      });
 | 
			
		||||
      setRedemptionCode('');
 | 
			
		||||
    } else {
 | 
			
		||||
      showError(message);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const openTopUpLink = () => {
 | 
			
		||||
    if (!topUpLink) {
 | 
			
		||||
      showError('超级管理员未设置充值链接!');
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    window.open(topUpLink, '_blank');
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const getUserQuota = async ()=>{
 | 
			
		||||
    let res  = await API.get(`/api/user/self`);
 | 
			
		||||
    const {success, message, data} = res.data;
 | 
			
		||||
    if (success) {
 | 
			
		||||
      setUserQuota(data.quota);
 | 
			
		||||
    } 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 (
 | 
			
		||||
    <Segment>
 | 
			
		||||
      <Header as='h3'>充值额度</Header>
 | 
			
		||||
      <Grid columns={2} stackable>
 | 
			
		||||
        <Grid.Column>
 | 
			
		||||
          <Form>
 | 
			
		||||
            <Form.Input
 | 
			
		||||
              placeholder='兑换码'
 | 
			
		||||
              name='redemptionCode'
 | 
			
		||||
              value={redemptionCode}
 | 
			
		||||
              onChange={(e) => {
 | 
			
		||||
                setRedemptionCode(e.target.value);
 | 
			
		||||
              }}
 | 
			
		||||
            />
 | 
			
		||||
            <Button color='green' onClick={openTopUpLink}>
 | 
			
		||||
              获取兑换码
 | 
			
		||||
            </Button>
 | 
			
		||||
            <Button color='yellow' onClick={topUp}>
 | 
			
		||||
              充值
 | 
			
		||||
            </Button>
 | 
			
		||||
          </Form>
 | 
			
		||||
        </Grid.Column>
 | 
			
		||||
        <Grid.Column>
 | 
			
		||||
          <Statistic.Group widths='one'>
 | 
			
		||||
            <Statistic>
 | 
			
		||||
              <Statistic.Value>{userQuota}</Statistic.Value>
 | 
			
		||||
              <Statistic.Label>剩余额度</Statistic.Label>
 | 
			
		||||
            </Statistic>
 | 
			
		||||
          </Statistic.Group>
 | 
			
		||||
        </Grid.Column>
 | 
			
		||||
      </Grid>
 | 
			
		||||
    </Segment>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export default TopUp;
 | 
			
		||||
		Reference in New Issue
	
	Block a user