Compare commits

..

39 Commits

Author SHA1 Message Date
zzy 4f4edf580e NFT签名添加assetID 2022-10-12 16:53:34 +08:00
zzy c9046ac173 修改NFT创建 2022-10-12 14:31:10 +08:00
zzy 0df3015576 修改权限 2022-10-10 16:19:45 +08:00
zzy dd30202a7f 添加数据看板接口 2022-09-23 17:44:46 +08:00
zzy 4cffaf367e 按钮权限整理 2022-09-14 11:27:48 +08:00
zzy cac5714935 删除无用文件 2022-09-13 20:12:41 +08:00
zzy fd840d4eed 授权等功能补充 2022-09-13 20:08:55 +08:00
zzy 9b8188a288 权限修改 2022-09-13 09:23:09 +08:00
zzy 514d0db8f3 新增代币,NFT等模块功能实现 2022-09-07 10:40:17 +08:00
zzy 9b71639ff9 修改规范 2022-08-24 16:35:43 +08:00
zzy 447ace2c80 修改规范 2022-08-24 16:33:31 +08:00
zzy 1423de8769 修改规范 2022-08-24 16:31:16 +08:00
zzy 149675a7e0 修改规范 2022-08-24 16:30:41 +08:00
zzy 55b6eb931c 修改规范 2022-08-24 16:28:33 +08:00
zzy a5040d8541 Merge branch 'dev' into user-zzy-dev 2022-08-24 15:21:00 +08:00
zzy 1cfda3b5b3 接口调试修改 2022-08-24 15:03:09 +08:00
vance b1ba349063 修复冲突 2022-08-23 15:45:51 +08:00
vance 494b7b467a 文件夹名称修改 2022-08-18 16:23:20 +08:00
vance 566bbd8e35 修改文件夹大小写问题 2022-08-18 16:20:39 +08:00
vance 144152bafc 代码修改规范 2022-08-18 16:03:44 +08:00
vance d8f099d7fd 代码规范修改 2022-08-18 16:01:17 +08:00
zzy ca87af0bdc 规范修改 2022-08-18 15:46:38 +08:00
zzy 827d10360c 修改规范 2022-08-18 15:43:56 +08:00
zzy 8a8c69b8ad 规范修改 2022-08-18 15:41:55 +08:00
zzy f6a6d7ba96 规范修改 2022-08-18 15:31:36 +08:00
zzy 9ac71cf6e5 修改规范 2022-08-18 15:31:04 +08:00
vance b2e43272d9 代码规范修改 2022-08-17 15:57:29 +08:00
vance 9f74a89073 代码规范修改 2022-08-16 15:54:04 +08:00
vance d13aab8aa4 代码优化 2022-08-16 14:46:04 +08:00
zzy 377e21b87a 规范修改 2022-08-16 14:45:09 +08:00
zzy 65c09c20e6 接口调试修改 2022-08-16 11:33:31 +08:00
vance afcaddcc0f 代码规范修改,NFT基础页面提交 2022-08-09 16:00:10 +08:00
vance d2991a5ddd 数据看板完善 2022-08-03 16:16:26 +08:00
zzy 2d06115a4e 添加密钥管理 2022-08-03 15:03:17 +08:00
zzy 31198544d7 系统管理功能完善 2022-07-30 16:42:08 +08:00
vance 43c2cdb98b 数据看板模块初步完成 2022-07-28 16:53:51 +08:00
zzy 3d6c3b873c 系统管理页面提交 2022-07-27 17:47:38 +08:00
zzy 2294e72882 充值系统模块提交 2022-07-25 17:29:24 +08:00
gary 53ca93a288 feat: 增加说明文件 2022-06-07 13:58:02 +08:00
86 changed files with 19079 additions and 12031 deletions

View File

@ -7,7 +7,7 @@ This project is initialized with [Ant Design Pro](https://pro.ant.design). Follo
Install `node_modules`:
```bash
npm install
yarn install
```
or
@ -55,3 +55,14 @@ npm test
## More
You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro).
### 参考文档
1. [ts 入门](https://ts.xcatliu.com/)
1. [react hook 入门](https://www.ruanyifeng.com/blog/2019/09/react-hooks.html)
1. [antd4.0](https://ant-design.gitee.io/components/overview-cn/)
1. [@ant-design/pro-layout](https://procomponents.ant.design/components/layout)
1. [@ant-design/icons](https://ant.design/components/icon-cn/#API)
1. [@ant-design/pro-table](https://procomponents.ant.design/components/table)
1. [@formily/antd-components](https://formilyjs.org/#/zoi8i0/ZrsYs6hytQ)
1. [@formily](https://formilyjs.org)

View File

@ -12,7 +12,7 @@ const Settings: LayoutSettings & {
fixedHeader: false,
fixSiderbar: true,
colorWeak: false,
title: 'Pro',
title: 'TBG',
pwa: false,
logo: '/pro_icon.svg',
iconfontUrl: '',

View File

@ -9,9 +9,11 @@
export default {
dev: {
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
'/admin/': {
'/tbg/api/v1': {
// 要代理的地址
target: '',
target: 'http://81.71.13.173:9080',
// target: 'http://192.168.88.238:9999',
// target: 'http://127.0.0.1:9999',
// 配置了这个可以从 http 代理到 https
// 依赖 origin 的功能可能需要这个,比如 cookie
changeOrigin: true,

View File

@ -56,14 +56,18 @@
"@formily/antd": "^2.0.7",
"@formily/core": "^2.0.7",
"@formily/react": "^2.0.7",
"@metamask/detect-provider": "^1.2.0",
"@umijs/route-utils": "^2.0.3",
"ahooks": "^2.10.14",
"antd": "^4.17.2",
"axios": "^0.24.0",
"bignumber.js": "^9.0.0",
"braft-editor": "^2.3.9",
"classnames": "^2.2.6",
"echarts": "^5.3.3",
"echarts-for-react": "^3.0.2",
"lodash": "^4.17.11",
"moment": "^2.25.3",
"moment": "^2.29.4",
"omit.js": "^2.0.2",
"rc-menu": "^9.0.13",
"rc-util": "^5.14.0",
@ -72,7 +76,10 @@
"react-dom": "^17.0.0",
"react-helmet-async": "^1.0.4",
"umi": "^3.5.0",
"umi-serve": "^1.9.10"
"umi-serve": "^1.9.10",
"web3": "^1.7.5",
"@metamask/detect-provider": "^1.2.0"
},
"devDependencies": {
"@ant-design/pro-cli": "^2.0.2",

View File

@ -1,9 +1,26 @@
/**
* @see https://umijs.org/zh-CN/plugins/plugin-access
* */
export default function access(initialState: { currentUser?: API.CurrentUser | undefined }) {
const { currentUser } = initialState || {};
export default function access(initialState: {
currentUser?: API.CurrentUser | undefined;
routeList: any;
}) {
const { currentUser, routeList } = initialState || {};
return {
canAdmin: currentUser && currentUser.access === 'admin',
normalRouteFilter: (route: any) => {
if (routeList == null || routeList == undefined || routeList.length == 0) {
return true;
}
return routeList.includes(route.code);
},
canShowButton: (buttonCode) => {
// return true;
if (routeList == null || routeList == undefined || routeList.length == 0) {
return true;
}
return routeList.includes(buttonCode);
},
};
}

View File

@ -4,8 +4,19 @@ import { history } from 'umi';
import RightContent from '@/components/RightContent';
import RoutePath from '@/routes/routePath';
import { CACHE_TOKEN } from '@/constants/cacheKey';
import { getAccounPermission } from './services/system/accountManage';
// ProLayout 支持的api https://procomponents.ant.design/components/layout
export async function getInitialState() {
if (localStorage.getItem(CACHE_TOKEN)) {
const res = await getAccounPermission({});
return {
routeList: res,
};
}
}
export const layout: RunTimeLayoutConfig = ({ initialState }) => {
return {
rightContentRender: () => <RightContent />,

View File

@ -0,0 +1,65 @@
// 创建弹窗
import React, { useRef } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input, Select } from '@formily/antd';
import { modifyPassword } from '@/services/login';
interface ModifyPasswordModalPropsType extends ModalProps {
onCancel: () => void;
onOk: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
Select,
},
});
const form = createForm({});
const ModifyPasswordModal = ({ onOk, onCancel, ...rest }: ModifyPasswordModalPropsType) => {
const handleOk = async () => {
onOk();
const formState = form.getFormState();
await modifyPassword(formState.values);
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="修改密码" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="password"
title="旧密码"
required
x-decorator="FormItem"
x-component="Input"
x-component-props={{
placeholder: '请输入旧密码',
}}
/>
<SchemaField.String
name="new_password"
title="新密码"
required
x-decorator="FormItem"
x-component="Input"
x-component-props={{
placeholder: '请输入新密码',
}}
/>
</SchemaField>
</Form>
</Modal>
);
};
export default ModifyPasswordModal;

View File

@ -1,5 +1,5 @@
import React, { useCallback } from 'react';
import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
import React, { useState, useCallback } from 'react';
import { LogoutOutlined, SettingOutlined, UserOutlined, EditOutlined } from '@ant-design/icons';
import { Avatar, Menu, Spin } from 'antd';
import { history } from 'umi';
import { stringify } from 'querystring';
@ -7,7 +7,8 @@ import HeaderDropdown from '../HeaderDropdown';
import styles from './index.less';
import type { MenuInfo } from 'rc-menu/lib/interface';
import RoutePath from '@/routes/routePath';
import { CACHE_TOKEN } from '@/constants/cacheKey';
import { CACHE_TOKEN, CACHE_USERNAME } from '@/constants/cacheKey';
import ModifyPasswordModal from '@/components/ModifyPassword/ModifyPasswordModal';
export type GlobalHeaderRightProps = {
menu?: boolean;
@ -22,6 +23,7 @@ const loginOut = async () => {
// Note: There may be security issues, please note
if (window.location.pathname !== RoutePath.LOGIN && !redirect) {
localStorage.removeItem(CACHE_TOKEN);
localStorage.removeItem(CACHE_USERNAME);
history.replace({
pathname: RoutePath.LOGIN,
search: stringify({
@ -32,8 +34,18 @@ const loginOut = async () => {
};
const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
const [isModalVisible, setIsModalVisible] = useState(false);
const modifyPassword = async () => {
setIsModalVisible(true);
};
const onMenuClick = useCallback((event: MenuInfo) => {
const { key } = event;
if (key === 'modify') {
modifyPassword();
return;
}
if (key === 'logout') {
loginOut();
return;
@ -55,7 +67,7 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
const currentUser = {
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
name: 'admin',
name: localStorage.getItem(CACHE_USERNAME),
};
if (!currentUser || !currentUser.name) {
@ -77,7 +89,10 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
</Menu.Item>
)}
{menu && <Menu.Divider />}
<Menu.Item key="modify">
<EditOutlined />
</Menu.Item>
<Menu.Item key="logout">
<LogoutOutlined />
退
@ -85,12 +100,24 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
</Menu>
);
return (
<HeaderDropdown overlay={menuHeaderDropdown}>
<span className={`${styles.action} ${styles.account}`}>
<Avatar size="small" className={styles.avatar} src={currentUser.avatar} alt="avatar" />
<span className={`${styles.name} anticon`}>{currentUser.name}</span>
</span>
</HeaderDropdown>
<div>
<HeaderDropdown overlay={menuHeaderDropdown}>
<span className={`${styles.action} ${styles.account}`}>
<Avatar size="small" className={styles.avatar} src={currentUser.avatar} alt="avatar" />
<span className={`${styles.name} anticon`}>{currentUser.name}</span>
</span>
</HeaderDropdown>
<ModifyPasswordModal
visible={isModalVisible}
onCancel={function () {
setIsModalVisible(false);
}}
onOk={function () {
console.log('onOk');
setIsModalVisible(false);
}}
/>
</div>
);
};

View File

@ -0,0 +1,14 @@
.disabled {
color: @disabled-color;
cursor: not-allowed;
&:hover {
color: @disabled-color !important;
}
}
.confirm-btn {
color: @success-color;
&:hover {
color: @success-color !important;
}
}

View File

@ -0,0 +1,24 @@
import React from 'react';
import { Popconfirm } from 'antd';
import styles from './index.less';
import classNames from 'classnames';
interface PropsType {
onConfirm: () => void;
title?: string;
buttonName?: string;
disabled?: boolean;
}
const ConfirmButton: React.FC<PropsType> = ({
onConfirm,
disabled = false,
title = '',
buttonName = '',
}) => {
return (
<Popconfirm title={title} disabled={disabled} onConfirm={onConfirm} okText="是" cancelText="否">
<a className={classNames(styles.confirmBtn, disabled ? styles.disabled : '')}>{buttonName}</a>
</Popconfirm>
);
};
export default ConfirmButton;

View File

@ -0,0 +1,14 @@
.disabled {
color: @disabled-color;
cursor: not-allowed;
&:hover {
color: @disabled-color !important;
}
}
.reject-btn {
color: @error-color;
&:hover {
color: @error-color !important;
}
}

View File

@ -0,0 +1,24 @@
import React from 'react';
import { Popconfirm } from 'antd';
import styles from './index.less';
import classNames from 'classnames';
interface PropsType {
onConfirm: () => void;
title?: string;
buttonName?: string;
disabled?: boolean;
}
const ConfirmButton: React.FC<PropsType> = ({
onConfirm,
disabled = false,
title = '',
buttonName = '',
}) => {
return (
<Popconfirm title={title} disabled={disabled} onConfirm={onConfirm} okText="是" cancelText="否">
<a className={classNames(styles.rejectBtn, disabled ? styles.disabled : '')}>{buttonName}</a>
</Popconfirm>
);
};
export default ConfirmButton;

View File

@ -12,9 +12,11 @@ import {
} from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import ProTable, { ProColumns, ActionType, ProTableProps } from '@ant-design/pro-table';
import { Access, useAccess } from 'umi';
export interface toolBarActionsItem {
type: 'add' | 'batchDelete';
permission?: string;
text?: string;
onConfirm: (val?: string[]) => void;
}
@ -29,6 +31,8 @@ type Record<K extends keyof any, T> = {
};
const Table = <T extends Record<string, any>>(props: PropsType) => {
const access = useAccess();
const {
columns: columnsProps = [],
search: searchProps = {},
@ -88,7 +92,9 @@ const Table = <T extends Record<string, any>>(props: PropsType) => {
const optionRender = (_: any, val: any, ...rest: any) => {
return (
<Space size={0} split={<Divider type="vertical" />}>
{(item.render && (item.render(_, val, ...rest) as []))?.map((item2) => item2)}
{(item.render && (item.render(_, val, ...rest) as []))?.map((item2) => {
return item2;
})}
</Space>
);
};
@ -142,6 +148,7 @@ const Table = <T extends Record<string, any>>(props: PropsType) => {
],
};
}, [searchProps, columnsProps]);
const handleBatchDelete = (action: any) => {
if (selectedRows.length === 0) {
message.warn('请选择要操作的项');
@ -164,28 +171,32 @@ const Table = <T extends Record<string, any>>(props: PropsType) => {
toolBarActions.forEach((item) => {
if (item.type === 'add') {
buttonList.push(
<Button
key="add"
type="primary"
onClick={() => {
item.onConfirm();
}}
>
<PlusOutlined />
{item.text || '新建'}
</Button>,
<Access accessible={access.canShowButton(item.permission)}>
<Button
key="add"
type="primary"
onClick={() => {
item.onConfirm();
}}
>
<PlusOutlined />
{item.text || '添加'}
</Button>
</Access>,
);
} else if (item.type === 'batchDelete') {
buttonList.push(
<Button
key="del"
danger
onClick={() => {
handleBatchDelete(item.onConfirm);
}}
>
{item.text || '批量删除'}
</Button>,
<Access accessible={access.canShowButton(item.permission)}>
<Button
key="del"
danger
onClick={() => {
handleBatchDelete(item.onConfirm);
}}
>
{item.text || '批量删除'}
</Button>
</Access>,
);
}
});

449
src/constants/abi.ts Normal file
View File

@ -0,0 +1,449 @@
export default [
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'owner',
type: 'address',
},
{
indexed: true,
internalType: 'address',
name: 'approved',
type: 'address',
},
{
indexed: true,
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
],
name: 'Approval',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'owner',
type: 'address',
},
{
indexed: true,
internalType: 'address',
name: 'operator',
type: 'address',
},
{
indexed: false,
internalType: 'bool',
name: 'approved',
type: 'bool',
},
],
name: 'ApprovalForAll',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'previousOwner',
type: 'address',
},
{
indexed: true,
internalType: 'address',
name: 'newOwner',
type: 'address',
},
],
name: 'OwnershipTransferred',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'from',
type: 'address',
},
{
indexed: true,
internalType: 'address',
name: 'to',
type: 'address',
},
{
indexed: true,
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
],
name: 'Transfer',
type: 'event',
},
{
inputs: [
{
internalType: 'address',
name: 'to',
type: 'address',
},
{
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
],
name: 'approve',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
],
name: 'Burn',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'to',
type: 'address',
},
{
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
],
name: 'GTransfer',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'to',
type: 'address',
},
{
internalType: 'bytes',
name: 'signature',
type: 'bytes',
},
],
name: 'Mint',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [],
name: 'renounceOwnership',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'from',
type: 'address',
},
{
internalType: 'address',
name: 'to',
type: 'address',
},
{
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
],
name: 'safeTransferFrom',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'from',
type: 'address',
},
{
internalType: 'address',
name: 'to',
type: 'address',
},
{
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
{
internalType: 'bytes',
name: 'data',
type: 'bytes',
},
],
name: 'safeTransferFrom',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'operator',
type: 'address',
},
{
internalType: 'bool',
name: 'approved',
type: 'bool',
},
],
name: 'setApprovalForAll',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'from',
type: 'address',
},
{
internalType: 'address',
name: 'to',
type: 'address',
},
{
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
],
name: 'transferFrom',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'newOwner',
type: 'address',
},
],
name: 'transferOwnership',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'string',
name: 'tokenName',
type: 'string',
},
{
internalType: 'string',
name: 'tokenSymbol',
type: 'string',
},
],
stateMutability: 'nonpayable',
type: 'constructor',
},
{
inputs: [
{
internalType: 'address',
name: 'owner',
type: 'address',
},
],
name: 'balanceOf',
outputs: [
{
internalType: 'uint256',
name: '',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
],
name: 'getApproved',
outputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'owner',
type: 'address',
},
{
internalType: 'address',
name: 'operator',
type: 'address',
},
],
name: 'isApprovedForAll',
outputs: [
{
internalType: 'bool',
name: '',
type: 'bool',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'name',
outputs: [
{
internalType: 'string',
name: '',
type: 'string',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'owner',
outputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
],
name: 'ownerOf',
outputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'bytes4',
name: 'interfaceId',
type: 'bytes4',
},
],
name: 'supportsInterface',
outputs: [
{
internalType: 'bool',
name: '',
type: 'bool',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'symbol',
outputs: [
{
internalType: 'string',
name: '',
type: 'string',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'uint256',
name: 'tokenId',
type: 'uint256',
},
],
name: 'tokenURI',
outputs: [
{
internalType: 'string',
name: '',
type: 'string',
},
],
stateMutability: 'view',
type: 'function',
},
];

View File

@ -1 +1,2 @@
export const CACHE_TOKEN = 'token';
export const CACHE_USERNAME = 'name';

View File

@ -0,0 +1,4 @@
export enum ContractType {
ERC20 = '20',
NFT721 = '721',
}

View File

@ -0,0 +1,7 @@
export enum NoticeType {
RECHARGE = 10000,
WITHDRAW = 20000,
NFTCREATE = 30000,
NFTTRANSFER = 30010,
NFTTDISPOSE = 30020,
}

View File

@ -0,0 +1,6 @@
export enum TimeType {
DAY = '1',
WEEK = '2',
MONTH = '3',
}
export default TimeType;

View File

@ -0,0 +1,5 @@
export enum WithdrawType {
SUCCESS = 10000,
FAILED = 20000,
PENDDING = 30000,
}

View File

@ -0,0 +1,102 @@
import React, { useRef, useState } from 'react';
import echarts from 'echarts/lib/echarts';
import {
getActiveData,
getMonthUserData,
getOldUserData,
getTodayUserData,
getWeekUserData,
} from '@/services/dataBorad/activeAnalysis';
import MoreData from '@/widget/MoreData';
import ReactEcharts from 'echarts-for-react';
import { Card, Col, Row, Select } from 'antd';
const ActiveAnalysis: React.FC = () => {
const { Option } = Select;
const [timeType, setTimeType] = useState('day');
const getData = async (func) => {
const res = await func({});
return res.data;
};
const [data, setData] = useState(getData(getTodayUserData));
const [oldUserData, setOldUserData] = useState(getData(getOldUserData));
const [weekData, setWeekData] = useState(getData(getWeekUserData));
const [monthData, setMonthData] = useState(getData(getMonthUserData));
const getOption = async (item = 'day') => {
const res = await getActiveData({ time_type: item });
const xData: any = [];
const seriesData: any = [];
res.data.foreach((item: any) => {
xData.push(item.time);
seriesData.push(item.dwn);
});
const option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
// data: xData,
},
yAxis: {
type: 'value',
},
series: [
{
data: [150, 230, 224, 218, 135, 147, 360],
// data: seriesData.reverse(),
type: 'line',
},
],
};
return option;
};
const [option, setOption] = useState(getOption);
const setTimeOption = (val: any) => {
setTimeType(val);
setOption(getOption(val));
};
return (
<div>
<Row gutter={24} justify="space-between" style={{ marginBottom: 10 }}>
<Col span={6}>
<Card style={{ height: '200px' }}>{MoreData(data, '今日活跃用户数')}</Card>
</Col>
<Col span={6}>
<Card style={{ height: '200px' }}>{MoreData(oldUserData, '今日登录老用户数')}</Card>
</Col>
<Col span={6}>
<Card style={{ height: '200px' }}>{MoreData(weekData, 'WAU(本周去重人数)')}</Card>
</Col>
<Col span={6}>
<Card style={{ height: '200px' }}>{MoreData(monthData, 'MAU(本月去重人数)')}</Card>
</Col>
</Row>
<Card style={{ width: '100%' }}>
<Row gutter={32} style={{ marginBottom: 10 }}>
<Col span={6}>
<h1></h1>
<Select
defaultValue="lucy"
value={timeType}
style={{ width: 100 }}
bordered={false}
onChange={(val) => {
setTimeOption(val);
}}
>
<Option value="day"></Option>
<Option value="week"></Option>
<Option value="month"></Option>
</Select>
</Col>
<Col span={18}>
<ReactEcharts option={option} />
</Col>
</Row>
</Card>
</div>
);
};
export default ActiveAnalysis;

View File

@ -0,0 +1,141 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import moment from 'moment';
import TimeType from '@/constants/enum/timeType';
import MoreData from '@/widget/MoreData';
import { getCoreData, getListData } from '@/services/dataBorad/coreData';
import { Card, Col, Row, Select } from 'antd';
import { fetchTableData } from '@/utils/table';
const CoreData: React.FC = () => {
const tableRef = useRef<ActionType>();
const TimeTypeList = [
{
label: '每日',
value: TimeType.DAY,
},
{
label: '每周',
value: TimeType.WEEK,
},
{
label: '每月',
value: TimeType.MONTH,
},
];
const [coreData, setCoreData] = useState({
today_active_user: '',
today_new_user: '',
month_recharge: '',
recharge_sum: '',
});
const columns: ProColumns<any>[] = [
{
title: '时间类型',
dataIndex: 'code',
hideInTable: true,
initialValue: TimeType.DAY,
valueEnum: () => {
const options = {};
TimeTypeList.forEach((item) => {
options[item.value] = { text: item.label };
});
return options;
},
},
{
title: '日期',
dataIndex: 'start_time',
hideInSearch: true,
},
{
title: '时间',
dataIndex: 'select_time',
valueType: 'dateRange',
hideInTable: true,
ellipsis: true,
},
{
title: '活跃用户数',
dataIndex: 'active_user',
hideInSearch: true,
},
{
title: '当日充值总额',
dataIndex: 'today_recharge',
hideInSearch: true,
},
{
title: '付费用户数',
dataIndex: 'pay_user',
hideInSearch: true,
},
{
title: '付费率',
dataIndex: 'pay_ratio',
hideInSearch: true,
},
];
return (
<div>
<Row gutter={24} style={{ marginBottom: 10 }}>
<Col span={6}>
<Card style={{ height: '120px' }}>
{MoreData(null, '今日活跃用户数', coreData.today_active_user + '人')}
</Card>
</Col>
<Col span={6}>
<Card style={{ height: '120px' }}>
{MoreData(null, '今日新增人数', coreData.today_new_user + '人')}
</Card>
</Col>
<Col span={6}>
<Card style={{ height: '120px' }}>
{MoreData(null, '本月充值总额', coreData.month_recharge + '万元')}
</Card>
</Col>
<Col span={6}>
<Card style={{ height: '120px' }}>
{MoreData(null, '充值总额', coreData.recharge_sum + '万元')}
</Card>
</Col>
</Row>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
request={async (params) => {
params.code = parseInt(params.code);
if ((params.select_time ?? '') !== '') {
params.start_time = moment(params.select_time[0]).valueOf() / 1000;
params.end_time = moment(params.select_time[1]).valueOf() / 1000;
}
const data = await getCoreData({});
setCoreData(data as any);
const res = await fetchTableData(getListData, params);
console.log('res', res);
for (const key in res.data) {
if (Object.prototype.hasOwnProperty.call(res.data, key)) {
const element = res.data[key];
if (
element.start_time != '' &&
element.start_time != undefined &&
element.start_time != null
) {
element.start_time = moment(element.start_time * 1000).format(
'YYYY-MM-DD HH:mm:ss',
);
}
}
}
return res;
}}
/>
</div>
);
};
export default CoreData;

View File

@ -0,0 +1,116 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { getRententionData } from '@/services/dataBorad/rechargeAnalysis';
import { Select } from 'antd';
import moment from 'moment';
import TimeType from '@/constants/enum/timeType';
const Address: React.FC = () => {
const tableRef = useRef<ActionType>();
const [timeType, setTimeType] = useState(TimeType.DAY);
const columns: ProColumns<any>[] = [
{
title: '时间类型',
dataIndex: 'time_type',
hideInTable: true,
initialValue: TimeType.DAY,
renderFormItem: (item, { type, defaultRender, ...rest }, form) => {
return (
<Select
options={[
{
label: '每日',
value: TimeType.DAY,
},
{
label: '每周',
value: TimeType.WEEK,
},
{
label: '每月',
value: TimeType.MONTH,
},
]}
onChange={(val) => {
setTimeType(val);
}}
/>
);
},
},
{
title: '日期',
dataIndex: 'time',
width: '10%',
hideInSearch: true,
},
{
title: '时间筛选',
dataIndex: 'timeData',
hideInTable: true,
width: '20%',
},
{
title: '时间',
dataIndex: 'select_time',
valueType: 'dateRange',
width: '20%',
hideInTable: true,
ellipsis: true,
},
{
title: '付费次数',
dataIndex: 'pay_num',
hideInSearch: true,
width: '20%',
},
{
title: '付费用户数',
dataIndex: 'pay_user',
width: 150,
hideInSearch: true,
},
{
title: '付费金额',
dataIndex: 'pay_price',
hideInSearch: true,
width: '20%',
},
{
title: '付费率',
dataIndex: 'rate',
hideInSearch: true,
width: '20%',
},
{
title: 'ARPU',
dataIndex: 'arpu',
hideInSearch: true,
width: '20%',
},
{
title: 'ARPPU',
dataIndex: 'arppu',
hideInSearch: true,
width: '20%',
},
];
return (
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
// search={false}
request={async (params) => {
if ((params.select_time ?? '') !== '') {
params.start_time = moment(params.select_time[0]).valueOf() / 1000;
params.end_time = moment(params.select_time[1]).valueOf() / 1000;
}
return getRententionData(params);
}}
/>
);
};
export default Address;

View File

@ -0,0 +1,86 @@
import React, { useRef } from 'react';
import Modal, { ModalProps } from '@/components/Modal';
import { fetchTableData } from '@/utils/table';
import Table, { ProColumns, ActionType } from '@/components/Table';
import TimeText from '@/components/Typography/TimeText';
import { getChannelData } from '@/services/dataBorad/retentionAnalysis';
interface TableModalPropsType extends ModalProps {
modalData: any;
onCancel: () => void;
}
const TableModal = ({ onCancel, modalData, ...rest }: TableModalPropsType) => {
const tableRef = useRef<ActionType>();
const columns: ProColumns<any>[] = [
{
title: '时间',
dataIndex: 'time',
hideInSearch: true,
width: '20%',
render: (_, row) => [<TimeText value={row} format={'YYYY-MM-DD'} />],
},
{
title: '渠道类型',
dataIndex: 'channel_name',
hideInSearch: true,
ellipsis: true,
},
{
title: '登录用户数',
dataIndex: 'login_user',
hideInSearch: true,
ellipsis: true,
},
{
title: '第一日',
dataIndex: 'day_one',
hideInSearch: true,
},
{
title: '第二日',
dataIndex: 'day_two',
hideInSearch: true,
},
{
title: '第四日',
dataIndex: 'day_three',
hideInSearch: true,
},
{
title: '第五日',
dataIndex: 'day_four',
hideInSearch: true,
},
{
title: '第六日',
dataIndex: 'day_five',
hideInSearch: true,
},
{
title: '第七日',
dataIndex: 'day_seven',
hideInSearch: true,
},
];
const handleCancel = () => {
onCancel();
};
return (
<Modal title="渠道活跃分析" onCancel={handleCancel} width={800} {...rest}>
<Table
columns={columns}
rowKey="id"
search={false}
actionRef={tableRef}
request={async (params) => {
params.time = modalData;
return fetchTableData(getChannelData, params);
}}
/>
</Modal>
);
};
export default TableModal;

View File

@ -0,0 +1,107 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import moment from 'moment';
import { fetchTableData } from '@/utils/table';
import { getRententionData } from '@/services/dataBorad/retentionAnalysis';
import TableModal from './components/TableModal';
const RetentionAnalysis = () => {
const [visible, setVisible] = useState(false);
const [requestData, setRequestData] = useState();
const sendData = (row: any) => {
setVisible(true);
setRequestData(row.time);
};
const tableRef = useRef<ActionType>();
const columns: ProColumns<any>[] = [
{
title: '时间',
dataIndex: 'time',
},
{
title: '时间',
dataIndex: 'select_time',
valueType: 'dateRange',
width: '20%',
hideInTable: true,
ellipsis: true,
},
{
title: '登录用户数',
dataIndex: 'login_user',
hideInSearch: true,
ellipsis: true,
},
{
title: '第一日',
dataIndex: 'day_one',
hideInSearch: true,
},
{
title: '第二日',
dataIndex: 'day_two',
hideInSearch: true,
},
{
title: '第四日',
dataIndex: 'day_three',
hideInSearch: true,
},
{
title: '第五日',
dataIndex: 'day_four',
hideInSearch: true,
},
{
title: '第六日',
dataIndex: 'day_five',
hideInSearch: true,
},
{
title: '第七日',
dataIndex: 'day_seven',
hideInSearch: true,
},
{
title: '操作',
valueType: 'option',
width: 150,
render: (_, row) => [
<button
key="edit"
onClick={() => {
sendData(row);
}}
>
</button>,
],
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
request={async (params) => {
if ((params.select_time ?? '') !== '') {
params.start_time = moment(params.select_time[0]).valueOf() / 1000;
params.end_time = moment(params.select_time[1]).valueOf() / 1000;
}
return await fetchTableData(getRententionData, params);
}}
/>
<TableModal
visible={visible}
modalData={requestData}
onCancel={function () {
setVisible(false);
}}
/>
</div>
);
};
export default RetentionAnalysis;

View File

@ -53,3 +53,7 @@
color: @primary-color;
}
}
.drawer-style {
padding: 0;
}

View File

@ -5,20 +5,24 @@ import { useIntl, history, FormattedMessage, useModel } from 'umi';
import { login } from '@/services/login';
import defaultSettings from '../../../config/defaultSettings';
import styles from './index.less';
import { CACHE_TOKEN } from '@/constants/cacheKey';
import { CACHE_TOKEN, CACHE_USERNAME } from '@/constants/cacheKey';
import access from '@/access';
const Login: React.FC = () => {
const intl = useIntl();
const { initialState, refresh } = useModel('@@initialState');
const handleSubmit = async (values: API.LoginParams) => {
// 登录
const res: any = await login({ ...values });
localStorage.setItem(CACHE_TOKEN, res.token);
localStorage.setItem(CACHE_USERNAME, values.name);
/** 此方法会跳转到 redirect 参数所在的位置 */
if (!history) return;
const { query } = history.location;
const { redirect } = query as { redirect: string };
history.push(redirect || '/');
refresh();
access(initialState);
};
return (
@ -35,7 +39,7 @@ const Login: React.FC = () => {
}}
>
<ProFormText
name="username"
name="name"
fieldProps={{
size: 'large',
prefix: <UserOutlined className={styles.prefixIcon} />,

View File

@ -0,0 +1,65 @@
import React, { useRef } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
// import { fetchTableData } from '@/utils/table';
import { Form, FormItem, Input, NumberPicker } from '@formily/antd';
interface AddNftContractModalPropsType extends ModalProps {
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
NumberPicker,
},
});
const form = createForm({});
const AddNftContractModal = ({ onOk, onCancel, ...rest }: AddNftContractModalPropsType) => {
const handleOk = () => {
form.submit(async () => {
const formState = form.getFormState();
onOk(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="添加合约" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="token_name"
title="合约名称"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.String
name="token_symbol"
title="合约单位"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.String
name="description"
title="描述"
required
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default AddNftContractModal;

View File

@ -0,0 +1,100 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { message } from 'antd';
import AddNftContractModal from './components/AddNftContract';
import { getContractInfo } from '@/services/contract';
import { ContractType } from '@/constants/enum/contract';
import { deployContract, initWeb3 } from '@/utils/web3';
import { createNFTContract, getNFTContractList } from '@/services/nft';
import { fetchTableData } from '@/utils/table';
const Address: React.FC = () => {
const tableRef = useRef<ActionType>();
const [visible, setVisible] = useState(false);
const [contractData, setContractData] = useState({});
const columns: ProColumns<any>[] = [
{
title: '合约名称',
dataIndex: 'token_name',
width: '15%',
hideInSearch: true,
},
{
title: '合约单位',
dataIndex: 'token_symbol',
width: '15%',
hideInSearch: true,
},
{
title: '合约地址',
dataIndex: 'address',
hideInSearch: true,
width: '50%',
},
{
title: '描述',
dataIndex: 'description',
hideInSearch: true,
ellipsis: true,
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
search={false}
actionRef={tableRef}
toolBarActions={[
{
type: 'add',
text: '新建NFT合约',
permission: 'nft_contract_create',
onConfirm: async () => {
const res = await getContractInfo({ erc: ContractType.NFT721 });
if ((res as any).bin != '') {
setContractData(res);
setVisible(true);
} else {
message.success('已有NFT合约');
}
},
},
]}
request={async (params) => {
const res = await fetchTableData(getNFTContractList, params);
if (res.data == null) {
return [];
}
return res;
}}
/>
<AddNftContractModal
visible={visible}
onCancel={function () {
setVisible(false);
}}
onOk={async function (val: any): Promise<void> {
try {
await initWeb3();
const res = await deployContract((contractData as any).abi, (contractData as any).bin, [
val.token_name,
val.token_symbol,
]);
val.address = res;
await createNFTContract(val);
message.success('添加成功');
setVisible(false);
} catch (e) {
message.success('发生错误');
setVisible(false);
}
}}
/>
</div>
);
};
export default Address;

View File

@ -0,0 +1,108 @@
import React, { useRef, useState } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input, NumberPicker } from '@formily/antd';
import { Button, message } from 'antd';
import { initWeb3, NFTMint, web3 } from '@/utils/web3';
import { getContractInfo } from '@/services/contract';
import { checkAssetID, getNFTContractList, getSignature, mintNFT } from '@/services/nft';
import { ContractType } from '@/constants/enum/contract';
interface AddNftModalPropsType extends ModalProps {
onOk: () => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
NumberPicker,
},
});
const form = createForm({});
const AddNftModal = ({ onOk, onCancel, ...rest }: AddNftModalPropsType) => {
const [loading, setLoading] = useState(false);
const handleOk = async () => {
form.submit(async () => {
const formState = form.getFormState();
await checkAssetID({ assetid: formState.values.assetid });
setLoading(true);
const isInit = await initWeb3();
if (isInit) {
const contractInfo = (await getContractInfo({ erc: ContractType.NFT721 })) as any;
const res = (await getNFTContractList()) as any;
const signature = (await getSignature({
address: web3.eth.defaultAccount,
assetid: formState.values.assetid,
})) as any;
console.log('signature = ', signature);
let tx_hash = '';
try {
tx_hash = await NFTMint({
abi: contractInfo.abi,
address: res.items[0].address,
toAddress: formState.values.address,
sign: signature,
});
} catch (error) {
setLoading(false);
message.error('创建失败');
onCancel();
return;
}
formState.values.tx_hash = tx_hash;
await mintNFT(formState.values);
setLoading(false);
onOk();
} else {
setLoading(false);
}
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal
title="添加NFT"
width={800}
footer={[
<Button key="cancel" onClick={handleCancel}>
</Button>,
<Button key="submit" type="primary" loading={loading} onClick={handleOk}>
</Button>,
]}
onCancel={handleCancel}
{...rest}
>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="address"
title="所有者地址"
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.String
name="assetid"
title="assetID"
required
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default AddNftModal;

View File

@ -0,0 +1,58 @@
import React, { useRef } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
// import { fetchTableData } from '@/utils/table';
import { Form, FormItem, Input, NumberPicker } from '@formily/antd';
interface TransferNFTModelPropsType extends ModalProps {
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
NumberPicker,
},
});
const form = createForm({});
const TransferNFTModel = ({ onOk, onCancel, ...rest }: TransferNFTModelPropsType) => {
const handleOk = async () => {
form.submit(async () => {
const formState = form.getFormState();
onOk(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="转账NFT" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="token_id"
title="ID"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.String
name="toAddress"
title="发送地址"
required
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default TransferNFTModel;

View File

@ -0,0 +1,77 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { fetchTableData } from '@/utils/table';
import AddNftModal from './components/AddNftModel';
import { getNFTList } from '@/services/nft';
const Address: React.FC = () => {
const tableRef = useRef<ActionType>();
const [visible, setVisible] = useState(false);
const columns: ProColumns<any>[] = [
{
title: '所有者地址',
dataIndex: 'address',
hideInSearch: true,
},
{
title: 'NFT名称',
dataIndex: 'name',
hideInSearch: true,
},
{
title: '图片',
dataIndex: 'image',
hideInSearch: true,
ellipsis: true,
},
{
title: '缩略图',
dataIndex: 'avatar',
hideInSearch: true,
ellipsis: true,
},
{
title: '描述',
dataIndex: 'description',
hideInSearch: true,
},
];
return (
<div>
<Table
columns={columns}
rowKey="tx_hash"
search={false}
actionRef={tableRef}
toolBarActions={[
{
type: 'add',
text: '添加NFT',
permission: 'nft_add',
onConfirm: () => {
setVisible(true);
},
},
]}
request={async (params) => {
const res = await fetchTableData(getNFTList, params);
return res;
}}
/>
<AddNftModal
visible={visible}
onCancel={function () {
setVisible(false);
}}
onOk={async function (): Promise<void> {
tableRef.current?.reload();
setVisible(false);
}}
/>
</div>
);
};
export default Address;

View File

@ -0,0 +1,80 @@
import React, { useRef } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { getRecordList } from '@/services/recharge/record';
import { fetchTableData } from '@/utils/table';
import moment from 'moment';
import { ContractType } from '@/constants/enum/contract';
const NFTTradeList = () => {
const tableRef = useRef<ActionType>();
const columns: ProColumns<any>[] = [
{
title: '时间',
dataIndex: 'time',
hideInTable: true,
valueType: 'dateRange',
ellipsis: true,
},
{
title: '交易哈希',
dataIndex: 'tx_hash',
hideInSearch: true,
ellipsis: true,
},
{
title: 'TokenID',
dataIndex: 'token_id',
width: '10%',
hideInSearch: true,
},
{
title: '块编号',
dataIndex: 'block_number',
hideInSearch: true,
},
{
title: '发送地址',
dataIndex: 'from_address',
hideInSearch: true,
ellipsis: true,
},
{
title: '接受地址',
dataIndex: 'to_address',
hideInSearch: true,
ellipsis: true,
},
{
title: '充值时间',
dataIndex: 'time',
hideInSearch: true,
ellipsis: true,
},
];
return (
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
request={async (params) => {
if ((params.time ?? '') !== '') {
const start = Date.parse(params.time[0] + ' 00:00:00');
const end = Date.parse(params.time[1] + ' 23:59:59');
params.start_time = start / 1000;
params.end_time = end / 1000;
}
params.erc = ContractType.NFT721;
const res = await fetchTableData(getRecordList, params);
for (const key in res.data) {
if (Object.prototype.hasOwnProperty.call(res.data, key)) {
const element = res.data[key];
element.time = moment(element.time * 1000).format('YYYY-MM-DD HH:mm:ss');
}
}
return res;
}}
/>
);
};
export default NFTTradeList;

View File

@ -0,0 +1,134 @@
import React, { useState, useRef } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { addCoinType, getCoinTypeList, modifyCoinType } from '@/services/recharge/coinType';
import { getContractInfo } from '@/services/contract';
import { fetchTableData } from '@/utils/table';
import AddCoinTypeModal from '../components/AddCoinTypeModal';
import EditCoinTypeModal from '../components/EditCoinTypeModal';
import { initWeb3, deployContract, transfer } from '@/utils/web3';
import { ContractType } from '@/constants/enum/contract';
import { Access, useAccess } from 'umi';
import { message } from 'antd';
const CoinTypeList = () => {
const access = useAccess();
const tableRef = useRef<ActionType>();
const [isModalVisible, setIsModalVisible] = useState(false);
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
const [modalData, setModalData] = useState({});
const handleEdit = (row: any) => {
setModalData(row);
setIsEditModalVisible(true);
tableRef.current?.reload();
};
const columns: ProColumns<any>[] = [
{
title: '游戏币名称',
dataIndex: 'name',
hideInSearch: true,
width: 150,
},
{
title: 'usdt比例',
dataIndex: 'usdt_price',
hideInSearch: true,
width: 100,
},
{
title: 'eth比例',
dataIndex: 'eth_price',
hideInSearch: true,
width: 100,
},
{
title: '合约地址',
dataIndex: 'address',
hideInSearch: true,
ellipsis: true,
},
{
title: '代币发行总量',
dataIndex: 'total',
hideInSearch: true,
width: 150,
},
{
title: '操作',
valueType: 'option',
width: 150,
render: (_, row) => [
<Access accessible={access.canShowButton('coin_type_edit')}>
<a
key="edit"
onClick={() => {
handleEdit(row);
}}
>
</a>
</Access>,
],
},
];
return (
<div>
<Table
columns={columns}
rowKey="uuid"
search={false}
toolBarActions={[
{
type: 'add',
permission: 'coin_type_add',
onConfirm: () => {
setIsModalVisible(true);
},
},
]}
actionRef={tableRef}
request={async (params) => {
const res = await fetchTableData(getCoinTypeList, params);
return res;
}}
/>
<AddCoinTypeModal
visible={isModalVisible}
onCancel={function () {
setIsModalVisible(false);
}}
onOk={async function (val: any): Promise<void> {
const res = await getContractInfo({ erc: ContractType.ERC20 });
await initWeb3();
const result = await deployContract((res as any).abi, (res as any).bin, [
val.num,
val.tokenName,
val.tokenSymbol,
]);
val.address = result;
await addCoinType(val);
message.success('代币添加成功');
setIsModalVisible(false);
tableRef.current?.reload();
}}
/>
<EditCoinTypeModal
visible={isEditModalVisible}
editModalData={modalData}
onCancel={function () {
setIsEditModalVisible(false);
}}
onOk={async function (val: any): Promise<void> {
await modifyCoinType(val);
message.success('代币修改成功');
setIsEditModalVisible(false);
tableRef.current?.reload();
}}
/>
</div>
);
};
export default CoinTypeList;

View File

@ -0,0 +1,87 @@
// 添加代币类型弹窗
import React, { useRef } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input, NumberPicker } from '@formily/antd';
interface AddCoinTypeModalPropsType extends ModalProps {
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
NumberPicker,
},
});
const form = createForm({});
const AddCoinTypeModal = ({ onOk, onCancel, ...rest }: AddCoinTypeModalPropsType) => {
const handleOk = async () => {
form.submit(async () => {
const formState = form.getFormState();
onOk(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="添加代币类型" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="name"
title="游戏币名称"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.Number
name="usdt_price"
title="usdt比例"
required
x-decorator="FormItem"
x-component="NumberPicker"
/>
<SchemaField.Number
name="eth_price"
title="eth比例"
required
x-decorator="FormItem"
x-component="NumberPicker"
/>
<SchemaField.String
name="tokenName"
title="代币名称"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.String
name="tokenSymbol"
title="代币单位"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.Number
name="num"
title="数量"
required
x-decorator="FormItem"
x-component="NumberPicker"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default AddCoinTypeModal;

View File

@ -0,0 +1,74 @@
// 修改弹窗
import React, { useRef, useEffect } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input, NumberPicker } from '@formily/antd';
interface EditCoinTypeModalPropsType extends ModalProps {
editModalData: any;
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
NumberPicker,
},
});
const form = createForm({});
const EditCoinTypeModal = ({
onOk,
onCancel,
editModalData,
...rest
}: EditCoinTypeModalPropsType) => {
useEffect(() => {
form.setInitialValues(editModalData);
});
const handleOk = () => {
form.submit(async () => {
const formState = form.getFormState();
onOk(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="修改代币信息" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="name"
title="游戏币名称"
x-disabled
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.Number
name="usdt_price"
title="usdt比例"
x-decorator="FormItem"
x-component="NumberPicker"
/>
<SchemaField.Number
name="eth_price"
title="eth比例"
x-decorator="FormItem"
x-component="NumberPicker"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default EditCoinTypeModal;

View File

@ -0,0 +1,81 @@
import React, { useRef } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { getRecordList } from '@/services/recharge/record';
import { fetchTableData } from '@/utils/table';
import moment from 'moment';
import { ContractType } from '@/constants/enum/contract';
const RecordList = () => {
const tableRef = useRef<ActionType>();
const columns: ProColumns<any>[] = [
{
title: '时间',
dataIndex: 'searchTime',
hideInTable: true,
valueType: 'dateRange',
ellipsis: true,
},
{
title: '交易哈希',
dataIndex: 'tx_hash',
hideInSearch: true,
ellipsis: true,
},
{
title: '金额',
dataIndex: 'price',
width: '10%',
hideInSearch: true,
},
{
title: '游戏币类型',
dataIndex: 'name',
width: '10%',
hideInSearch: true,
},
{
title: '发送地址',
dataIndex: 'from_address',
hideInSearch: true,
ellipsis: true,
},
{
title: '接受地址',
dataIndex: 'to_address',
hideInSearch: true,
ellipsis: true,
},
{
title: '充值时间',
dataIndex: 'time',
hideInSearch: true,
ellipsis: true,
},
];
return (
<Table
columns={columns}
rowKey="tx_hash"
actionRef={tableRef}
request={async (params) => {
if ((params.searchTime ?? '') !== '') {
const start = Date.parse(params.searchTime[0] + ' 00:00:00');
const end = Date.parse(params.searchTime[1] + ' 23:59:59');
params.start_time = start / 1000;
params.end_time = end / 1000;
}
params.erc = ContractType.ERC20;
const res = await fetchTableData(getRecordList, params);
for (const key in res.data) {
if (Object.prototype.hasOwnProperty.call(res.data, key)) {
const element = res.data[key];
element.time = moment(element.time * 1000).format('YYYY-MM-DD HH:mm:ss');
}
}
return res;
}}
/>
);
};
export default RecordList;

View File

@ -0,0 +1,124 @@
import React, { useState, useRef } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import {
getAddressList,
deleteAddress,
createAddress,
modifyAddress,
} from '@/services/recharge/wallet';
import { fetchTableData } from '@/utils/table';
import DeleteButton from '@/components/Table/DeleteButton';
import AddWalletModal from '../components/AddWalletModal';
import EditWalletModal from '../components/EditWalletModal';
import { Access, useAccess } from 'umi';
import { message } from 'antd';
const CollectionAddressList = () => {
const tableRef = useRef<ActionType>();
const access = useAccess();
const [isModalVisible, setIsModalVisible] = useState(false);
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
const [modalData, setModalData] = useState({});
const handleEdit = (row: any) => {
row.new_address = '';
setModalData(row);
setIsEditModalVisible(true);
};
const handleDelete = async (address: any) => {
await deleteAddress({ address: address });
tableRef.current?.reload();
};
const columns: ProColumns<any>[] = [
{
title: '收款地址',
dataIndex: 'address',
hideInSearch: true,
ellipsis: true,
},
{
title: '收款游戏币名称',
dataIndex: 'name',
hideInSearch: true,
},
{
title: '操作',
valueType: 'option',
width: 150,
render: (_, row) => [
<Access accessible={access.canShowButton('wallet_edit')}>
<a
key="edit"
onClick={() => {
handleEdit(row);
}}
>
</a>
</Access>,
<Access accessible={access.canShowButton('wallet_delete')}>
<DeleteButton
key="delete"
onDelete={() => {
handleDelete(row.address);
}}
/>
</Access>,
],
},
];
return (
<div>
<Table
columns={columns}
search={false}
rowKey="address"
toolBarActions={[
{
type: 'add',
permission: 'wallet_add',
onConfirm: () => {
setIsModalVisible(true);
},
},
]}
actionRef={tableRef}
request={async (params) => {
const res = await fetchTableData(getAddressList, params);
return res;
}}
/>
<AddWalletModal
visible={isModalVisible}
onCancel={function () {
setIsModalVisible(false);
}}
onOk={async function (val: any): Promise<void> {
await createAddress(val);
setIsModalVisible(false);
message.success('收款地址添加成功');
tableRef.current?.reload();
}}
/>
<EditWalletModal
visible={isEditModalVisible}
editModalData={modalData}
onCancel={function () {
setIsEditModalVisible(false);
}}
onOk={async function (val: any): Promise<void> {
await modifyAddress(val);
message.success('收款地址修改成功');
setIsEditModalVisible(false);
tableRef.current?.reload();
}}
/>
</div>
);
};
export default CollectionAddressList;

View File

@ -0,0 +1,108 @@
// 创建弹窗
import React, { useEffect, useRef, useState } from 'react';
import { createForm, onFieldReact, FormPathPattern, Field } from '@formily/core';
import { FormProvider, createSchemaField } from '@formily/react';
import { getCoinTypeList } from '@/services/recharge/coinType';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input, Select } from '@formily/antd';
import { action } from '@formily/reactive';
interface AddWalletModalPropsType extends ModalProps {
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
Select,
},
});
const AddWalletModal = ({ onOk, onCancel, ...rest }: AddWalletModalPropsType) => {
const [coinTypeData, setCoinTypeData] = useState({});
const getSelectData = async () => {
const list = await getCoinTypeList({ page: 1, size: 10 });
setCoinTypeData(list);
};
const UseAsyncDataSource = (
pattern: FormPathPattern,
service: (field: Field) => Promise<{ label: string; value: any }[]>,
) => {
onFieldReact(pattern, (field) => {
service(field).then(
action.bound((data) => {
field.dataSource = data;
}),
);
});
};
const form = createForm({
effects: () => {
UseAsyncDataSource('name', async (field) => {
const list = coinTypeData;
const option = [];
for (let index = 0; index < list.items.length; index++) {
const element = list.items[index];
const item = {
label: element.name,
value: element.name,
};
option.push(item);
}
return option;
});
},
});
useEffect(() => {
getSelectData();
}, []);
const handleOk = () => {
form.submit(async () => {
const formState = form.getFormState();
onOk(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="创建收款地址" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<FormProvider form={form}>
<SchemaField>
<SchemaField.String
name="name"
title="游戏币名称"
required
x-decorator="FormItem"
x-component="Select"
x-component-props={{
placeholder: '',
}}
/>
<SchemaField.String
name="address"
title="收款地址"
x-decorator="FormItem"
x-component="Input"
x-component-props={{
placeholder: '不传则后台生成',
}}
/>
</SchemaField>
</FormProvider>
{/* <Form form={form} labelCol={4} wrapperCol={18}> */}
{/* </Form> */}
</Modal>
);
};
export default AddWalletModal;

View File

@ -0,0 +1,102 @@
// 修改弹窗
import React, { useRef, useEffect, useState } from 'react';
import { createForm, onFieldReact, FormPathPattern, Field } from '@formily/core';
import { getCoinTypeList } from '@/services/recharge/coinType';
import { FormProvider, createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input, Select } from '@formily/antd';
import { action } from '@formily/reactive';
interface EditWalletModalPropsType extends ModalProps {
editModalData: any;
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
Select,
},
});
const EditWalletModal = ({ onOk, onCancel, editModalData, ...rest }: EditWalletModalPropsType) => {
const UseAsyncDataSource = (
pattern: FormPathPattern,
service: (field: Field) => Promise<{ label: string; value: any }[]>,
) => {
onFieldReact(pattern, (field) => {
service(field).then(
action.bound((data) => {
field.dataSource = data;
}),
);
});
};
const form = createForm({
effects: () => {
UseAsyncDataSource('name', async (field) => {
const list = await getCoinTypeList({ page: 1, size: 10 });
const option = [];
for (let index = 0; index < list.items.length; index++) {
const element = list.items[index];
const item = {
label: element.name,
value: element.name,
};
option.push(item);
}
return option;
});
},
});
useEffect(() => {
form.setInitialValues(editModalData);
});
const handleOk = () => {
form.submit(async () => {
const formState = form.getFormState();
onOk(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="修改收款地址" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="address"
title="旧收款地址"
x-disabled
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.String
name="name"
title="游戏币名称"
required
x-decorator="FormItem"
x-component="Select"
/>
<SchemaField.String
name="new_address"
title="新收款地址"
required
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default EditWalletModal;

View File

@ -0,0 +1,134 @@
import React, { useRef } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { getWithdrawList, solveWithdraw } from '@/services/recharge/withdraw';
import { fetchTableData } from '@/utils/table';
import ConfirmButton from '@/components/Table/ConfirmButton';
import moment from 'moment';
import { WithdrawType } from '@/constants/enum/withdraw';
import RejectButton from '@/components/Table/RejectButton';
import { Access, useAccess } from 'umi';
const WithdrawList = () => {
const tableRef = useRef<ActionType>();
const access = useAccess();
const handleClick = async (uuid, status) => {
await solveWithdraw({ uuid: uuid, status: status });
tableRef.current?.reload();
};
const WithdrawTypeList = [
{
label: '成功',
value: WithdrawType.SUCCESS,
status: 'Success',
},
{
label: '失败',
value: WithdrawType.FAILED,
status: 'Error',
},
{
label: '待处理',
value: WithdrawType.PENDDING,
status: 'Warning',
},
];
const columns: ProColumns<any>[] = [
{
title: '提现订单号',
dataIndex: 'uuid',
ellipsis: true,
},
{
title: '金额',
dataIndex: 'price',
width: '10%',
hideInSearch: true,
},
{
title: '接收地址',
dataIndex: 'to_address',
hideInSearch: true,
ellipsis: true,
},
{
title: '类型',
dataIndex: 'type',
hideInSearch: true,
width: '10%',
},
{
title: '状态',
dataIndex: 'status',
width: '10%',
valueEnum: () => {
const options = {};
WithdrawTypeList.forEach((item) => {
options[item.value] = { text: item.label, status: item.status };
});
return options;
},
},
{
title: '时间',
dataIndex: 'time',
valueType: 'dateRange',
ellipsis: true,
},
{
title: '操作',
valueType: 'option',
width: 150,
render: (_, row) => [
row.status === 30000 ? (
<Access accessible={access.canShowButton('withdraw_pass')}>
<ConfirmButton
key="confirm"
title="确认通过提现?"
buttonName="通过"
onConfirm={() => {
handleClick(row.uuid, WithdrawType.SUCCESS);
}}
/>
</Access>
) : null,
row.status === 30000 ? (
<Access accessible={access.canShowButton('withdraw_refuse')}>
<RejectButton
key="confirm"
title="确认拒绝提现?"
buttonName="拒绝"
onConfirm={() => {
handleClick(row.uuid, WithdrawType.FAILED);
}}
/>
</Access>
) : null,
],
},
];
return (
<div>
<Table
columns={columns}
rowKey="uuid"
actionRef={tableRef}
request={async (params) => {
params.status = params.status == null ? 0 : parseInt(params.status);
const res = await fetchTableData(getWithdrawList, params);
for (const key in res.data) {
if (Object.prototype.hasOwnProperty.call(res.data, key)) {
const element = res.data[key];
element.time = moment(element.time * 1000).format('YYYY-MM-DD HH:mm:ss');
}
}
return res;
}}
/>
</div>
);
};
export default WithdrawList;

View File

@ -0,0 +1,146 @@
import React, { useState, useRef } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import {
getAccountList,
deleteUser,
updateUser,
changeUserStatus,
} from '@/services/system/accountManage';
import { fetchTableData } from '@/utils/table';
import DeleteButton from '@/components/Table/DeleteButton';
import { Popover, Switch } from 'antd';
import AddAccountModal from '../components/AddAccountModal';
import EditAccountModal from '../components/EditAccountModal';
import { getRoleList } from '@/services/system/role';
import { Access, useAccess } from 'umi';
const AccountManageList = () => {
const [isModalVisible, setIsModalVisible] = useState(false);
const [isEditModal, setIsEditModal] = useState(false);
const [modalData, setModalData] = useState({});
const handleEdit = (row: any) => {
setModalData(row);
setIsEditModal(true);
};
const handleDelete = async (name: any) => {
await deleteUser({ name: name });
};
const tableRef = useRef<ActionType>();
const access = useAccess();
const columns: ProColumns<any>[] = [
{
title: '账号',
dataIndex: 'name',
hideInSearch: true,
ellipsis: true,
},
{
title: '角色',
dataIndex: 'role',
hideInSearch: true,
valueType: 'select',
request: async () => {
const res = await getRoleList({ page: 1, size: 10 });
const options = new Array();
(res as any).items.forEach((element) => {
options.push({ label: element.role_name, value: element.id });
});
return options;
},
},
{
title: '操作',
valueType: 'option',
width: 180,
render: (_, row) => [
<Access accessible={access.canShowButton('account_edit')}>
<a
key="edit"
placeholder=""
onClick={() => {
handleEdit(row);
}}
>
</a>
</Access>,
<Access accessible={access.canShowButton('account_disabled')}>
<Popover
content={
<div>
<p>/</p>
</div>
}
>
<Switch
key="switch"
defaultChecked={row.status}
size="small"
onChange={async (checked) => {
await changeUserStatus({ name: row.name, status: checked });
}}
/>
</Popover>
</Access>,
<Access accessible={access.canShowButton('account_delete')}>
<DeleteButton
key="delete"
onDelete={() => {
handleDelete(row.name);
tableRef.current?.reload();
}}
/>
</Access>,
],
},
];
return (
<div>
<Table
columns={columns}
rowKey="name"
search={false}
toolBarActions={[
{
type: 'add',
permission: 'account_add',
onConfirm: () => {
setIsModalVisible(true);
},
},
]}
actionRef={tableRef}
request={async (params) => {
const res = await fetchTableData(getAccountList, params);
return res;
}}
/>
<AddAccountModal
visible={isModalVisible}
onCancel={function () {
setIsModalVisible(false);
}}
onOk={async function (): Promise<void> {
setIsModalVisible(false);
tableRef.current?.reload();
}}
/>
<EditAccountModal
visible={isEditModal}
editModalData={modalData}
onCancel={function () {
setIsEditModal(false);
}}
onOk={async function (): Promise<void> {
setIsEditModal(false);
tableRef.current?.reload();
}}
/>
</div>
);
};
export default AccountManageList;

View File

@ -0,0 +1,123 @@
// 创建弹窗
import React, { useEffect, useRef, useState } from 'react';
import { createForm, Field, FormPathPattern, onFieldReact } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input, Select } from '@formily/antd';
import { addUser } from '@/services/system/accountManage';
import { action } from '@formily/reactive';
import { getRoleList } from '@/services/system/role';
interface AddAccountModalPropsType extends ModalProps {
onOk: () => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
Select,
},
});
const AddAccountModal = ({ onOk, onCancel, ...rest }: AddAccountModalPropsType) => {
const [roleListData, setRoleListData] = useState({});
const UseAsyncDataSource = (
pattern: FormPathPattern,
service: (field: Field) => Promise<{ label: string; value: any }[]>,
) => {
onFieldReact(pattern, (field) => {
service(field).then(
action.bound((data) => {
field.dataSource = data;
}),
);
});
};
const form = createForm({
effects: () => {
UseAsyncDataSource('role', async (field) => {
const list = roleListData as any;
const option = [] as any;
for (let index = 0; index < list.items.length; index++) {
const element = list.items[index];
const item = {
label: element.role_name,
value: element.id,
};
option.push(item);
}
return option;
});
},
});
const getSelectData = async () => {
const list = await getRoleList({ page: 1, size: 10 });
setRoleListData(list);
};
useEffect(() => {
getSelectData();
}, []);
const handleOk = async () => {
form.submit(async () => {
onOk();
const formState = form.getFormState();
await addUser(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="添加管理账号" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="name"
title="账号名"
required
x-decorator="FormItem"
x-component="Input"
x-component-props={{
placeholder: '请输入账号名',
}}
/>
<SchemaField.String
name="password"
title="密码"
required
x-validator={{
required: true,
}}
x-decorator="FormItem"
x-component="Input"
x-component-props={{
placeholder: '请输入密码',
}}
/>
<SchemaField.String
name="role"
title="角色"
required
x-decorator="FormItem"
x-component="Select"
x-component-props={{
mode: 'multiple',
placeholder: '请选择角色',
}}
/>
</SchemaField>
</Form>
</Modal>
);
};
export default AddAccountModal;

View File

@ -0,0 +1,126 @@
// 创建弹窗
import React, { useRef, useEffect, useState } from 'react';
import { createForm, FormPathPattern, onFieldReact } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input, Select } from '@formily/antd';
import { updateUser } from '@/services/system/accountManage';
import { getRoleList } from '@/services/system/role';
import { action } from '@formily/reactive';
interface EditAccountModalPropsType extends ModalProps {
editModalData: any;
onCancel: () => void;
onOk: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
Select,
},
});
const EditAccountModal = ({
onOk,
onCancel,
editModalData,
...rest
}: EditAccountModalPropsType) => {
// const [roleListData, setRoleListData] = useState({});
// const getSelectData = async () => {
// const list =
// setRoleListData(list);
// };
const UseAsyncDataSource = (
pattern: FormPathPattern,
service: (field: Field) => Promise<{ label: string; value: any }[]>,
) => {
onFieldReact(pattern, (field) => {
service(field).then(
action.bound((data) => {
field.dataSource = data;
}),
);
});
};
const form = createForm({
effects: () => {
UseAsyncDataSource('role', async (field) => {
const list = await getRoleList({ page: 1, size: 10 });
const option = [];
for (let index = 0; index < list.items.length; index++) {
const element = list.items[index];
const item = {
label: element.role_name,
value: element.id,
};
option.push(item);
}
return option;
});
},
});
useEffect(() => {
form.setInitialValues(editModalData);
});
const handleOk = async () => {
form.submit(async () => {
onOk();
const formState = form.getFormState();
await updateUser(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="编辑管理账号" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="name"
title="账号名"
required
x-disabled
x-decorator="FormItem"
x-component="Input"
x-component-props={{
placeholder: '请输入账号名',
}}
/>
<SchemaField.String
name="password"
title="密码"
required
x-decorator="FormItem"
x-component="Input"
x-component-props={{
placeholder: '请输入密码',
}}
/>
<SchemaField.String
name="role"
title="角色"
required
x-decorator="FormItem"
x-component="Select"
x-component-props={{
placeholder: '请选择角色',
}}
/>
</SchemaField>
</Form>
</Modal>
);
};
export default EditAccountModal;

View File

@ -0,0 +1,166 @@
import React, { useState, useRef } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { getNoticeList, createNotice, updateNotice, deleteNotice } from '@/services/system/notice';
import { fetchTableData } from '@/utils/table';
import DeleteButton from '@/components/Table/DeleteButton';
import { Popover, Switch } from 'antd';
import AddNoticeModal from '../components/AddNoticeModal';
import EditNoticeModal from '../components/EditNoticeModal';
import { NoticeType } from '@/constants/enum/notice';
import { Access, useAccess } from 'umi';
const NoticeList = () => {
const tableRef = useRef<ActionType>();
const access = useAccess();
const [isModalVisible, setIsModalVisible] = useState(false);
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
const [modalData, setModalData] = useState({});
const handleEdit = (row: any) => {
setModalData(row);
setIsEditModalVisible(true);
tableRef.current?.reload();
};
const handleDelete = async (code: any) => {
await deleteNotice({ code: code });
tableRef.current?.reload();
};
const noticeTypeList = [
{
label: '充值',
value: NoticeType.RECHARGE,
},
{
label: '提现',
value: NoticeType.WITHDRAW,
},
{
label: 'NFT创建',
value: NoticeType.NFTCREATE,
},
{
label: 'NFT转移',
value: NoticeType.NFTTRANSFER,
},
{
label: 'NFT销毁',
value: NoticeType.NFTTDISPOSE,
},
];
const columns: ProColumns<any>[] = [
{
title: '通知码',
dataIndex: 'code',
valueEnum: () => {
const options = {};
noticeTypeList.forEach((item) => {
options[item.value] = item.label;
});
return options;
},
},
{
title: '通知地址',
dataIndex: 'addr',
hideInSearch: true,
ellipsis: true,
},
{
title: '操作',
valueType: 'option',
width: 180,
render: (_, row) => [
<Access accessible={access.canShowButton('notice_edit')}>
<a
key="edit"
onClick={() => {
handleEdit(row);
}}
>
</a>
</Access>,
<Access accessible={access.canShowButton('notice_disabled')}>
<Popover
content={
<div>
<p>/</p>
</div>
}
>
<Switch
key="switch"
defaultChecked={row.status}
size="small"
onChange={async (checked) => {
row.status = checked;
await updateNotice(row);
}}
/>
,
</Popover>
</Access>,
<Access accessible={access.canShowButton('notice_delete')}>
<DeleteButton
key="delete"
onDelete={() => {
handleDelete(row.code);
}}
/>
</Access>,
],
},
];
return (
<div>
<Table
columns={columns}
rowKey="code"
search={false}
toolBarActions={[
{
type: 'add',
permission: 'notice_add',
onConfirm: () => {
setIsModalVisible(true);
},
},
]}
actionRef={tableRef}
request={async (params) => {
const res = await fetchTableData(getNoticeList, params);
return res;
}}
/>
<AddNoticeModal
visible={isModalVisible}
onCancel={function () {
setIsModalVisible(false);
}}
onOk={async function (val: any): Promise<void> {
await createNotice(val);
setIsModalVisible(false);
tableRef.current?.reload();
}}
/>
<EditNoticeModal
visible={isEditModalVisible}
editModalData={modalData}
onCancel={function () {
setIsEditModalVisible(false);
}}
onOk={async function (val: any): Promise<void> {
await updateNotice(val);
setIsEditModalVisible(false);
tableRef.current?.reload();
}}
/>
</div>
);
};
export default NoticeList;

View File

@ -0,0 +1,99 @@
// 添加代币类型弹窗
import React, { useRef } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input, Select } from '@formily/antd';
import { NoticeType } from '@/constants/enum/notice';
const noticeTypeList = [
{
label: '充值',
value: NoticeType.RECHARGE,
},
{
label: '提现',
value: NoticeType.WITHDRAW,
},
{
label: 'NFT创建',
value: NoticeType.NFTCREATE,
},
{
label: 'NFT转移',
value: NoticeType.NFTTRANSFER,
},
{
label: 'NFT销毁',
value: NoticeType.NFTTDISPOSE,
},
];
interface AddNoticeModalPropsType extends ModalProps {
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
Select,
},
});
const form = createForm({});
const AddNoticeModal = ({ onOk, onCancel, ...rest }: AddNoticeModalPropsType) => {
const handleOk = () => {
form.submit(async () => {
const formState = form.getFormState();
formState.values.code = parseInt(formState.values.code);
onOk(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="添加通知" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.Number
name="code"
title="通知码"
required
x-component-props={{
options: noticeTypeList,
}}
x-decorator="FormItem"
x-component="Select"
/>
<SchemaField.String
name="addr"
title="通知地址"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.Boolean
name="status"
title="状态"
required
x-decorator="FormItem"
x-component="Select"
enum={[
{ label: '启用', value: true },
{ label: '禁用', value: false },
]}
/>
</SchemaField>
</Form>
</Modal>
);
};
export default AddNoticeModal;

View File

@ -0,0 +1,92 @@
// 修改弹窗
import React, { useRef, useEffect } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input, Select } from '@formily/antd';
import { NoticeType } from '@/constants/enum/notice';
interface EditNoticeModalPropsType extends ModalProps {
editModalData: any;
onOk: (val: any) => void;
onCancel: () => void;
}
const noticeTypeList = [
{
label: '充值',
value: NoticeType.RECHARGE,
},
{
label: '提现',
value: NoticeType.WITHDRAW,
},
{
label: 'NFT创建',
value: NoticeType.NFTCREATE,
},
{
label: 'NFT转移',
value: NoticeType.NFTTRANSFER,
},
{
label: 'NFT销毁',
value: NoticeType.NFTTDISPOSE,
},
];
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
Select,
},
});
const form = createForm({});
const EditNoticeModal = ({ onOk, onCancel, editModalData, ...rest }: EditNoticeModalPropsType) => {
useEffect(() => {
form.setInitialValues(editModalData);
});
const handleOk = () => {
form.submit(async () => {
const formState = form.getFormState();
onOk(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="修改通知" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.Number
name="code"
title="通知码"
required
x-component-props={{
options: noticeTypeList,
}}
x-decorator="FormItem"
x-component="Select"
x-disabled
/>
<SchemaField.String
name="addr"
title="通知地址"
required
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default EditNoticeModal;

View File

@ -0,0 +1,246 @@
import React, { useState, useRef } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import routes from '@/routes';
import DeleteButton from '@/components/Table/DeleteButton';
import { getPermission, upsertPermission } from '@/services/system/permission';
import AddPermissionModal from '../components/AddPermissionModal';
import EditPermissionModal from '../components/EditPermissionModal';
import { message } from 'antd';
import { Access, useAccess } from 'umi';
const PermissionsList = () => {
const [isModalVisible, setIsModalVisible] = useState(false);
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
const [permissionsData, setPermissionsData] = useState(Array);
const [isAddFirst, setIsAddFirst] = useState(false);
const [modalData, setModalData] = useState({});
const [rowData, setRowData] = useState({} as any);
const tableRef = useRef<ActionType>();
const access = useAccess();
const handleAdd = (row: any) => {
setRowData(row);
setIsModalVisible(true);
};
const handleEdit = (row: any) => {
setRowData(row);
setIsEditModalVisible(true);
};
const deleteLoopChildren = (rowData, children) => {
for (let index = 0; index < children.length; index++) {
const childrenElement = children[index];
if (rowData.code == childrenElement.code) {
if (children.length == 1) {
return 1;
}
children.splice(index, 1);
return true;
} else {
if (Object.prototype.hasOwnProperty.call(childrenElement, 'children')) {
const childrenLength = deleteLoopChildren(rowData, childrenElement.children);
if (childrenLength == 1) {
delete childrenElement.children;
}
} else {
continue;
}
}
}
};
const handleDelete = async (row: any) => {
for (let index = 0; index < permissionsData.length; index++) {
const element = permissionsData[index];
if (row.code == (element as any).code) {
permissionsData.splice(index, 1);
break;
} else {
const result = deleteLoopChildren(row, (element as any).children);
if (result) {
break;
}
}
}
await upsertPermission(permissionsData);
message.success('删除权限成功');
tableRef.current?.reload();
};
const addLoopChildren = (rowData, children) => {
for (let index = 0; index < children.length; index++) {
let childrenElement = children[index];
if (rowData.code == childrenElement.code) {
if (Object.prototype.hasOwnProperty.call(childrenElement, 'children')) {
childrenElement = rowData;
} else {
childrenElement.children = rowData;
}
return true;
} else {
if (Object.prototype.hasOwnProperty.call(childrenElement, 'children')) {
addLoopChildren(rowData, childrenElement.children);
} else {
continue;
}
}
}
};
const editLoopChildren = (val, rowData, children) => {
for (let index = 0; index < children.length; index++) {
const childrenElement = children[index];
if (rowData.code == childrenElement.code) {
childrenElement.code = val.code;
return true;
} else {
if (Object.prototype.hasOwnProperty.call(childrenElement, 'children')) {
editLoopChildren(val, rowData, childrenElement.children);
} else {
continue;
}
}
}
};
const columns: ProColumns<any>[] = [
{
title: '权限名',
dataIndex: 'name',
},
{
title: '操作',
valueType: 'option',
width: 180,
render: (_, row) => [
<Access accessible={access.canShowButton('permission_add')}>
<a
key="add"
onClick={() => {
setIsAddFirst(false);
handleAdd(row);
}}
>
</a>
</Access>,
<Access accessible={access.canShowButton('permission_edit')}>
<a
key="edit"
onClick={() => {
setModalData(row);
handleEdit(row);
}}
>
</a>
</Access>,
Object.prototype.hasOwnProperty.call(row, 'children') ? null : (
<Access accessible={access.canShowButton('permission_delete')}>
<DeleteButton
key="delete"
onDelete={() => {
handleDelete(row);
}}
/>
</Access>
),
],
},
];
return (
<div>
<Table
columns={columns}
search={false}
rowKey="name"
actionRef={tableRef}
toolBarActions={[
{
type: 'add',
permission: 'permission_add',
onConfirm: () => {
setIsAddFirst(true);
setIsModalVisible(true);
},
},
]}
request={async (params) => {
const res = await getPermission({});
const data = JSON.parse((res as any).items);
setPermissionsData(data);
return { data: data };
}}
/>
<AddPermissionModal
visible={isModalVisible}
onCancel={function () {
setIsModalVisible(false);
}}
onOk={async function (val) {
if (isAddFirst) {
permissionsData.push(val);
} else {
if (Object.prototype.hasOwnProperty.call(rowData, 'children')) {
const result = rowData.children.find(function (value, index, arr) {
return value.code === val.code;
});
if (!result) {
rowData.children.push(val);
} else {
message.warning('已有改权限名');
}
} else {
rowData.children = [val];
}
for (let index = 0; index < permissionsData.length; index++) {
const element = permissionsData[index] as any;
if (rowData.code == element.code) {
permissionsData[index] = rowData;
break;
} else {
const result = addLoopChildren(rowData, element.children);
if (result) {
break;
}
}
}
}
await upsertPermission(permissionsData);
message.success('添加权限成功');
setIsModalVisible(false);
tableRef.current?.reload();
}}
/>
<EditPermissionModal
visible={isEditModalVisible}
editModalData={modalData}
onCancel={function () {
setIsEditModalVisible(false);
}}
onOk={async function (val) {
for (let index = 0; index < permissionsData.length; index++) {
const element = permissionsData[index] as any;
if (rowData.code == element.code) {
(permissionsData[index] as any).code = val.code;
break;
} else {
const result = editLoopChildren(val, rowData, element.children);
if (result) {
break;
}
}
}
await upsertPermission(permissionsData);
setIsEditModalVisible(false);
message.success('编辑权限成功');
tableRef.current?.reload();
}}
/>
</div>
);
};
export default PermissionsList;

View File

@ -0,0 +1,58 @@
// 创建弹窗
import React, { useRef } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input } from '@formily/antd';
interface AddPermissionModalPropsType extends ModalProps {
onCancel: () => void;
onOk: (val: any) => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
},
});
const form = createForm({});
const AddPermissionModal = ({ onOk, onCancel, ...rest }: AddPermissionModalPropsType) => {
const handleOk = async () => {
form.submit(async () => {
const formState = form.getFormState();
onOk(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="添加权限" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="name"
title="权限名"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.String
name="code"
title="权限码"
required
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default AddPermissionModal;

View File

@ -0,0 +1,68 @@
// 创建弹窗
import React, { useEffect, useRef } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input } from '@formily/antd';
interface EditPermissionModalPropsType extends ModalProps {
editModalData: any;
onCancel: () => void;
onOk: (val: any) => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
},
});
const form = createForm({});
const EditPermissionModal = ({
onOk,
onCancel,
editModalData,
...rest
}: EditPermissionModalPropsType) => {
useEffect(() => {
form.setInitialValues(editModalData);
});
const handleOk = async () => {
form.submit(async () => {
const formState = form.getFormState();
onOk(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="编辑权限" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="name"
title="权限名"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.String
name="code"
title="权限码"
required
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default EditPermissionModal;

View File

@ -0,0 +1,143 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { getRoleList, deleteRole, createRole, updateRole } from '@/services/system/role';
import { fetchTableData } from '@/utils/table';
import DeleteButton from '@/components/Table/DeleteButton';
import AddRoleModal from '../components/AddRoleModal';
import EditRoleModal from '../components/EditRoleModal';
import AuthPermissionsDrawer from '../components/AuthPermissionsDrawer';
import { Access, useAccess } from 'umi';
const RoleList = () => {
const tableRef = useRef<ActionType>();
const access = useAccess();
const [isDrawerVisible, setIsDrawerVisible] = useState(false);
const [isModalVisible, setIsModalVisible] = useState(false);
const [isEditModal, setIsEditModal] = useState(false);
const [modalData, setModalData] = useState({});
const [rowData, setRowData] = useState({});
const handleEdit = (row: any) => {
setModalData(row);
setIsEditModal(true);
};
const handleAuth = (row: any) => {
setRowData(row);
setIsDrawerVisible(true);
};
const handleDelete = async (id: any) => {
await deleteRole({ id: id });
tableRef.current?.reload();
};
const columns: ProColumns<any>[] = [
{
title: '角色名称',
dataIndex: 'role_name',
hideInSearch: true,
ellipsis: true,
},
{
title: '备注',
dataIndex: 'remark',
hideInSearch: true,
ellipsis: true,
},
{
title: '操作',
valueType: 'option',
width: 180,
render: (_, row) => [
<Access accessible={access.canShowButton('role_edit')}>
<a
key="edit"
onClick={() => {
handleEdit(row);
}}
>
</a>
</Access>,
<Access accessible={access.canShowButton('role_authorization')}>
<a
key="auth"
onClick={() => {
handleAuth(row);
}}
>
</a>
</Access>,
<Access accessible={access.canShowButton('role_delete')}>
<DeleteButton
key="delete"
onDelete={() => {
handleDelete(row.id);
}}
/>
</Access>,
],
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
search={false}
toolBarActions={[
{
type: 'add',
permission: 'role_add',
onConfirm: () => {
setIsModalVisible(true);
},
},
]}
actionRef={tableRef}
request={async (params) => {
const res = await fetchTableData(getRoleList, params);
return res;
}}
/>
<AddRoleModal
visible={isModalVisible}
onCancel={function () {
setIsModalVisible(false);
}}
onOk={async function (val): Promise<void> {
await createRole(val);
setIsModalVisible(false);
tableRef.current?.reload();
}}
/>
<EditRoleModal
visible={isEditModal}
editModalData={modalData}
onCancel={function () {
setIsEditModal(false);
}}
onOk={async function (val): Promise<void> {
await updateRole(val);
setIsEditModal(false);
tableRef.current?.reload();
}}
/>
<AuthPermissionsDrawer
visible={isDrawerVisible}
rowData={rowData}
onCancel={function () {
setIsDrawerVisible(false);
}}
onOk={async function (val) {
await updateRole(val);
setIsDrawerVisible(false);
}}
/>
</div>
);
};
export default RoleList;

View File

@ -0,0 +1,58 @@
// 创建弹窗
import React, { useRef } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input } from '@formily/antd';
interface AddRoleModalPropsType extends ModalProps {
onCancel: () => void;
onOk: (val: any) => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
},
});
const form = createForm({});
const AddRoleModal = ({ onOk, onCancel, ...rest }: AddRoleModalPropsType) => {
const handleOk = async () => {
form.submit(async () => {
const formState = form.getFormState();
onOk(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="添加角色" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="role_name"
title="角色名"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.String
name="remark"
title="备注"
required
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default AddRoleModal;

View File

@ -0,0 +1,164 @@
// 创建弹窗
import React, { useEffect, useRef, useState } from 'react';
import { Button, Drawer, DrawerProps, message, Space } from 'antd';
import { ActionType, ProColumns } from '@ant-design/pro-table';
import Table from '@/components/Table';
import { TableRowSelection } from 'antd/lib/table/interface';
import { getPermission } from '@/services/system/permission';
import { useModel } from '@/.umi/plugin-model/useModel';
interface AuthPermissionsDrawerPropsType extends DrawerProps {
onCancel: () => void;
onOk: (val: any) => void;
rowData: any;
}
const divStyle: React.CSSProperties = {
padding: 0,
};
const columnsPermissions: ProColumns<any>[] = [
{
title: '权限名',
dataIndex: 'name',
},
];
const valueMap = new Map();
const loops = (list, parent) => {
return (list || []).map(({ children, name, code }) => {
const node = (valueMap[code] = {
parent,
name,
code,
}) as any;
node.children = loops(children, node);
return node;
});
};
const getPath = (code) => {
const path = [] as any;
let current = valueMap[code];
while (current) {
path.unshift(current.code);
current = current.parent;
}
return path;
};
const mergeArray = (arr1, arr2) => {
const _arr = new Array();
for (let i = 0; i < arr1.length; i++) {
_arr.push(arr1[i]);
}
for (let i = 0; i < arr2.length; i++) {
let flag = true;
for (let j = 0; j < arr1.length; j++) {
if (arr2[i] == arr1[j]) {
flag = false;
break;
}
}
if (flag) {
_arr.push(arr2[i]);
}
}
return _arr;
};
const AuthPermissionsDrawer = ({
onOk,
onCancel,
rowData,
...rest
}: AuthPermissionsDrawerPropsType) => {
const tableRef = useRef<ActionType>();
const [selectedKeys, setSelectedKeys] = useState(new Array());
const [tempValue, setTempValue] = useState(new Map());
const rowSelection: TableRowSelection<DataType> = {
onChange: (selectedRowKeys, selectedRows) => {
console.log('selectedRowKeys = ', selectedRowKeys);
setSelectedKeys(selectedRowKeys);
},
};
const handleOk = () => {
let postSelectedKeys = new Array();
selectedKeys.forEach((element) => {
const path = getPath(element);
postSelectedKeys = mergeArray(postSelectedKeys, path);
});
if (postSelectedKeys.length == 0) {
message.warning('请选择权限。');
return;
}
onOk({ id: rowData.id, permission: postSelectedKeys });
};
const handleCancel = () => {
onCancel();
};
useEffect(() => {
const parentArray = new Array();
let newPermission = new Array();
for (const key in tempValue) {
if (Object.prototype.hasOwnProperty.call(tempValue, key)) {
const element = tempValue[key];
if (element.children.length != 0) {
parentArray.push(element.code);
}
}
}
if (
rowData.permission != undefined &&
rowData.permission.length != 0 &&
rowData.permission != null
) {
newPermission = rowData.permission.filter((item) => !parentArray.includes(item));
}
setSelectedKeys(newPermission);
}, [rowData.permission, tempValue]);
return (
<Drawer
onClose={handleCancel}
width={600}
{...rest}
bodyStyle={divStyle}
extra={
<Space>
<Button onClick={handleCancel}></Button>
<Button type="primary" onClick={handleOk}>
</Button>
</Space>
}
>
<Table
columns={columnsPermissions}
rowKey="code"
key="code"
search={false}
pagination={false}
rowSelection={{
...rowSelection,
hideSelectAll: true,
checkStrictly: false,
selectedRowKeys: selectedKeys,
}}
actionRef={tableRef}
request={async (params) => {
const res = (await getPermission({})) as any;
const treeData = JSON.parse(res.items);
loops(treeData, null);
setTempValue(valueMap);
return { data: treeData };
}}
/>
</Drawer>
);
};
export default AuthPermissionsDrawer;

View File

@ -0,0 +1,71 @@
// 创建弹窗
import React, { useRef, useEffect } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
import { Form, FormItem, Input, Select } from '@formily/antd';
import { addUser, updateUser } from '@/services/system/accountManage';
interface EditRoleModalPropsType extends ModalProps {
editModalData: any;
onCancel: () => void;
onOk: (val: any) => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
Select,
},
});
const form = createForm({});
const EditRoleModal = ({ onOk, onCancel, editModalData, ...rest }: EditRoleModalPropsType) => {
useEffect(() => {
form.setInitialValues(editModalData);
});
const handleOk = async () => {
form.submit(async () => {
const formState = form.getFormState();
onOk(formState.values);
});
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="编辑角色" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="role_name"
title="角色名"
required
x-decorator="FormItem"
x-component="Input"
x-component-props={{
placeholder: '请输入账号名',
}}
/>
<SchemaField.String
name="remark"
title="备注"
required
x-decorator="FormItem"
x-component="Input"
x-component-props={{
placeholder: '请输入密码',
}}
/>
</SchemaField>
</Form>
</Modal>
);
};
export default EditRoleModal;

View File

@ -0,0 +1,24 @@
@import '~antd/es/style/themes/default.less';
.container {
display: flex;
flex-direction: column;
height: 100vh;
padding: 20px;
overflow: auto;
background: white;
:global {
.ant-pro-form-login-header {
margin-bottom: 40px;
}
}
}
.button {
width: 150px;
}
.textarea {
width: 600px;
margin-top: 20px;
}

View File

@ -0,0 +1,83 @@
import React, { useState, useEffect, ChangeEvent } from 'react';
import { Button, Input, message, Popconfirm } from 'antd';
import styles from './index.less';
import { createAppID, createSecretKey, getAppID, getSecretKey } from '@/services/system/secretKey';
import { Access, useAccess } from 'umi';
const { TextArea } = Input;
const SecretKey: React.FC = () => {
const access = useAccess();
const [secretKey, setSecretKey] = useState('');
const [appIDString, setAppIDString] = useState('');
const create = async () => {
const data = await createSecretKey({});
message.success('密钥创建成功');
setSecretKey(data + '');
};
const appIDCreate = async () => {
if (appIDString == '') {
message.warning('请填写AppID');
return;
}
await createAppID({ appid: appIDString });
message.success('AppID创建成功');
};
const getAppIDString = async () => {
const data = await getAppID();
setAppIDString(data + '');
};
const getUserSecretKey = async () => {
const data = await getSecretKey();
setSecretKey(data + '');
};
useEffect(() => {
getAppIDString();
getUserSecretKey();
}, [secretKey]);
return (
<div className={styles.container}>
<Access accessible={access.canShowButton('secret_create')}>
<Popconfirm title="确定创建密钥?" onConfirm={create} okText="确定" cancelText="取消">
<Button className={styles.button} type="primary">
</Button>
</Popconfirm>
</Access>
<TextArea className={styles.textarea} rows={16} value={secretKey} />
<br />
<br />
<Input.Group compact>
<Input
style={{ width: 'calc(50% - 160px)' }}
value={appIDString}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
setAppIDString(event.target.value);
}}
placeholder="请输入AppID"
/>
<Access accessible={access.canShowButton('AppID_create')}>
<Popconfirm
title="确定创建AppID?"
onConfirm={appIDCreate}
okText="确定"
cancelText="取消"
>
<Button className={styles.button} type="primary">
AppID
</Button>
</Popconfirm>
</Access>
</Input.Group>
</div>
);
};
export default SecretKey;

View File

@ -0,0 +1,66 @@
import React, { useState, useRef } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { getUserList } from '@/services/user/user';
import { fetchTableData } from '@/utils/table';
import moment from 'moment';
const UserManageList = () => {
const tableRef = useRef<ActionType>();
const columns: ProColumns<any>[] = [
{
title: '钱包地址',
dataIndex: 'address',
ellipsis: true,
},
{
title: '注册时间',
dataIndex: 'create_time',
hideInSearch: true,
ellipsis: true,
},
{
title: '登陆时间',
dataIndex: 'last_time',
hideInSearch: true,
ellipsis: true,
},
];
return (
<Table
columns={columns}
rowKey="address"
actionRef={tableRef}
request={async (params) => {
const res = await fetchTableData(getUserList, params);
console.log('res = ', res);
for (const key in res.data) {
if (Object.prototype.hasOwnProperty.call(res.data, key)) {
const element = res.data[key];
if (
element.create_time != '' &&
element.create_time != undefined &&
element.create_time != null
) {
element.create_time = moment(element.create_time * 1000).format(
'YYYY-MM-DD HH:mm:ss',
);
}
if (
element.last_time != '' &&
element.last_time != undefined &&
element.last_time != null
) {
element.last_time = moment(element.last_time * 1000).format('YYYY-MM-DD HH:mm:ss');
}
}
}
if (res.data == null) {
return [];
}
return res;
}}
/>
);
};
export default UserManageList;

View File

@ -21,7 +21,7 @@
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
/>
<title>Pro</title>
<title>TBG</title>
<link rel="icon" href="<%= context.config.publicPath +'logo.png'%>" type="image/png" />
</head>
<body>

View File

@ -11,33 +11,172 @@ export default [
component: './Login',
},
{
name: '设置',
path: RoutePath.SETTING,
name: '充值系统',
path: RoutePath.RECHARGE,
code: 'recharge_system',
access: 'normalRouteFilter',
routes: [
{
path: RoutePath.SETTING,
redirect: RoutePath.WORK.LIST,
path: RoutePath.RECHARGE,
redirect: RoutePath.RECORD.LIST,
hideInMenu: true,
},
{
name: '作品',
path: RoutePath.WORK.LIST,
component: './Work/List',
name: '充值订单',
path: RoutePath.RECORD.LIST,
code: 'recharge_record',
access: 'normalRouteFilter',
component: './Recharge/Record/List',
},
{
name: '作品详情',
path: RoutePath.WORK.EDIT,
name: '收款地址',
path: RoutePath.WALLET.LIST,
code: 'wallet_address',
access: 'normalRouteFilter',
component: './Recharge/Wallet/List',
},
{
name: '代币种类',
path: RoutePath.COIN_TYPE.LIST,
code: 'coin_type',
access: 'normalRouteFilter',
component: './Recharge/CoinType/List',
},
{
name: '提现管理',
path: RoutePath.WITHDRAW.LIST,
code: 'withdraw_manage',
access: 'normalRouteFilter',
component: './Recharge/Withdraw/List',
},
],
},
{
name: '用户账号',
path: RoutePath.USER,
code: 'user_account',
access: 'normalRouteFilter',
routes: [
{
path: RoutePath.USER,
redirect: RoutePath.USER_LIST.LIST,
hideInMenu: true,
component: './Work/Edit',
},
{
name: '用户账号管理',
path: RoutePath.USER_LIST.LIST,
code: 'user_account_manage',
access: 'normalRouteFilter',
component: './User/List',
},
],
},
{
name: '系统设置',
path: RoutePath.SYSTEM,
code: 'system_setting',
access: 'normalRouteFilter',
routes: [
{
path: RoutePath.SYSTEM,
redirect: RoutePath.ACCOUNT.LIST,
hideInMenu: true,
},
{
name: '账号管理',
path: RoutePath.ACCOUNT.LIST,
code: 'account_manage',
access: 'normalRouteFilter',
component: './System/Account/List',
},
{
name: '角色管理',
path: RoutePath.ROLE.LIST,
code: 'role_manage',
access: 'normalRouteFilter',
component: './System/Role/List',
},
{
name: '权限管理',
path: RoutePath.PERMISSIONS.LIST,
code: 'permission_manage',
access: 'normalRouteFilter',
component: './System/Permissions/List',
},
{
name: '通知管理',
path: RoutePath.NOTICE.LIST,
code: 'notice_manage',
access: 'normalRouteFilter',
component: './System/Notice/List',
},
{
name: '密钥管理',
path: RoutePath.SECRET_KEY,
code: 'secret_manage',
access: 'normalRouteFilter',
component: './System/SecretKey',
},
],
},
{
name: '数据看板',
access: 'normalRouteFilter',
code: 'data_board',
path: RoutePath.DATABOARD,
routes: [
{
path: RoutePath.DATABOARD,
redirect: RoutePath.COREDATA.LIST,
hideInMenu: true,
},
{
name: '核心看板',
path: RoutePath.COREDATA.LIST,
code: 'core_ata_board',
access: 'normalRouteFilter',
component: './DataBoard/CoreData/List',
},
],
},
{
name: 'NFT',
path: RoutePath.NFT,
code: 'nft',
access: 'normalRouteFilter',
routes: [
{
path: RoutePath.NFT,
redirect: RoutePath.NFTCONTRACT.LIST,
hideInMenu: true,
},
{
name: 'NFT合约管理',
path: RoutePath.NFTCONTRACT.LIST,
code: 'nft_contract_manage',
access: 'normalRouteFilter',
component: './NFT/NftContract/List',
},
{
name: 'NFT管理',
path: RoutePath.NFTTOKEN.LIST,
code: 'nft_manage',
access: 'normalRouteFilter',
component: './NFT/NftToken/List',
},
{
name: 'NFT交易',
path: RoutePath.NFTTRADE.LIST,
code: 'nft_trade_manage',
access: 'normalRouteFilter',
component: './NFT/NftTrade/List',
},
],
},
{
path: '/',
redirect: RoutePath.WORK.LIST,
redirect: RoutePath.RECORD.LIST,
},
{
component: './404',
},

View File

@ -1,11 +1,62 @@
const SETTING = '/setting';
const RECHARGE = '/recharge';
const USER = '/user';
const SYSTEM = '/system';
const DATABOARD = '/databoard';
const NFT = '/nft';
const RoutePath = {
LOGIN: '/login',
SETTING: SETTING,
WORK: {
LIST: `${SETTING}/work`,
EDIT: `${SETTING}/work/edit`,
RECHARGE: RECHARGE,
RECORD: {
LIST: `${RECHARGE}/record`,
},
WALLET: {
LIST: `${RECHARGE}/wallet`,
},
COIN_TYPE: {
LIST: `${RECHARGE}/coin_type`,
},
WITHDRAW: {
LIST: `${RECHARGE}/withdraw`,
},
USER: USER,
USER_LIST: {
LIST: `${USER}/user`,
},
SYSTEM: SYSTEM,
ACCOUNT: {
LIST: `${SYSTEM}/account`,
},
ROLE: {
LIST: `${SYSTEM}/role`,
},
PERMISSIONS: {
LIST: `${SYSTEM}/permissions`,
},
NOTICE: {
LIST: `${SYSTEM}/notice`,
},
NFT: NFT,
NFTCONTRACT: {
LIST: `${NFT}/nftcontract`,
},
NFTTOKEN: {
LIST: `${NFT}/nfttoken`,
},
NFTTRADE: {
LIST: `${NFT}/NFT_trade`,
},
DATABOARD: DATABOARD,
COREDATA: {
LIST: `${DATABOARD}/coredata`,
},
SECRET_KEY: `${SYSTEM}/secret_key`,
// SETTING: SETTING,
// WORK: {
// LIST: `${SETTING}/work`,
// EDIT: `${SETTING}/work/edit`,
// },
};
export default RoutePath;

15
src/services/contract.ts Normal file
View File

@ -0,0 +1,15 @@
import request from '@/utils/request';
/**
*
* @param {object} params
* erc
* @returns {object} data
*/
export const getContractInfo = (data) => {
return request.request({
url: '/contract/get',
method: 'post',
data,
});
};

View File

@ -0,0 +1,41 @@
import request from '@/utils/request';
//获取基础数据报表
export const getActiveData = (data) => {
return request.request({
url: '/tgb/api/v1/data/get',
method: 'post',
data,
});
};
// 获取今日登陆人数接口
export const getTodayUserData = (params) => {
return request.request({
url: '/tgb/api/v1/data/user/today',
method: 'get',
params,
});
};
//获取本日新用户人数
export const getMonthUserData = (params) => {
return request.request({
url: '/tgb/api/v1/data/user/month',
method: 'get',
params,
});
};
// 获取本周登录人数
export const getWeekUserData = (params) => {
return request.request({
url: '/tgb/api/v1/data/user/week',
method: 'get',
params,
});
};
// 获取本日老用户登录人数
export const getOldUserData = (params) => {
return request.request({
url: '/tgb/api/v1/data/user/old',
method: 'get',
params,
});
};

View File

@ -0,0 +1,18 @@
import request from '@/utils/request';
// 获取核心列表数据
export const getCoreData = (data) => {
return request.request({
url: '/coredata/get',
method: 'post',
data,
});
};
// 数据库列表
export const getListData = (data) => {
return request.request({
url: '/data/get',
method: 'post',
data,
});
};

View File

@ -0,0 +1,9 @@
import request from '@/utils/request';
//获取基础数据报表
export const getRententionData = (data) => {
return request.request({
url: '/tgb/api/v1/data/get',
method: 'post',
data,
});
};

View File

@ -0,0 +1,17 @@
import request from '@/utils/request';
//获取基础数据报表
export const getRententionData = (data) => {
return request.request({
url: '/tgb/api/v1/data/get',
method: 'post',
data,
});
};
//获取渠道
export const getChannelData = (data) => {
return request.request({
url: '/tgb/api/v1/data/get',
method: 'post',
data,
});
};

View File

@ -7,3 +7,18 @@ export const login = (data) => {
data,
});
};
/**
*
* @param data
* password
* new_password
* @returns
*/
export const modifyPassword = (data) => {
return request.request({
url: '/admin/update',
method: 'post',
data,
});
};

101
src/services/nft.ts Normal file
View File

@ -0,0 +1,101 @@
import request from '@/utils/request';
/**
* NFT合约列表
* @param {object} params
* @returns {array} data
* name
* type
* usdt_price usdt的比例
* eth_price eth的比例
*/
export const getNFTContractList = () => {
return request.request({
url: '/erc721/get',
method: 'get',
});
};
/**
* NFT合约
* @param {object} data
*
* @returns {array} data
*/
export const createNFTContract = (data) => {
return request.request({
url: '/erc721/create',
method: 'post',
data,
});
};
/**
* NFT列表
* @param {object} params
*
* @returns {array} data
*/
export const getNFTList = (params) => {
return request.request({
url: '/nft/get',
method: 'get',
params,
});
};
/**
* NFT后通知后端
* @param {object} data
*
* @returns {array} data
*/
export const mintNFT = (data) => {
return request.request({
url: '/nft/create',
method: 'post',
data,
});
};
/**
* NFT时的签名
* @param {object} data
*
* @returns {array} data
*/
export const getSignature = (data) => {
return request.request({
url: '/nft/sign/get',
method: 'post',
data,
});
};
/**
* assetID是否存在
* @param {object} data
*
* @returns {array} data
*/
export const checkAssetID = (data) => {
return request.request({
url: '/nft/assetid/check',
method: 'post',
data,
});
};
/**
* NFT信息
* @param {object} data
*
* @returns {array} data
*/
export const updateNFTInfo = (data) => {
return request.request({
url: '/nft/update',
method: 'post',
data,
});
};

View File

@ -0,0 +1,53 @@
import request from '@/utils/request';
/**
*
* @param {object} params
* type
* @returns {array} dat
* name
* type
* usdt_price usdt的比例
* eth_price eth的比例
*/
export const getCoinTypeList = (params) => {
return request.request({
url: '/erc20/get',
method: 'get',
params,
});
};
/**
*
* @param {object} params
* name
* usdt_price usdt的比例
* eth_price eth的比例
* num
* @returns {object} data
*/
export const addCoinType = (data) => {
return request.request({
url: '/erc20/create',
method: 'post',
data,
});
};
/**
*
* @param {object} params
* name
* type
* usdt_price usdt的比例
* eth_price eth的比例
* @returns {object} data
*/
export const modifyCoinType = (data) => {
return request.request({
url: '/erc20/update',
method: 'post',
data,
});
};

View File

@ -0,0 +1,20 @@
import request from '@/utils/request';
/**
*
* @param {object} data
* page
* pageSize
* start_time
* end_time
* @returns {array} data
* type
* address
*/
export const getRecordList = (data) => {
return request.request({
url: '/recharge/record',
method: 'post',
data,
});
};

View File

@ -0,0 +1,61 @@
import request from '@/utils/request';
/**
*
* @param {object} params
* @returns {array} data
* type
* address
*/
export const getAddressList = (params) => {
return request.request({
url: '/wallet/get',
method: 'get',
params,
});
};
/**
*
* @param {Object} data
* type
* address ()
* @returns
*/
export const createAddress = (data) => {
return request.request({
url: '/wallet/create',
method: 'post',
data,
});
};
/**
*
* @param {Object} data
* address
* @returns
*/
export const deleteAddress = (data) => {
return request.request({
url: '/wallet/delete',
method: 'post',
data,
});
};
/**
*
* @param {Object} data
* address
* type
* new_address
* @returns
*/
export const modifyAddress = (data) => {
return request.request({
url: '/wallet/update',
method: 'post',
data,
});
};

View File

@ -0,0 +1,34 @@
import request from '@/utils/request';
/**
*
* @param {object} params
*
* @returns {array} data
* uuid id
* to_addrss
* from_address
* time
* price
*/
export const getWithdrawList = (data) => {
return request.request({
url: '/withdrawal/get',
method: 'post',
data,
});
};
/**
*
* @param {object} params
* uuid id
* @returns {object} data
*/
export const solveWithdraw = (data) => {
return request.request({
url: 'withdrawal/update',
method: 'post',
data,
});
};

View File

@ -0,0 +1,88 @@
import request from '@/utils/request';
/**
*
* @param {object} params
* @returns {array} data
*/
export const getAccountList = (params) => {
return request.request({
url: '/user/get',
method: 'get',
params,
});
};
/**
*
* @param {object} data
* name
* password
* role
* @returns {array} data
*/
export const addUser = (data) => {
return request.request({
url: '/user/create',
method: 'post',
data,
});
};
/**
*
* @param {object} data
* name
* password
* role
* @returns {array} data
*/
export const updateUser = (data) => {
return request.request({
url: '/user/update',
method: 'post',
data,
});
};
/**
*
* @param {object} data
* name
* status
* @returns {array} data
*/
export const changeUserStatus = (data) => {
return request.request({
url: '/user/status',
method: 'post',
data,
});
};
/**
*
* @param {object} data
* name
* @returns {array} data
*/
export const deleteUser = (data) => {
return request.request({
url: '/user/delete',
method: 'post',
data,
});
};
/**
*
* @param {object} params
* @returns {array} data
*/
export const getAccounPermission = (params) => {
return request.request({
url: '/user/role/get',
method: 'get',
params,
});
};

View File

@ -0,0 +1,53 @@
import request from '@/utils/request';
/**
*
* @param {object} params
* @returns {array} data
*/
export const getNoticeList = (params) => {
return request.request({
url: '/notice/get',
method: 'get',
params,
});
};
/**
*
* @param {object} params
* @returns {array} data
*/
export const createNotice = (data) => {
return request.request({
url: '/notice/create',
method: 'post',
data,
});
};
/**
*
* @param {object} params
* @returns {array} data
*/
export const updateNotice = (data) => {
return request.request({
url: '/notice/update',
method: 'post',
data,
});
};
/**
*
* @param {object} params
* @returns {array} data
*/
export const deleteNotice = (data) => {
return request.request({
url: '/notice/delete',
method: 'post',
data,
});
};

View File

@ -0,0 +1,27 @@
import request from '@/utils/request';
/**
*
* @param {object} params
* @returns {array} data
*/
export const getPermission = (params) => {
return request.request({
url: '/permission/get',
method: 'get',
params,
});
};
/**
*
* @param {object} params
* @returns {array} data
*/
export const upsertPermission = (data) => {
return request.request({
url: '/permission/upsert',
method: 'post',
data,
});
};

View File

@ -0,0 +1,60 @@
import request from '@/utils/request';
/**
*
* @param {object} params
* @returns {array} data
* role_name
* role
*/
export const getRoleList = (params) => {
return request.request({
url: '/role/get',
method: 'get',
params,
});
};
/**
*
* @param {object} params
* role_name
* remark
* @returns {array} data
*/
export const createRole = (data) => {
return request.request({
url: '/role/create',
method: 'post',
data,
});
};
/**
*
* @param {object} params
* role_name
* remark
* @returns {array} data
*/
export const updateRole = (data) => {
return request.request({
url: '/role/update',
method: 'post',
data,
});
};
/**
*
* @param {object} params
* role
* @returns {array} data
*/
export const deleteRole = (data) => {
return request.request({
url: '/role/delete',
method: 'post',
data,
});
};

View File

@ -0,0 +1,51 @@
import request from '@/utils/request';
/**
*
* @param {object} params
* @returns {array} data
*/
export const getSecretKey = () => {
return request.request({
url: '/key/get',
method: 'get',
});
};
/**
*
* @param {object} data
* @returns {array} data
*/
export const createSecretKey = (data) => {
return request.request({
url: '/key/create',
method: 'post',
data,
});
};
/**
* AppID
* @param {object} params
* @returns {array} data
*/
export const getAppID = () => {
return request.request({
url: '/appid/get',
method: 'get',
});
};
/**
* AppID
* @param {object} data
* @returns {array} data
*/
export const createAppID = (data) => {
return request.request({
url: '/appid/create',
method: 'post',
data,
});
};

16
src/services/user/user.ts Normal file
View File

@ -0,0 +1,16 @@
import request from '@/utils/request';
/**
*
* @param {object} data
* @returns {array} data
* role_name
* role
*/
export const getUserList = (data) => {
return request.request({
url: '/account/get',
method: 'post',
data,
});
};

View File

@ -0,0 +1,42 @@
import Web3 from 'web3';
import detectEthereumProvider from '@metamask/detect-provider';
import { message } from 'antd';
let provider: any;
export const web3 = new Web3();
if (typeof window.ethereum !== 'undefined') {
web3.eth.defaultAccount = window.ethereum.selectedAddress;
}
export async function initWeb3() {
provider = await detectEthereumProvider();
if (!provider) {
console.log('请安装MetaMask');
return false;
}
try {
web3.setProvider(provider);
const accounts = await provider.request({
method: 'eth_requestAccounts',
});
web3.eth.defaultAccount = accounts[0];
return true;
} catch (reason) {
switch (reason) {
case 'Already processing eth_requestAccounts. Please wait.': // 已有请求存在
message.warning('请打开MetaMask完成授权');
break;
case 'User rejected provider access': //如果用户拒绝了登录请求
message.warning('请同意登录请求');
break;
default:
// 本不该执行到这里,但是真到这里了,说明发生了意外
message.warning('登录出错err:' + reason);
break;
}
}
return false;
}

6
src/utils/bigNumber.ts Normal file
View File

@ -0,0 +1,6 @@
import BigNumber from 'bignumber.js';
export const BIG_ZERO = new BigNumber(0);
export const BIG_ONE = new BigNumber(1);
export const BIG_NINE = new BigNumber(9);
export const BIG_TEN = new BigNumber(10);

View File

@ -0,0 +1,6 @@
import BigNumber from 'bignumber.js';
import { BIG_TEN } from './bigNumber';
export const getBalanceAmount = (amount: BigNumber, decimals = 18) => {
return new BigNumber(amount).dividedBy(BIG_TEN.pow(decimals));
};

4
src/utils/getData.ts Normal file
View File

@ -0,0 +1,4 @@
export const getData = async (fetch: any) => {
const res = (await fetch()) || {};
return res.data;
};

View File

@ -6,13 +6,13 @@ import { CACHE_TOKEN } from '@/constants/cacheKey';
// create an axios instance
const request = axios.create({
baseURL: '', //
baseURL: '/tbg/api/v1', //
timeout: 10000, // request timeout
});
// request interceptor
request.interceptors.request.use(
(memo: any) => {
memo.headers.token = localStorage.getItem(CACHE_TOKEN);
memo.headers.Authorization = localStorage.getItem(CACHE_TOKEN);
return memo;
},
(error) => {
@ -24,7 +24,7 @@ request.interceptors.response.use(
(response) => {
const res: any = response.data;
if (res.code !== 200) {
if (res.code === 401) {
if (res.code === 401 || res.code === 10001) {
notification.error({
message: '错误信息',
description: '登录失效',

View File

@ -5,12 +5,11 @@ export const fetchTableData = async (
formatObj: any = {},
) => {
params.page = params.current;
params.num = params.pageSize;
params.size = params.pageSize;
delete params.current;
delete params.pageSize;
const res = (await fetch(params)) || {};
const data = res.data;
const data = res.items || [];
data?.forEach((n: any) => {
for (const key in formatObj) {
n[key] = n[formatObj[key]];

150
src/utils/web3.ts Normal file
View File

@ -0,0 +1,150 @@
import Web3 from 'web3';
import detectEthereumProvider from '@metamask/detect-provider';
import { message } from 'antd';
import BigNumber from 'bignumber.js';
import abi from '@/constants/abi';
let provider: any;
export const web3 = new Web3();
if (typeof window.ethereum !== 'undefined') {
web3.eth.defaultAccount = (window.ethereum as any).selectedAddress;
}
export async function initWeb3() {
provider = await detectEthereumProvider();
if (!provider) {
message.warning('请安装MetaMask');
return false;
}
try {
web3.setProvider(provider);
const accounts = await provider.request({
method: 'eth_requestAccounts',
});
web3.eth.defaultAccount = accounts[0];
return true;
} catch (reason) {
switch (reason) {
case 'Already processing eth_requestAccounts. Please wait.': // 已有请求存在
message.warning('请打开MetaMask完成授权');
break;
case 'User rejected provider access': //如果用户拒绝了登录请求
message.warning('请同意登录请求');
break;
default:
// 本不该执行到这里,但是真到这里了,说明发生了意外
message.warning('登录出错err:' + reason);
break;
}
}
return false;
}
//发布合约
/**
*
* @param abi ABI
* @param bin
*/
export async function deployContract(abi: string, bin: string, params) {
const contract = new web3.eth.Contract(eval('(' + abi + ')'));
const result = await contract
.deploy({
data: '0x' + bin,
arguments: params,
})
.send({ from: web3.eth.defaultAccount + '', gas: 3000000 }, async function (e, contract) {});
return (result as any)._address;
}
// 代币转账
/**
*
* @param abi ABI
* @param address
* @param toAddress
*/
export async function transfer(abi: string, address: string, toAddress: string) {
const contract = new web3.eth.Contract(eval('(' + abi + ')'), address);
await contract.methods
.transfer(toAddress, new BigNumber(1 * Math.pow(10, 18)))
.send({ from: web3.eth.defaultAccount, gas: 3000000 }, function (error, transactionHash) {
if (!error) {
console.log('transactionHash is ' + transactionHash);
} else {
console.log(error);
}
});
}
// NFT创建
/**
*
* @param abi ABI
* @param address
* @param toAddress
* @param sign
*/
export async function NFTMint(params: any) {
if (params.toAddress == '' || params.toAddress == null || params.toAddress == undefined) {
params.toAddress = web3.eth.defaultAccount;
}
let tx_hash = '';
const contract = new web3.eth.Contract(eval('(' + params.abi + ')'), params.address);
await contract.methods
.Mint(params.toAddress, params.sign)
.send({ from: web3.eth.defaultAccount, gas: 3000000 }, function (error, transactionHash) {
if (!error) {
tx_hash = transactionHash;
} else {
console.log('error = ', error);
return tx_hash;
}
});
return tx_hash;
}
// NFT转移
/**
*
* @param abi ABI
* @param address
* @param toAddress
* @param token_id
*/
export async function transferNFT(params: any) {
const contract = new web3.eth.Contract(eval('(' + params.abi + ')'), params.address);
await contract.methods
.safeTransferFrom(web3.eth.defaultAccount, params.toAddress, params.token_id)
.send({ from: web3.eth.defaultAccount }),
function (error, transactionHash) {
if (!error) {
return transactionHash;
} else {
console.log(error);
}
};
}
// NFT销毁
/**
*
* @param abi ABI
* @param address
* @param token_id
*/
export async function burnNFT(params: any) {
const contract = new web3.eth.Contract(eval('(' + params.abi + ')'), params.address);
await contract.methods.Burn(params.token_id).send({ from: web3.eth.defaultAccount }),
function (error, transactionHash) {
if (!error) {
return transactionHash;
} else {
console.log(error);
}
};
}

41
src/widget/MoreData.tsx Normal file
View File

@ -0,0 +1,41 @@
import { Col, Row, Statistic } from 'antd';
import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
const MoreData = (props: any, title: string, company = '人') => {
return (
<div>
<p>{title}</p>
<h1>{company}</h1>
{/* <div>
<Row>
<Col span={12}>
<Statistic
title="较环比"
value={props.day_proportion}
valueStyle={
props.week_proportion > 0
? { color: '#3f8600', fontSize: 15 }
: { color: '#cf1322', fontSize: 15 }
}
prefix={props.week_proportion > 0 ? <ArrowDownOutlined /> : <ArrowUpOutlined />}
suffix="%"
/>
</Col>
<Col span={12}>
<Statistic
title="较同比"
value={Math.abs(props.week_proportion)}
valueStyle={
props.week_proportion > 0
? { color: '#3f8600', fontSize: 15 }
: { color: '#cf1322', fontSize: 15 }
}
prefix={props.week_proportion > 0 ? <ArrowDownOutlined /> : <ArrowUpOutlined />}
suffix="%"
/>
</Col>
</Row>
</div> */}
</div>
);
};
export default MoreData;

View File

@ -0,0 +1,67 @@
import React, { useRef, useState } from 'react';
import { createForm, onFieldChange } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
// import { fetchTableData } from '@/utils/table';
import { Form, FormItem, Input, Select, Radio, Switch } from '@formily/antd';
interface PaySelectModalPropsType extends ModalProps {
onOk: (val: any) => void;
onCancel: () => void;
// modalData: any;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
Select,
Radio,
Switch,
},
});
const form = createForm({});
const PaySelectModal = ({ onOk, onCancel, ...rest }: PaySelectModalPropsType) => {
const [send, useSend] = useState(true);
const handleOk = () => {
const formState = form.getFormState();
onOk(formState.values);
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="选择方式" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.Number
name="type"
title="支付方式"
required
x-decorator="FormItem"
x-component="Radio.Group"
enum={[
{ label: '秘钥支付', value: 1 },
{ label: '小狐狸钱包支付', value: 2 },
]}
/>
<SchemaField.String
name="key"
title="秘钥"
x-decorator="FormItem"
x-component="Input"
x-component-props={{
placeholder: '请输入秘钥,若使用小狐狸支付则不需要输入',
}}
/>
</SchemaField>
</Form>
</Modal>
);
};
export default PaySelectModal;

25470
yarn.lock

File diff suppressed because it is too large Load Diff