|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import React, { useEffect, useState, useRef } from 'react'; |
|
|
import { useTranslation } from 'react-i18next'; |
|
|
import { |
|
|
API, |
|
|
downloadTextAsFile, |
|
|
showError, |
|
|
showSuccess, |
|
|
renderQuota, |
|
|
renderQuotaWithPrompt, |
|
|
} from '../../../../helpers'; |
|
|
import { useIsMobile } from '../../../../hooks/common/useIsMobile'; |
|
|
import { |
|
|
Button, |
|
|
Modal, |
|
|
SideSheet, |
|
|
Space, |
|
|
Spin, |
|
|
Typography, |
|
|
Card, |
|
|
Tag, |
|
|
Form, |
|
|
Avatar, |
|
|
Row, |
|
|
Col, |
|
|
} from '@douyinfe/semi-ui'; |
|
|
import { |
|
|
IconCreditCard, |
|
|
IconSave, |
|
|
IconClose, |
|
|
IconGift, |
|
|
} from '@douyinfe/semi-icons'; |
|
|
|
|
|
const { Text, Title } = Typography; |
|
|
|
|
|
const EditRedemptionModal = (props) => { |
|
|
const { t } = useTranslation(); |
|
|
const isEdit = props.editingRedemption.id !== undefined; |
|
|
const [loading, setLoading] = useState(isEdit); |
|
|
const isMobile = useIsMobile(); |
|
|
const formApiRef = useRef(null); |
|
|
|
|
|
const getInitValues = () => ({ |
|
|
name: '', |
|
|
quota: 100000, |
|
|
count: 1, |
|
|
expired_time: null, |
|
|
}); |
|
|
|
|
|
const handleCancel = () => { |
|
|
props.handleClose(); |
|
|
}; |
|
|
|
|
|
const loadRedemption = async () => { |
|
|
setLoading(true); |
|
|
let res = await API.get(`/api/redemption/${props.editingRedemption.id}`); |
|
|
const { success, message, data } = res.data; |
|
|
if (success) { |
|
|
if (data.expired_time === 0) { |
|
|
data.expired_time = null; |
|
|
} else { |
|
|
data.expired_time = new Date(data.expired_time * 1000); |
|
|
} |
|
|
formApiRef.current?.setValues({ ...getInitValues(), ...data }); |
|
|
} else { |
|
|
showError(message); |
|
|
} |
|
|
setLoading(false); |
|
|
}; |
|
|
|
|
|
useEffect(() => { |
|
|
if (formApiRef.current) { |
|
|
if (isEdit) { |
|
|
loadRedemption(); |
|
|
} else { |
|
|
formApiRef.current.setValues(getInitValues()); |
|
|
} |
|
|
} |
|
|
}, [props.editingRedemption.id]); |
|
|
|
|
|
const submit = async (values) => { |
|
|
let name = values.name; |
|
|
if (!isEdit && (!name || name === '')) { |
|
|
name = renderQuota(values.quota); |
|
|
} |
|
|
setLoading(true); |
|
|
let localInputs = { ...values }; |
|
|
localInputs.count = parseInt(localInputs.count) || 0; |
|
|
localInputs.quota = parseInt(localInputs.quota) || 0; |
|
|
localInputs.name = name; |
|
|
if (!localInputs.expired_time) { |
|
|
localInputs.expired_time = 0; |
|
|
} else { |
|
|
localInputs.expired_time = Math.floor( |
|
|
localInputs.expired_time.getTime() / 1000, |
|
|
); |
|
|
} |
|
|
let res; |
|
|
if (isEdit) { |
|
|
res = await API.put(`/api/redemption/`, { |
|
|
...localInputs, |
|
|
id: parseInt(props.editingRedemption.id), |
|
|
}); |
|
|
} else { |
|
|
res = await API.post(`/api/redemption/`, { |
|
|
...localInputs, |
|
|
}); |
|
|
} |
|
|
const { success, message, data } = res.data; |
|
|
if (success) { |
|
|
if (isEdit) { |
|
|
showSuccess(t('兑换码更新成功!')); |
|
|
props.refresh(); |
|
|
props.handleClose(); |
|
|
} else { |
|
|
showSuccess(t('兑换码创建成功!')); |
|
|
props.refresh(); |
|
|
formApiRef.current?.setValues(getInitValues()); |
|
|
props.handleClose(); |
|
|
} |
|
|
} else { |
|
|
showError(message); |
|
|
} |
|
|
if (!isEdit && data) { |
|
|
let text = ''; |
|
|
for (let i = 0; i < data.length; i++) { |
|
|
text += data[i] + '\n'; |
|
|
} |
|
|
Modal.confirm({ |
|
|
title: t('兑换码创建成功'), |
|
|
content: ( |
|
|
<div> |
|
|
<p>{t('兑换码创建成功,是否下载兑换码?')}</p> |
|
|
<p>{t('兑换码将以文本文件的形式下载,文件名为兑换码的名称。')}</p> |
|
|
</div> |
|
|
), |
|
|
onOk: () => { |
|
|
downloadTextAsFile(text, `${localInputs.name}.txt`); |
|
|
}, |
|
|
}); |
|
|
} |
|
|
setLoading(false); |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<> |
|
|
<SideSheet |
|
|
placement={isEdit ? 'right' : 'left'} |
|
|
title={ |
|
|
<Space> |
|
|
{isEdit ? ( |
|
|
<Tag color='blue' shape='circle'> |
|
|
{t('更新')} |
|
|
</Tag> |
|
|
) : ( |
|
|
<Tag color='green' shape='circle'> |
|
|
{t('新建')} |
|
|
</Tag> |
|
|
)} |
|
|
<Title heading={4} className='m-0'> |
|
|
{isEdit ? t('更新兑换码信息') : t('创建新的兑换码')} |
|
|
</Title> |
|
|
</Space> |
|
|
} |
|
|
bodyStyle={{ padding: '0' }} |
|
|
visible={props.visiable} |
|
|
width={isMobile ? '100%' : 600} |
|
|
footer={ |
|
|
<div className='flex justify-end bg-white'> |
|
|
<Space> |
|
|
<Button |
|
|
theme='solid' |
|
|
onClick={() => formApiRef.current?.submitForm()} |
|
|
icon={<IconSave />} |
|
|
loading={loading} |
|
|
> |
|
|
{t('提交')} |
|
|
</Button> |
|
|
<Button |
|
|
theme='light' |
|
|
type='primary' |
|
|
onClick={handleCancel} |
|
|
icon={<IconClose />} |
|
|
> |
|
|
{t('取消')} |
|
|
</Button> |
|
|
</Space> |
|
|
</div> |
|
|
} |
|
|
closeIcon={null} |
|
|
onCancel={() => handleCancel()} |
|
|
> |
|
|
<Spin spinning={loading}> |
|
|
<Form |
|
|
initValues={getInitValues()} |
|
|
getFormApi={(api) => (formApiRef.current = api)} |
|
|
onSubmit={submit} |
|
|
> |
|
|
{({ values }) => ( |
|
|
<div className='p-2'> |
|
|
<Card className='!rounded-2xl shadow-sm border-0 mb-6'> |
|
|
{/* Header: Basic Info */} |
|
|
<div className='flex items-center mb-2'> |
|
|
<Avatar |
|
|
size='small' |
|
|
color='blue' |
|
|
className='mr-2 shadow-md' |
|
|
> |
|
|
<IconGift size={16} /> |
|
|
</Avatar> |
|
|
<div> |
|
|
<Text className='text-lg font-medium'> |
|
|
{t('基本信息')} |
|
|
</Text> |
|
|
<div className='text-xs text-gray-600'> |
|
|
{t('设置兑换码的基本信息')} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<Row gutter={12}> |
|
|
<Col span={24}> |
|
|
<Form.Input |
|
|
field='name' |
|
|
label={t('名称')} |
|
|
placeholder={t('请输入名称')} |
|
|
style={{ width: '100%' }} |
|
|
rules={ |
|
|
!isEdit |
|
|
? [] |
|
|
: [{ required: true, message: t('请输入名称') }] |
|
|
} |
|
|
showClear |
|
|
/> |
|
|
</Col> |
|
|
<Col span={24}> |
|
|
<Form.DatePicker |
|
|
field='expired_time' |
|
|
label={t('过期时间')} |
|
|
type='dateTime' |
|
|
placeholder={t('选择过期时间(可选,留空为永久)')} |
|
|
style={{ width: '100%' }} |
|
|
showClear |
|
|
/> |
|
|
</Col> |
|
|
</Row> |
|
|
</Card> |
|
|
|
|
|
<Card className='!rounded-2xl shadow-sm border-0'> |
|
|
{/* Header: Quota Settings */} |
|
|
<div className='flex items-center mb-2'> |
|
|
<Avatar |
|
|
size='small' |
|
|
color='green' |
|
|
className='mr-2 shadow-md' |
|
|
> |
|
|
<IconCreditCard size={16} /> |
|
|
</Avatar> |
|
|
<div> |
|
|
<Text className='text-lg font-medium'> |
|
|
{t('额度设置')} |
|
|
</Text> |
|
|
<div className='text-xs text-gray-600'> |
|
|
{t('设置兑换码的额度和数量')} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<Row gutter={12}> |
|
|
<Col span={12}> |
|
|
<Form.AutoComplete |
|
|
field='quota' |
|
|
label={t('额度')} |
|
|
placeholder={t('请输入额度')} |
|
|
style={{ width: '100%' }} |
|
|
type='number' |
|
|
rules={[ |
|
|
{ required: true, message: t('请输入额度') }, |
|
|
{ |
|
|
validator: (rule, v) => { |
|
|
const num = parseInt(v, 10); |
|
|
return num > 0 |
|
|
? Promise.resolve() |
|
|
: Promise.reject(t('额度必须大于0')); |
|
|
}, |
|
|
}, |
|
|
]} |
|
|
extraText={renderQuotaWithPrompt( |
|
|
Number(values.quota) || 0, |
|
|
)} |
|
|
data={[ |
|
|
{ value: 500000, label: '1$' }, |
|
|
{ value: 5000000, label: '10$' }, |
|
|
{ value: 25000000, label: '50$' }, |
|
|
{ value: 50000000, label: '100$' }, |
|
|
{ value: 250000000, label: '500$' }, |
|
|
{ value: 500000000, label: '1000$' }, |
|
|
]} |
|
|
showClear |
|
|
/> |
|
|
</Col> |
|
|
{!isEdit && ( |
|
|
<Col span={12}> |
|
|
<Form.InputNumber |
|
|
field='count' |
|
|
label={t('生成数量')} |
|
|
min={1} |
|
|
rules={[ |
|
|
{ required: true, message: t('请输入生成数量') }, |
|
|
{ |
|
|
validator: (rule, v) => { |
|
|
const num = parseInt(v, 10); |
|
|
return num > 0 |
|
|
? Promise.resolve() |
|
|
: Promise.reject(t('生成数量必须大于0')); |
|
|
}, |
|
|
}, |
|
|
]} |
|
|
style={{ width: '100%' }} |
|
|
showClear |
|
|
/> |
|
|
</Col> |
|
|
)} |
|
|
</Row> |
|
|
</Card> |
|
|
</div> |
|
|
)} |
|
|
</Form> |
|
|
</Spin> |
|
|
</SideSheet> |
|
|
</> |
|
|
); |
|
|
}; |
|
|
|
|
|
export default EditRedemptionModal; |
|
|
|