|
|
@@ -1,10 +1,11 @@
|
|
|
import React, { useEffect, useState } from 'react';
|
|
|
-import { Button, Form, Label, Popup, Pagination, Table } from 'semantic-ui-react';
|
|
|
+import { Form, Label, Popup, Pagination } from 'semantic-ui-react';
|
|
|
import { Link } from 'react-router-dom';
|
|
|
import { API, copy, showError, showInfo, showSuccess, showWarning, timestamp2string } from '../helpers';
|
|
|
|
|
|
import { ITEMS_PER_PAGE } from '../constants';
|
|
|
import { renderQuota } from '../helpers/render';
|
|
|
+import {Button, Modal, Popconfirm, Popover, Table, Tag} from "@douyinfe/semi-ui";
|
|
|
|
|
|
function renderTimestamp(timestamp) {
|
|
|
return (
|
|
|
@@ -17,22 +18,133 @@ function renderTimestamp(timestamp) {
|
|
|
function renderStatus(status) {
|
|
|
switch (status) {
|
|
|
case 1:
|
|
|
- return <Label basic color='green'>未使用</Label>;
|
|
|
+ return <Tag color='green' size='large'>未使用</Tag>;
|
|
|
case 2:
|
|
|
- return <Label basic color='red'> 已禁用 </Label>;
|
|
|
+ return <Tag color='red' size='large'> 已禁用 </Tag>;
|
|
|
case 3:
|
|
|
- return <Label basic color='grey'> 已使用 </Label>;
|
|
|
+ return <Tag color='grey' size='large'> 已使用 </Tag>;
|
|
|
default:
|
|
|
- return <Label basic color='black'> 未知状态 </Label>;
|
|
|
+ return <Tag color='black' size='large'> 未知状态 </Tag>;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const RedemptionsTable = () => {
|
|
|
+ const columns = [
|
|
|
+ {
|
|
|
+ title: 'ID',
|
|
|
+ dataIndex: 'id',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '名称',
|
|
|
+ dataIndex: 'name',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '状态',
|
|
|
+ dataIndex: 'status',
|
|
|
+ key: 'status',
|
|
|
+ render: (text, record, index) => {
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ {renderStatus(text)}
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '额度',
|
|
|
+ dataIndex: 'quota',
|
|
|
+ render: (text, record, index) => {
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ {renderQuota(parseInt(text))}
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '创建时间',
|
|
|
+ dataIndex: 'created_time',
|
|
|
+ render: (text, record, index) => {
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ {renderTimestamp(text)}
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '',
|
|
|
+ dataIndex: 'operate',
|
|
|
+ render: (text, record, index) => (
|
|
|
+ <div>
|
|
|
+ <Popover
|
|
|
+ content={
|
|
|
+ record.key
|
|
|
+ }
|
|
|
+ style={{padding: 20}}
|
|
|
+ position="top"
|
|
|
+ >
|
|
|
+ <Button theme='light' type='tertiary' style={{marginRight: 1}}>查看</Button>
|
|
|
+ </Popover>
|
|
|
+ <Button theme='light' type='secondary' style={{marginRight: 1}}
|
|
|
+ onClick={async (text) => {
|
|
|
+ await copyText(record.key)
|
|
|
+ }}
|
|
|
+ >复制</Button>
|
|
|
+ <Popconfirm
|
|
|
+ title="确定是否要删除此令牌?"
|
|
|
+ content="此修改将不可逆"
|
|
|
+ okType={'danger'}
|
|
|
+ position={'left'}
|
|
|
+ onConfirm={() => {
|
|
|
+ manageRedemption(record.id, 'delete', record).then(
|
|
|
+ () => {
|
|
|
+ removeRecord(record.key);
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Button theme='light' type='danger' style={{marginRight: 1}}>删除</Button>
|
|
|
+ </Popconfirm>
|
|
|
+ {
|
|
|
+ record.status === 1 ?
|
|
|
+ <Button theme='light' type='warning' style={{marginRight: 1}} onClick={
|
|
|
+ async () => {
|
|
|
+ manageRedemption(
|
|
|
+ record.id,
|
|
|
+ 'disable',
|
|
|
+ record
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }>禁用</Button> :
|
|
|
+ <Button theme='light' type='secondary' style={{marginRight: 1}} onClick={
|
|
|
+ async () => {
|
|
|
+ manageRedemption(
|
|
|
+ record.id,
|
|
|
+ 'enable',
|
|
|
+ record
|
|
|
+ );
|
|
|
+ }
|
|
|
+ } disabled={record.status===3}>启用</Button>
|
|
|
+ }
|
|
|
+ {/*<Button theme='light' type='tertiary' style={{marginRight: 1}} onClick={*/}
|
|
|
+ {/* () => {*/}
|
|
|
+ {/* setEditingToken(record);*/}
|
|
|
+ {/* setShowEdit(true);*/}
|
|
|
+ {/* }*/}
|
|
|
+ {/*}>编辑</Button>*/}
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+ },
|
|
|
+ ];
|
|
|
+
|
|
|
const [redemptions, setRedemptions] = useState([]);
|
|
|
const [loading, setLoading] = useState(true);
|
|
|
const [activePage, setActivePage] = useState(1);
|
|
|
const [searchKeyword, setSearchKeyword] = useState('');
|
|
|
const [searching, setSearching] = useState(false);
|
|
|
+ const [tokenCount, setTokenCount] = useState(ITEMS_PER_PAGE);
|
|
|
+ const [selectedKeys, setSelectedKeys] = useState([]);
|
|
|
|
|
|
const loadRedemptions = async (startIdx) => {
|
|
|
const res = await API.get(`/api/redemption/?p=${startIdx}`);
|
|
|
@@ -51,6 +163,27 @@ const RedemptionsTable = () => {
|
|
|
setLoading(false);
|
|
|
};
|
|
|
|
|
|
+ const removeRecord = key => {
|
|
|
+ let newDataSource = [...redemptions];
|
|
|
+ if (key != null) {
|
|
|
+ let idx = newDataSource.findIndex(data => data.key === key);
|
|
|
+
|
|
|
+ if (idx > -1) {
|
|
|
+ newDataSource.splice(idx, 1);
|
|
|
+ setRedemptions(newDataSource);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const copyText = async (text) => {
|
|
|
+ if (await copy(text)) {
|
|
|
+ showSuccess('已复制到剪贴板!');
|
|
|
+ } else {
|
|
|
+ // setSearchKeyword(text);
|
|
|
+ Modal.error({ title: '无法复制到剪贴板,请手动复制', content: text });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
const onPaginationChange = (e, { activePage }) => {
|
|
|
(async () => {
|
|
|
if (activePage === Math.ceil(redemptions.length / ITEMS_PER_PAGE) + 1) {
|
|
|
@@ -69,7 +202,7 @@ const RedemptionsTable = () => {
|
|
|
});
|
|
|
}, []);
|
|
|
|
|
|
- const manageRedemption = async (id, action, idx) => {
|
|
|
+ const manageRedemption = async (id, action, record) => {
|
|
|
let data = { id };
|
|
|
let res;
|
|
|
switch (action) {
|
|
|
@@ -90,11 +223,11 @@ const RedemptionsTable = () => {
|
|
|
showSuccess('操作成功完成!');
|
|
|
let redemption = res.data.data;
|
|
|
let newRedemptions = [...redemptions];
|
|
|
- let realIdx = (activePage - 1) * ITEMS_PER_PAGE + idx;
|
|
|
+ // let realIdx = (activePage - 1) * ITEMS_PER_PAGE + idx;
|
|
|
if (action === 'delete') {
|
|
|
- newRedemptions[realIdx].deleted = true;
|
|
|
+
|
|
|
} else {
|
|
|
- newRedemptions[realIdx].status = redemption.status;
|
|
|
+ record.status = redemption.status;
|
|
|
}
|
|
|
setRedemptions(newRedemptions);
|
|
|
} else {
|
|
|
@@ -139,6 +272,25 @@ const RedemptionsTable = () => {
|
|
|
setLoading(false);
|
|
|
};
|
|
|
|
|
|
+ const handlePageChange = page => {
|
|
|
+ setActivePage(page);
|
|
|
+ if (page === Math.ceil(redemptions.length / ITEMS_PER_PAGE) + 1) {
|
|
|
+ // In this case we have to load more data and then append them.
|
|
|
+ loadRedemptions(page - 1).then(r => {});
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ let pageData = redemptions.slice((activePage - 1) * ITEMS_PER_PAGE, activePage * ITEMS_PER_PAGE);
|
|
|
+ const rowSelection = {
|
|
|
+ onSelect: (record, selected) => {
|
|
|
+ },
|
|
|
+ onSelectAll: (selected, selectedRows) => {
|
|
|
+ },
|
|
|
+ onChange: (selectedRowKeys, selectedRows) => {
|
|
|
+ setSelectedKeys(selectedRows);
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
return (
|
|
|
<>
|
|
|
<Form onSubmit={searchRedemptions}>
|
|
|
@@ -153,159 +305,19 @@ const RedemptionsTable = () => {
|
|
|
/>
|
|
|
</Form>
|
|
|
|
|
|
- <Table basic compact size='small'>
|
|
|
- <Table.Header>
|
|
|
- <Table.Row>
|
|
|
- <Table.HeaderCell
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => {
|
|
|
- sortRedemption('id');
|
|
|
- }}
|
|
|
- >
|
|
|
- ID
|
|
|
- </Table.HeaderCell>
|
|
|
- <Table.HeaderCell
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => {
|
|
|
- sortRedemption('name');
|
|
|
- }}
|
|
|
- >
|
|
|
- 名称
|
|
|
- </Table.HeaderCell>
|
|
|
- <Table.HeaderCell
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => {
|
|
|
- sortRedemption('status');
|
|
|
- }}
|
|
|
- >
|
|
|
- 状态
|
|
|
- </Table.HeaderCell>
|
|
|
- <Table.HeaderCell
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => {
|
|
|
- sortRedemption('quota');
|
|
|
- }}
|
|
|
- >
|
|
|
- 额度
|
|
|
- </Table.HeaderCell>
|
|
|
- <Table.HeaderCell
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => {
|
|
|
- sortRedemption('created_time');
|
|
|
- }}
|
|
|
- >
|
|
|
- 创建时间
|
|
|
- </Table.HeaderCell>
|
|
|
- <Table.HeaderCell
|
|
|
- style={{ cursor: 'pointer' }}
|
|
|
- onClick={() => {
|
|
|
- sortRedemption('redeemed_time');
|
|
|
- }}
|
|
|
- >
|
|
|
- 兑换时间
|
|
|
- </Table.HeaderCell>
|
|
|
- <Table.HeaderCell>操作</Table.HeaderCell>
|
|
|
- </Table.Row>
|
|
|
- </Table.Header>
|
|
|
-
|
|
|
- <Table.Body>
|
|
|
- {redemptions
|
|
|
- .slice(
|
|
|
- (activePage - 1) * ITEMS_PER_PAGE,
|
|
|
- activePage * ITEMS_PER_PAGE
|
|
|
- )
|
|
|
- .map((redemption, idx) => {
|
|
|
- if (redemption.deleted) return <></>;
|
|
|
- return (
|
|
|
- <Table.Row key={redemption.id}>
|
|
|
- <Table.Cell>{redemption.id}</Table.Cell>
|
|
|
- <Table.Cell>{redemption.name ? redemption.name : '无'}</Table.Cell>
|
|
|
- <Table.Cell>{renderStatus(redemption.status)}</Table.Cell>
|
|
|
- <Table.Cell>{renderQuota(redemption.quota)}</Table.Cell>
|
|
|
- <Table.Cell>{renderTimestamp(redemption.created_time)}</Table.Cell>
|
|
|
- <Table.Cell>{redemption.redeemed_time ? renderTimestamp(redemption.redeemed_time) : "尚未兑换"} </Table.Cell>
|
|
|
- <Table.Cell>
|
|
|
- <div>
|
|
|
- <Button
|
|
|
- size={'small'}
|
|
|
- positive
|
|
|
- onClick={async () => {
|
|
|
- if (await copy(redemption.key)) {
|
|
|
- showSuccess('已复制到剪贴板!');
|
|
|
- } else {
|
|
|
- showWarning('无法复制到剪贴板,请手动复制,已将兑换码填入搜索框。')
|
|
|
- setSearchKeyword(redemption.key);
|
|
|
- }
|
|
|
- }}
|
|
|
- >
|
|
|
- 复制
|
|
|
- </Button>
|
|
|
- <Popup
|
|
|
- trigger={
|
|
|
- <Button size='small' negative>
|
|
|
- 删除
|
|
|
- </Button>
|
|
|
- }
|
|
|
- on='click'
|
|
|
- flowing
|
|
|
- hoverable
|
|
|
- >
|
|
|
- <Button
|
|
|
- negative
|
|
|
- onClick={() => {
|
|
|
- manageRedemption(redemption.id, 'delete', idx);
|
|
|
- }}
|
|
|
- >
|
|
|
- 确认删除
|
|
|
- </Button>
|
|
|
- </Popup>
|
|
|
- <Button
|
|
|
- size={'small'}
|
|
|
- disabled={redemption.status === 3} // used
|
|
|
- onClick={() => {
|
|
|
- manageRedemption(
|
|
|
- redemption.id,
|
|
|
- redemption.status === 1 ? 'disable' : 'enable',
|
|
|
- idx
|
|
|
- );
|
|
|
- }}
|
|
|
- >
|
|
|
- {redemption.status === 1 ? '禁用' : '启用'}
|
|
|
- </Button>
|
|
|
- <Button
|
|
|
- size={'small'}
|
|
|
- as={Link}
|
|
|
- to={'/redemption/edit/' + redemption.id}
|
|
|
- >
|
|
|
- 编辑
|
|
|
- </Button>
|
|
|
- </div>
|
|
|
- </Table.Cell>
|
|
|
- </Table.Row>
|
|
|
- );
|
|
|
- })}
|
|
|
- </Table.Body>
|
|
|
-
|
|
|
- <Table.Footer>
|
|
|
- <Table.Row>
|
|
|
- <Table.HeaderCell colSpan='8'>
|
|
|
- <Button size='small' as={Link} to='/redemption/add' loading={loading}>
|
|
|
- 添加新的兑换码
|
|
|
- </Button>
|
|
|
- <Pagination
|
|
|
- floated='right'
|
|
|
- activePage={activePage}
|
|
|
- onPageChange={onPaginationChange}
|
|
|
- size='small'
|
|
|
- siblingRange={1}
|
|
|
- totalPages={
|
|
|
- Math.ceil(redemptions.length / ITEMS_PER_PAGE) +
|
|
|
- (redemptions.length % ITEMS_PER_PAGE === 0 ? 1 : 0)
|
|
|
- }
|
|
|
- />
|
|
|
- </Table.HeaderCell>
|
|
|
- </Table.Row>
|
|
|
- </Table.Footer>
|
|
|
+ <Table style={{marginTop: 20}} columns={columns} dataSource={pageData} pagination={{
|
|
|
+ currentPage: activePage,
|
|
|
+ pageSize: ITEMS_PER_PAGE,
|
|
|
+ total: tokenCount,
|
|
|
+ // showSizeChanger: true,
|
|
|
+ // pageSizeOptions: [10, 20, 50, 100],
|
|
|
+ formatPageText: (page) => `第 ${page.currentStart} - ${page.currentEnd} 条,共 ${redemptions.length} 条`,
|
|
|
+ // onPageSizeChange: (size) => {
|
|
|
+ // setPageSize(size);
|
|
|
+ // setActivePage(1);
|
|
|
+ // },
|
|
|
+ onPageChange: handlePageChange,
|
|
|
+ }} loading={loading} rowSelection={rowSelection}>
|
|
|
</Table>
|
|
|
</>
|
|
|
);
|