This commit standardizes the card header design across multiple edit pages to create a consistent and modern UI experience. Changes made: - Add gradient background colors to card headers - Implement decorative circular elements for visual appeal - Update icon colors to white with semi-transparent backgrounds - Standardize text colors and opacity for better readability - Add consistent padding and border radius - Maintain unique color schemes for different functional sections Modified files: - EditChannel.js - EditRedemption.js - EditToken.js - EditUser.js - AddUser.js The new design features: - Blue gradient for basic information sections - Green gradient for quota/permission settings - Purple gradient for access restrictions - Orange gradient for binding/group information - Consistent layout structure across all edit pages This update improves visual hierarchy and maintains brand consistency while enhancing the overall user experience.
297 lines
9.4 KiB
JavaScript
297 lines
9.4 KiB
JavaScript
import React, { useEffect, useState } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import {
|
|
API,
|
|
downloadTextAsFile,
|
|
isMobile,
|
|
showError,
|
|
showSuccess,
|
|
} from '../../helpers';
|
|
import {
|
|
renderQuota,
|
|
renderQuotaWithPrompt,
|
|
} from '../../helpers/render';
|
|
import {
|
|
AutoComplete,
|
|
Button,
|
|
Input,
|
|
Modal,
|
|
SideSheet,
|
|
Space,
|
|
Spin,
|
|
Typography,
|
|
Card,
|
|
Tag,
|
|
} from '@douyinfe/semi-ui';
|
|
import {
|
|
IconCreditCard,
|
|
IconSave,
|
|
IconClose,
|
|
IconPlusCircle,
|
|
IconGift,
|
|
} from '@douyinfe/semi-icons';
|
|
|
|
const { Text, Title } = Typography;
|
|
|
|
const EditRedemption = (props) => {
|
|
const { t } = useTranslation();
|
|
const isEdit = props.editingRedemption.id !== undefined;
|
|
const [loading, setLoading] = useState(isEdit);
|
|
|
|
const originInputs = {
|
|
name: '',
|
|
quota: 100000,
|
|
count: 1,
|
|
};
|
|
const [inputs, setInputs] = useState(originInputs);
|
|
const { name, quota, count } = inputs;
|
|
|
|
const handleCancel = () => {
|
|
props.handleClose();
|
|
};
|
|
|
|
const handleInputChange = (name, value) => {
|
|
setInputs((inputs) => ({ ...inputs, [name]: value }));
|
|
};
|
|
|
|
const loadRedemption = async () => {
|
|
setLoading(true);
|
|
let res = await API.get(`/api/redemption/${props.editingRedemption.id}`);
|
|
const { success, message, data } = res.data;
|
|
if (success) {
|
|
setInputs(data);
|
|
} else {
|
|
showError(message);
|
|
}
|
|
setLoading(false);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (isEdit) {
|
|
loadRedemption().then(() => {
|
|
// console.log(inputs);
|
|
});
|
|
} else {
|
|
setInputs(originInputs);
|
|
}
|
|
}, [props.editingRedemption.id]);
|
|
|
|
const submit = async () => {
|
|
let name = inputs.name;
|
|
if (!isEdit && inputs.name === '') {
|
|
// set default name
|
|
name = renderQuota(quota);
|
|
}
|
|
setLoading(true);
|
|
let localInputs = inputs;
|
|
localInputs.count = parseInt(localInputs.count);
|
|
localInputs.quota = parseInt(localInputs.quota);
|
|
localInputs.name = name;
|
|
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('兑换码创建成功!'));
|
|
setInputs(originInputs);
|
|
props.refresh();
|
|
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, `${inputs.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>
|
|
}
|
|
headerStyle={{
|
|
borderBottom: '1px solid var(--semi-color-border)',
|
|
padding: '24px'
|
|
}}
|
|
bodyStyle={{
|
|
backgroundColor: 'var(--semi-color-bg-0)',
|
|
padding: '0'
|
|
}}
|
|
visible={props.visiable}
|
|
width={isMobile() ? '100%' : 600}
|
|
footer={
|
|
<div className="flex justify-end bg-white">
|
|
<Space>
|
|
<Button
|
|
theme="solid"
|
|
size="large"
|
|
className="!rounded-full"
|
|
onClick={submit}
|
|
icon={<IconSave />}
|
|
loading={loading}
|
|
>
|
|
{t('提交')}
|
|
</Button>
|
|
<Button
|
|
theme="light"
|
|
size="large"
|
|
className="!rounded-full"
|
|
type="primary"
|
|
onClick={handleCancel}
|
|
icon={<IconClose />}
|
|
>
|
|
{t('取消')}
|
|
</Button>
|
|
</Space>
|
|
</div>
|
|
}
|
|
closeIcon={null}
|
|
onCancel={() => handleCancel()}
|
|
>
|
|
<Spin spinning={loading}>
|
|
<div className="p-6">
|
|
<Card className="!rounded-2xl shadow-sm border-0 mb-6">
|
|
<div className="flex items-center mb-4 p-6 rounded-xl" style={{
|
|
background: 'linear-gradient(135deg, #1e3a8a 0%, #2563eb 50%, #3b82f6 100%)',
|
|
position: 'relative'
|
|
}}>
|
|
<div className="absolute inset-0 overflow-hidden">
|
|
<div className="absolute -top-10 -right-10 w-40 h-40 bg-white opacity-5 rounded-full"></div>
|
|
<div className="absolute -bottom-8 -left-8 w-24 h-24 bg-white opacity-10 rounded-full"></div>
|
|
</div>
|
|
<div className="w-10 h-10 rounded-full bg-white/20 flex items-center justify-center mr-4 relative">
|
|
<IconGift size="large" style={{ color: '#ffffff' }} />
|
|
</div>
|
|
<div className="relative">
|
|
<Text style={{ color: '#ffffff' }} className="text-lg font-medium">{t('基本信息')}</Text>
|
|
<div style={{ color: '#ffffff' }} className="text-sm opacity-80">{t('设置兑换码的基本信息')}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div>
|
|
<Text strong className="block mb-2">{t('名称')}</Text>
|
|
<Input
|
|
placeholder={t('请输入名称')}
|
|
onChange={(value) => handleInputChange('name', value)}
|
|
value={name}
|
|
autoComplete="new-password"
|
|
size="large"
|
|
className="!rounded-lg"
|
|
showClear
|
|
required={!isEdit}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
|
|
<Card className="!rounded-2xl shadow-sm border-0">
|
|
<div className="flex items-center mb-4 p-6 rounded-xl" style={{
|
|
background: 'linear-gradient(135deg, #065f46 0%, #059669 50%, #10b981 100%)',
|
|
position: 'relative'
|
|
}}>
|
|
<div className="absolute inset-0 overflow-hidden">
|
|
<div className="absolute -top-10 -right-10 w-40 h-40 bg-white opacity-5 rounded-full"></div>
|
|
<div className="absolute -bottom-8 -left-8 w-24 h-24 bg-white opacity-10 rounded-full"></div>
|
|
</div>
|
|
<div className="w-10 h-10 rounded-full bg-white/20 flex items-center justify-center mr-4 relative">
|
|
<IconCreditCard size="large" style={{ color: '#ffffff' }} />
|
|
</div>
|
|
<div className="relative">
|
|
<Text style={{ color: '#ffffff' }} className="text-lg font-medium">{t('额度设置')}</Text>
|
|
<div style={{ color: '#ffffff' }} className="text-sm opacity-80">{t('设置兑换码的额度和数量')}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<div>
|
|
<div className="flex justify-between mb-2">
|
|
<Text strong>{t('额度')}</Text>
|
|
<Text type="tertiary">{renderQuotaWithPrompt(quota)}</Text>
|
|
</div>
|
|
<AutoComplete
|
|
placeholder={t('请输入额度')}
|
|
onChange={(value) => handleInputChange('quota', value)}
|
|
value={quota}
|
|
autoComplete="new-password"
|
|
type="number"
|
|
size="large"
|
|
className="w-full !rounded-lg"
|
|
prefix={<IconCreditCard />}
|
|
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$' },
|
|
]}
|
|
/>
|
|
</div>
|
|
|
|
{!isEdit && (
|
|
<div>
|
|
<Text strong className="block mb-2">{t('生成数量')}</Text>
|
|
<Input
|
|
placeholder={t('请输入生成数量')}
|
|
onChange={(value) => handleInputChange('count', value)}
|
|
value={count}
|
|
autoComplete="new-password"
|
|
type="number"
|
|
size="large"
|
|
className="!rounded-lg"
|
|
prefix={<IconPlusCircle />}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</Spin>
|
|
</SideSheet>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default EditRedemption;
|