parent
7c56a36a1c
commit
01abed0a30
@ -50,10 +50,10 @@ var WeChatAccountQRCodeImageURL = ""
|
|||||||
var TurnstileSiteKey = ""
|
var TurnstileSiteKey = ""
|
||||||
var TurnstileSecretKey = ""
|
var TurnstileSecretKey = ""
|
||||||
|
|
||||||
var QuotaForNewUser = 100
|
var QuotaForNewUser = 0
|
||||||
var ChannelDisableThreshold = 5.0
|
var ChannelDisableThreshold = 5.0
|
||||||
var AutomaticDisableChannelEnabled = false
|
var AutomaticDisableChannelEnabled = false
|
||||||
var QuotaRemindThreshold = 1000 // TODO: QuotaRemindThreshold
|
var QuotaRemindThreshold = 1000
|
||||||
|
|
||||||
var RootUserEmail = ""
|
var RootUserEmail = ""
|
||||||
|
|
||||||
|
@ -100,7 +100,6 @@ func GetTokenStatus(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AddToken(c *gin.Context) {
|
func AddToken(c *gin.Context) {
|
||||||
isAdmin := c.GetInt("role") >= common.RoleAdminUser
|
|
||||||
token := model.Token{}
|
token := model.Token{}
|
||||||
err := c.ShouldBindJSON(&token)
|
err := c.ShouldBindJSON(&token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -124,21 +123,8 @@ func AddToken(c *gin.Context) {
|
|||||||
CreatedTime: common.GetTimestamp(),
|
CreatedTime: common.GetTimestamp(),
|
||||||
AccessedTime: common.GetTimestamp(),
|
AccessedTime: common.GetTimestamp(),
|
||||||
ExpiredTime: token.ExpiredTime,
|
ExpiredTime: token.ExpiredTime,
|
||||||
}
|
RemainQuota: token.RemainQuota,
|
||||||
if isAdmin {
|
UnlimitedQuota: token.UnlimitedQuota,
|
||||||
cleanToken.RemainQuota = token.RemainQuota
|
|
||||||
cleanToken.UnlimitedQuota = token.UnlimitedQuota
|
|
||||||
} else {
|
|
||||||
userId := c.GetInt("id")
|
|
||||||
quota, err := model.GetUserQuota(userId)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
|
||||||
"success": false,
|
|
||||||
"message": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cleanToken.RemainQuota = quota
|
|
||||||
}
|
}
|
||||||
err = cleanToken.Insert()
|
err = cleanToken.Insert()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -148,10 +134,6 @@ func AddToken(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !isAdmin {
|
|
||||||
// update user quota
|
|
||||||
err = model.DecreaseUserQuota(c.GetInt("id"), cleanToken.RemainQuota)
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"success": true,
|
"success": true,
|
||||||
"message": "",
|
"message": "",
|
||||||
@ -240,34 +222,3 @@ func UpdateToken(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type topUpRequest struct {
|
|
||||||
Id int `json:"id"`
|
|
||||||
Key string `json:"key"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func TopUp(c *gin.Context) {
|
|
||||||
req := topUpRequest{}
|
|
||||||
err := c.ShouldBindJSON(&req)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
|
||||||
"success": false,
|
|
||||||
"message": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
quota, err := model.Redeem(req.Key, req.Id)
|
|
||||||
if err != nil {
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
|
||||||
"success": false,
|
|
||||||
"message": err.Error(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
|
||||||
"success": true,
|
|
||||||
"message": "",
|
|
||||||
"data": quota,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
@ -654,3 +654,34 @@ func EmailBind(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type topUpRequest struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TopUp(c *gin.Context) {
|
||||||
|
req := topUpRequest{}
|
||||||
|
err := c.ShouldBindJSON(&req)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"message": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id := c.GetInt("id")
|
||||||
|
quota, err := model.Redeem(req.Key, id)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": false,
|
||||||
|
"message": err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"success": true,
|
||||||
|
"message": "",
|
||||||
|
"data": quota,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -40,12 +40,12 @@ func GetRedemptionById(id int) (*Redemption, error) {
|
|||||||
return &redemption, err
|
return &redemption, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func Redeem(key string, tokenId int) (quota int, err error) {
|
func Redeem(key string, userId int) (quota int, err error) {
|
||||||
if key == "" {
|
if key == "" {
|
||||||
return 0, errors.New("未提供兑换码")
|
return 0, errors.New("未提供兑换码")
|
||||||
}
|
}
|
||||||
if tokenId == 0 {
|
if userId == 0 {
|
||||||
return 0, errors.New("未提供 token id")
|
return 0, errors.New("无效的 user id")
|
||||||
}
|
}
|
||||||
redemption := &Redemption{}
|
redemption := &Redemption{}
|
||||||
err = DB.Where("`key` = ?", key).First(redemption).Error
|
err = DB.Where("`key` = ?", key).First(redemption).Error
|
||||||
@ -55,7 +55,7 @@ func Redeem(key string, tokenId int) (quota int, err error) {
|
|||||||
if redemption.Status != common.RedemptionCodeStatusEnabled {
|
if redemption.Status != common.RedemptionCodeStatusEnabled {
|
||||||
return 0, errors.New("该兑换码已被使用")
|
return 0, errors.New("该兑换码已被使用")
|
||||||
}
|
}
|
||||||
err = IncreaseTokenQuota(tokenId, redemption.Quota)
|
err = IncreaseUserQuota(userId, redemption.Quota)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
_ "gorm.io/driver/sqlite"
|
_ "gorm.io/driver/sqlite"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"one-api/common"
|
"one-api/common"
|
||||||
@ -82,6 +83,16 @@ func GetTokenByIds(id int, userId int) (*Token, error) {
|
|||||||
return &token, err
|
return &token, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetTokenById(id int) (*Token, error) {
|
||||||
|
if id == 0 {
|
||||||
|
return nil, errors.New("id 为空!")
|
||||||
|
}
|
||||||
|
token := Token{Id: id}
|
||||||
|
var err error = nil
|
||||||
|
err = DB.First(&token, "id = ?", id).Error
|
||||||
|
return &token, err
|
||||||
|
}
|
||||||
|
|
||||||
func (token *Token) Insert() error {
|
func (token *Token) Insert() error {
|
||||||
var err error
|
var err error
|
||||||
err = DB.Create(token).Error
|
err = DB.Create(token).Error
|
||||||
@ -116,26 +127,53 @@ func DeleteTokenById(id int, userId int) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
quota := token.RemainQuota
|
|
||||||
if quota != 0 {
|
|
||||||
if quota > 0 {
|
|
||||||
err = IncreaseUserQuota(userId, quota)
|
|
||||||
} else {
|
|
||||||
err = DecreaseUserQuota(userId, -quota)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return token.Delete()
|
return token.Delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
func IncreaseTokenQuota(id int, quota int) (err error) {
|
func DecreaseTokenQuota(tokenId int, quota int) (err error) {
|
||||||
err = DB.Model(&Token{}).Where("id = ?", id).Update("remain_quota", gorm.Expr("remain_quota + ?", quota)).Error
|
if quota < 0 {
|
||||||
|
return errors.New("quota 不能为负数!")
|
||||||
|
}
|
||||||
|
token, err := GetTokenById(tokenId)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if token.RemainQuota < quota {
|
||||||
func DecreaseTokenQuota(id int, quota int) (err error) {
|
return errors.New("令牌额度不足")
|
||||||
err = DB.Model(&Token{}).Where("id = ?", id).Update("remain_quota", gorm.Expr("remain_quota - ?", quota)).Error
|
}
|
||||||
|
userQuota, err := GetUserQuota(token.UserId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if userQuota < quota {
|
||||||
|
return errors.New("用户额度不足")
|
||||||
|
}
|
||||||
|
quotaTooLow := userQuota >= common.QuotaRemindThreshold && userQuota-quota < common.QuotaRemindThreshold
|
||||||
|
noMoreQuota := userQuota-quota <= 0
|
||||||
|
if quotaTooLow || noMoreQuota {
|
||||||
|
go func() {
|
||||||
|
email, err := GetUserEmail(token.UserId)
|
||||||
|
if err != nil {
|
||||||
|
common.SysError("获取用户邮箱失败:" + err.Error())
|
||||||
|
}
|
||||||
|
prompt := "您的额度即将用尽"
|
||||||
|
if noMoreQuota {
|
||||||
|
prompt = "您的额度已用尽"
|
||||||
|
}
|
||||||
|
if email != "" {
|
||||||
|
topUpLink := fmt.Sprintf("%s/topup", common.ServerAddress)
|
||||||
|
err = common.SendEmail(prompt, email,
|
||||||
|
fmt.Sprintf("%s,剩余额度为 %d,为了不影响您的使用,请及时充值。<br/>充值链接:<a href='%s'>%s</a>", prompt, userQuota-quota, topUpLink, topUpLink))
|
||||||
|
if err != nil {
|
||||||
|
common.SysError("发送邮件失败:" + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
err = DB.Model(&Token{}).Where("id = ?", tokenId).Update("remain_quota", gorm.Expr("remain_quota - ?", quota)).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = DecreaseUserQuota(token.UserId, quota)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -225,12 +225,23 @@ func GetUserQuota(id int) (quota int, err error) {
|
|||||||
return quota, err
|
return quota, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetUserEmail(id int) (email string, err error) {
|
||||||
|
err = DB.Model(&User{}).Where("id = ?", id).Select("email").Find(&email).Error
|
||||||
|
return email, err
|
||||||
|
}
|
||||||
|
|
||||||
func IncreaseUserQuota(id int, quota int) (err error) {
|
func IncreaseUserQuota(id int, quota int) (err error) {
|
||||||
|
if quota < 0 {
|
||||||
|
return errors.New("quota 不能为负数!")
|
||||||
|
}
|
||||||
err = DB.Model(&User{}).Where("id = ?", id).Update("quota", gorm.Expr("quota + ?", quota)).Error
|
err = DB.Model(&User{}).Where("id = ?", id).Update("quota", gorm.Expr("quota + ?", quota)).Error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func DecreaseUserQuota(id int, quota int) (err error) {
|
func DecreaseUserQuota(id int, quota int) (err error) {
|
||||||
|
if quota < 0 {
|
||||||
|
return errors.New("quota 不能为负数!")
|
||||||
|
}
|
||||||
err = DB.Model(&User{}).Where("id = ?", id).Update("quota", gorm.Expr("quota - ?", quota)).Error
|
err = DB.Model(&User{}).Where("id = ?", id).Update("quota", gorm.Expr("quota - ?", quota)).Error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ func SetApiRouter(router *gin.Engine) {
|
|||||||
selfRoute.PUT("/self", controller.UpdateSelf)
|
selfRoute.PUT("/self", controller.UpdateSelf)
|
||||||
selfRoute.DELETE("/self", controller.DeleteSelf)
|
selfRoute.DELETE("/self", controller.DeleteSelf)
|
||||||
selfRoute.GET("/token", controller.GenerateAccessToken)
|
selfRoute.GET("/token", controller.GenerateAccessToken)
|
||||||
|
selfRoute.POST("/topup", controller.TopUp)
|
||||||
}
|
}
|
||||||
|
|
||||||
adminRoute := userRoute.Group("/")
|
adminRoute := userRoute.Group("/")
|
||||||
@ -74,7 +75,6 @@ func SetApiRouter(router *gin.Engine) {
|
|||||||
{
|
{
|
||||||
tokenRoute.GET("/", controller.GetAllTokens)
|
tokenRoute.GET("/", controller.GetAllTokens)
|
||||||
tokenRoute.GET("/search", controller.SearchTokens)
|
tokenRoute.GET("/search", controller.SearchTokens)
|
||||||
tokenRoute.POST("/topup", controller.TopUp)
|
|
||||||
tokenRoute.GET("/:id", controller.GetToken)
|
tokenRoute.GET("/:id", controller.GetToken)
|
||||||
tokenRoute.POST("/", controller.AddToken)
|
tokenRoute.POST("/", controller.AddToken)
|
||||||
tokenRoute.PUT("/", controller.UpdateToken)
|
tokenRoute.PUT("/", controller.UpdateToken)
|
||||||
|
@ -21,6 +21,7 @@ import EditToken from './pages/Token/EditToken';
|
|||||||
import EditChannel from './pages/Channel/EditChannel';
|
import EditChannel from './pages/Channel/EditChannel';
|
||||||
import Redemption from './pages/Redemption';
|
import Redemption from './pages/Redemption';
|
||||||
import EditRedemption from './pages/Redemption/EditRedemption';
|
import EditRedemption from './pages/Redemption/EditRedemption';
|
||||||
|
import TopUp from './pages/TopUp';
|
||||||
|
|
||||||
const Home = lazy(() => import('./pages/Home'));
|
const Home = lazy(() => import('./pages/Home'));
|
||||||
const About = lazy(() => import('./pages/About'));
|
const About = lazy(() => import('./pages/About'));
|
||||||
@ -239,6 +240,16 @@ function App() {
|
|||||||
</PrivateRoute>
|
</PrivateRoute>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path='/topup'
|
||||||
|
element={
|
||||||
|
<PrivateRoute>
|
||||||
|
<Suspense fallback={<Loading></Loading>}>
|
||||||
|
<TopUp />
|
||||||
|
</Suspense>
|
||||||
|
</PrivateRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path='/about'
|
path='/about'
|
||||||
element={
|
element={
|
||||||
|
@ -30,6 +30,12 @@ const headerButtons = [
|
|||||||
icon: 'dollar sign',
|
icon: 'dollar sign',
|
||||||
admin: true,
|
admin: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '充值',
|
||||||
|
to: '/topup',
|
||||||
|
icon: 'cart',
|
||||||
|
admin: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '用户',
|
name: '用户',
|
||||||
to: '/user',
|
to: '/user',
|
||||||
|
@ -36,8 +36,6 @@ const TokensTable = () => {
|
|||||||
const [searching, setSearching] = useState(false);
|
const [searching, setSearching] = useState(false);
|
||||||
const [showTopUpModal, setShowTopUpModal] = useState(false);
|
const [showTopUpModal, setShowTopUpModal] = useState(false);
|
||||||
const [targetTokenIdx, setTargetTokenIdx] = useState(0);
|
const [targetTokenIdx, setTargetTokenIdx] = useState(0);
|
||||||
const [redemptionCode, setRedemptionCode] = useState('');
|
|
||||||
const [topUpLink, setTopUpLink] = useState('');
|
|
||||||
|
|
||||||
const loadTokens = async (startIdx) => {
|
const loadTokens = async (startIdx) => {
|
||||||
const res = await API.get(`/api/token/?p=${startIdx}`);
|
const res = await API.get(`/api/token/?p=${startIdx}`);
|
||||||
@ -77,13 +75,6 @@ const TokensTable = () => {
|
|||||||
.catch((reason) => {
|
.catch((reason) => {
|
||||||
showError(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) => {
|
const manageToken = async (id, action, idx) => {
|
||||||
@ -156,28 +147,6 @@ const TokensTable = () => {
|
|||||||
setLoading(false);
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Form onSubmit={searchTokens}>
|
<Form onSubmit={searchTokens}>
|
||||||
@ -279,15 +248,6 @@ const TokensTable = () => {
|
|||||||
>
|
>
|
||||||
复制
|
复制
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
size={'small'}
|
|
||||||
color={'yellow'}
|
|
||||||
onClick={() => {
|
|
||||||
setTargetTokenIdx(idx);
|
|
||||||
setShowTopUpModal(true);
|
|
||||||
}}>
|
|
||||||
充值
|
|
||||||
</Button>
|
|
||||||
<Popup
|
<Popup
|
||||||
trigger={
|
trigger={
|
||||||
<Button size='small' negative>
|
<Button size='small' negative>
|
||||||
@ -355,39 +315,6 @@ const TokensTable = () => {
|
|||||||
</Table.Row>
|
</Table.Row>
|
||||||
</Table.Footer>
|
</Table.Footer>
|
||||||
</Table>
|
</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 React, { useEffect, useState } from 'react';
|
||||||
import { Button, Form, Header, Segment } from 'semantic-ui-react';
|
import { Button, Form, Header, Segment } from 'semantic-ui-react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { API, isAdmin, showError, showSuccess, timestamp2string } from '../../helpers';
|
import { API, showError, showSuccess, timestamp2string } from '../../helpers';
|
||||||
|
|
||||||
const EditToken = () => {
|
const EditToken = () => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@ -14,7 +14,6 @@ const EditToken = () => {
|
|||||||
expired_time: -1,
|
expired_time: -1,
|
||||||
unlimited_quota: false
|
unlimited_quota: false
|
||||||
};
|
};
|
||||||
const isAdminUser = isAdmin();
|
|
||||||
const [inputs, setInputs] = useState(originInputs);
|
const [inputs, setInputs] = useState(originInputs);
|
||||||
const { name, remain_quota, expired_time, unlimited_quota } = inputs;
|
const { name, remain_quota, expired_time, unlimited_quota } = inputs;
|
||||||
|
|
||||||
@ -107,8 +106,6 @@ const EditToken = () => {
|
|||||||
required={!isEdit}
|
required={!isEdit}
|
||||||
/>
|
/>
|
||||||
</Form.Field>
|
</Form.Field>
|
||||||
{
|
|
||||||
isAdminUser && <>
|
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='额度'
|
label='额度'
|
||||||
@ -124,8 +121,6 @@ const EditToken = () => {
|
|||||||
<Button type={'button'} style={{ marginBottom: '14px' }} onClick={() => {
|
<Button type={'button'} style={{ marginBottom: '14px' }} onClick={() => {
|
||||||
setUnlimitedQuota();
|
setUnlimitedQuota();
|
||||||
}}>{unlimited_quota ? '取消无限额度' : '设置为无限额度'}</Button>
|
}}>{unlimited_quota ? '取消无限额度' : '设置为无限额度'}</Button>
|
||||||
</>
|
|
||||||
}
|
|
||||||
<Form.Field>
|
<Form.Field>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
label='过期时间'
|
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;
|
Loading…
Reference in New Issue
Block a user