|
@@ -59,7 +59,16 @@ function renderTimestamp(timestamp) {
|
|
|
const RedemptionsTable = () => {
|
|
const RedemptionsTable = () => {
|
|
|
const { t } = useTranslation();
|
|
const { t } = useTranslation();
|
|
|
|
|
|
|
|
- const renderStatus = (status) => {
|
|
|
|
|
|
|
+ const isExpired = (rec) => {
|
|
|
|
|
+ return rec.status === 1 && rec.expired_time !== 0 && rec.expired_time < Math.floor(Date.now() / 1000);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const renderStatus = (status, record) => {
|
|
|
|
|
+ if (isExpired(record)) {
|
|
|
|
|
+ return (
|
|
|
|
|
+ <Tag color='orange' size='large' shape='circle' prefixIcon={<Minus size={14} />}>{t('已过期')}</Tag>
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
switch (status) {
|
|
switch (status) {
|
|
|
case 1:
|
|
case 1:
|
|
|
return (
|
|
return (
|
|
@@ -102,7 +111,7 @@ const RedemptionsTable = () => {
|
|
|
dataIndex: 'status',
|
|
dataIndex: 'status',
|
|
|
key: 'status',
|
|
key: 'status',
|
|
|
render: (text, record, index) => {
|
|
render: (text, record, index) => {
|
|
|
- return <div>{renderStatus(text)}</div>;
|
|
|
|
|
|
|
+ return <div>{renderStatus(text, record)}</div>;
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
@@ -125,6 +134,13 @@ const RedemptionsTable = () => {
|
|
|
return <div>{renderTimestamp(text)}</div>;
|
|
return <div>{renderTimestamp(text)}</div>;
|
|
|
},
|
|
},
|
|
|
},
|
|
},
|
|
|
|
|
+ {
|
|
|
|
|
+ title: t('过期时间'),
|
|
|
|
|
+ dataIndex: 'expired_time',
|
|
|
|
|
+ render: (text) => {
|
|
|
|
|
+ return <div>{text === 0 ? t('永不过期') : renderTimestamp(text)}</div>;
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
{
|
|
{
|
|
|
title: t('兑换人ID'),
|
|
title: t('兑换人ID'),
|
|
|
dataIndex: 'used_user_id',
|
|
dataIndex: 'used_user_id',
|
|
@@ -158,8 +174,7 @@ const RedemptionsTable = () => {
|
|
|
}
|
|
}
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
- // 动态添加启用/禁用按钮
|
|
|
|
|
- if (record.status === 1) {
|
|
|
|
|
|
|
+ if (record.status === 1 && !isExpired(record)) {
|
|
|
moreMenuItems.push({
|
|
moreMenuItems.push({
|
|
|
node: 'item',
|
|
node: 'item',
|
|
|
name: t('禁用'),
|
|
name: t('禁用'),
|
|
@@ -169,7 +184,7 @@ const RedemptionsTable = () => {
|
|
|
manageRedemption(record.id, 'disable', record);
|
|
manageRedemption(record.id, 'disable', record);
|
|
|
},
|
|
},
|
|
|
});
|
|
});
|
|
|
- } else {
|
|
|
|
|
|
|
+ } else if (!isExpired(record)) {
|
|
|
moreMenuItems.push({
|
|
moreMenuItems.push({
|
|
|
node: 'item',
|
|
node: 'item',
|
|
|
name: t('启用'),
|
|
name: t('启用'),
|
|
@@ -436,7 +451,7 @@ const RedemptionsTable = () => {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleRow = (record, index) => {
|
|
const handleRow = (record, index) => {
|
|
|
- if (record.status !== 1) {
|
|
|
|
|
|
|
+ if (record.status !== 1 || isExpired(record)) {
|
|
|
return {
|
|
return {
|
|
|
style: {
|
|
style: {
|
|
|
background: 'var(--semi-color-disabled-border)',
|
|
background: 'var(--semi-color-disabled-border)',
|
|
@@ -459,39 +474,66 @@ const RedemptionsTable = () => {
|
|
|
<Divider margin="12px" />
|
|
<Divider margin="12px" />
|
|
|
|
|
|
|
|
<div className="flex flex-col md:flex-row justify-between items-center gap-4 w-full">
|
|
<div className="flex flex-col md:flex-row justify-between items-center gap-4 w-full">
|
|
|
- <div className="flex gap-2 w-full md:w-auto order-2 md:order-1">
|
|
|
|
|
|
|
+ <div className="flex flex-col sm:flex-row gap-2 w-full md:w-auto order-2 md:order-1">
|
|
|
|
|
+ <div className="flex gap-2 w-full sm:w-auto">
|
|
|
|
|
+ <Button
|
|
|
|
|
+ theme='light'
|
|
|
|
|
+ type='primary'
|
|
|
|
|
+ icon={<IconPlus />}
|
|
|
|
|
+ className="!rounded-full w-full sm:w-auto"
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ setEditingRedemption({
|
|
|
|
|
+ id: undefined,
|
|
|
|
|
+ });
|
|
|
|
|
+ setShowEdit(true);
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ {t('添加兑换码')}
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ type='warning'
|
|
|
|
|
+ icon={<IconCopy />}
|
|
|
|
|
+ className="!rounded-full w-full sm:w-auto"
|
|
|
|
|
+ onClick={async () => {
|
|
|
|
|
+ if (selectedKeys.length === 0) {
|
|
|
|
|
+ showError(t('请至少选择一个兑换码!'));
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ let keys = '';
|
|
|
|
|
+ for (let i = 0; i < selectedKeys.length; i++) {
|
|
|
|
|
+ keys +=
|
|
|
|
|
+ selectedKeys[i].name + ' ' + selectedKeys[i].key + '\n';
|
|
|
|
|
+ }
|
|
|
|
|
+ await copyText(keys);
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ {t('复制所选兑换码到剪贴板')}
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </div>
|
|
|
<Button
|
|
<Button
|
|
|
- theme='light'
|
|
|
|
|
- type='primary'
|
|
|
|
|
- icon={<IconPlus />}
|
|
|
|
|
- className="!rounded-full w-full md:w-auto"
|
|
|
|
|
|
|
+ type='danger'
|
|
|
|
|
+ icon={<IconDelete />}
|
|
|
|
|
+ className="!rounded-full w-full sm:w-auto"
|
|
|
onClick={() => {
|
|
onClick={() => {
|
|
|
- setEditingRedemption({
|
|
|
|
|
- id: undefined,
|
|
|
|
|
|
|
+ Modal.confirm({
|
|
|
|
|
+ title: t('确定清除所有失效兑换码?'),
|
|
|
|
|
+ content: t('将删除已使用、已禁用及过期的兑换码,此操作不可撤销。'),
|
|
|
|
|
+ onOk: async () => {
|
|
|
|
|
+ setLoading(true);
|
|
|
|
|
+ const res = await API.delete('/api/redemption/invalid');
|
|
|
|
|
+ const { success, message, data } = res.data;
|
|
|
|
|
+ if (success) {
|
|
|
|
|
+ showSuccess(t('已删除 {{count}} 条失效兑换码', { count: data }));
|
|
|
|
|
+ await refresh();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showError(message);
|
|
|
|
|
+ }
|
|
|
|
|
+ setLoading(false);
|
|
|
|
|
+ },
|
|
|
});
|
|
});
|
|
|
- setShowEdit(true);
|
|
|
|
|
- }}
|
|
|
|
|
- >
|
|
|
|
|
- {t('添加兑换码')}
|
|
|
|
|
- </Button>
|
|
|
|
|
- <Button
|
|
|
|
|
- type='warning'
|
|
|
|
|
- icon={<IconCopy />}
|
|
|
|
|
- className="!rounded-full w-full md:w-auto"
|
|
|
|
|
- onClick={async () => {
|
|
|
|
|
- if (selectedKeys.length === 0) {
|
|
|
|
|
- showError(t('请至少选择一个兑换码!'));
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- let keys = '';
|
|
|
|
|
- for (let i = 0; i < selectedKeys.length; i++) {
|
|
|
|
|
- keys +=
|
|
|
|
|
- selectedKeys[i].name + ' ' + selectedKeys[i].key + '\n';
|
|
|
|
|
- }
|
|
|
|
|
- await copyText(keys);
|
|
|
|
|
}}
|
|
}}
|
|
|
>
|
|
>
|
|
|
- {t('复制所选兑换码到剪贴板')}
|
|
|
|
|
|
|
+ {t('清除失效兑换码')}
|
|
|
</Button>
|
|
</Button>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|