Compare commits

...

13 Commits
master ... dev

34 changed files with 16053 additions and 11974 deletions

View File

@ -56,12 +56,16 @@
"@formily/antd": "^2.0.7", "@formily/antd": "^2.0.7",
"@formily/core": "^2.0.7", "@formily/core": "^2.0.7",
"@formily/react": "^2.0.7", "@formily/react": "^2.0.7",
"@metamask/detect-provider": "^1.2.0",
"@umijs/route-utils": "^2.0.3", "@umijs/route-utils": "^2.0.3",
"@walletconnect/client": "^1.7.1",
"@walletconnect/qrcode-modal": "^1.7.1",
"ahooks": "^2.10.14", "ahooks": "^2.10.14",
"antd": "^4.17.2", "antd": "^4.17.2",
"axios": "^0.24.0", "axios": "^0.24.0",
"braft-editor": "^2.3.9", "braft-editor": "^2.3.9",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"js-md5": "^0.7.3",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"moment": "^2.25.3", "moment": "^2.25.3",
"omit.js": "^2.0.2", "omit.js": "^2.0.2",
@ -71,8 +75,14 @@
"react-dev-inspector": "^1.1.1", "react-dev-inspector": "^1.1.1",
"react-dom": "^17.0.0", "react-dom": "^17.0.0",
"react-helmet-async": "^1.0.4", "react-helmet-async": "^1.0.4",
"tronweb": "^4.2.0",
"umi": "^3.5.0", "umi": "^3.5.0",
"umi-serve": "^1.9.10" "umi-serve": "^1.9.10",
"web3": "^1.7.0",
"@metamask/detect-provider": "^1.2.0",
"@walletconnect/client": "^1.7.1",
"@walletconnect/qrcode-modal": "^1.7.1",
"bignumber.js": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"@ant-design/pro-cli": "^2.0.2", "@ant-design/pro-cli": "^2.0.2",
@ -94,7 +104,7 @@
"@umijs/yorkie": "^2.0.3", "@umijs/yorkie": "^2.0.3",
"babel-plugin-import": "^1.13.3", "babel-plugin-import": "^1.13.3",
"carlo": "^0.9.46", "carlo": "^0.9.46",
"cross-env": "^7.0.0", "cross-env": "^7.0.3",
"cross-port-killer": "^1.1.1", "cross-port-killer": "^1.1.1",
"detect-installer": "^1.0.1", "detect-installer": "^1.0.1",
"enzyme": "^3.11.0", "enzyme": "^3.11.0",

View File

@ -0,0 +1,5 @@
export enum CoinType {
ETH = 'eth',
TRON = 'tron',
}
export default CoinType;

View File

@ -0,0 +1,69 @@
import React, { useState } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import { message, Spin } from 'antd';
import DetailPageContainer from '@/components/DetailPageContainer';
import { AddAddress } from '@/services/eth';
import { initWeb3, walletSign } from '../../../../utils/web3';
import { Form, FormItem, Input, FormButtonGroup, Submit } from '@formily/antd';
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
Submit,
},
});
const form = createForm({});
const WorkEdit = () => {
const [loading, setLoading] = useState(false);
const handleSubmit = async (val) => {
setLoading(true);
try {
await initWeb3();
const signInfo = await walletSign();
val.key = signInfo.raw;
val.sign = signInfo.sign;
val.coinType = 'eth';
val.num = parseInt(val.num);
await AddAddress(val);
message.success('操作成功');
setLoading(false);
history.back();
} catch (e) {
setLoading(false);
}
};
return (
<DetailPageContainer>
<Spin spinning={loading}>
<Form form={form} labelCol={4} wrapperCol={18} onAutoSubmit={handleSubmit}>
<SchemaField>
<SchemaField.String
name="alias"
title="备注"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.Number
name="num"
title="数量"
required
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField>
<FormButtonGroup.FormItem>
<Submit block size="large">
</Submit>
</FormButtonGroup.FormItem>
</Form>
</Spin>
</DetailPageContainer>
);
};
export default WorkEdit;

View File

@ -0,0 +1,57 @@
// 选择作品弹窗
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 AddAddressModalPropsType extends ModalProps {
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
NumberPicker,
},
});
const form = createForm({});
const AddAddressModal = ({ onOk, onCancel, ...rest }: AddAddressModalPropsType) => {
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.String
name="alias"
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 AddAddressModal;

View File

@ -0,0 +1,103 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { queryAddressList } from '@/services/eth';
import { fetchTableData } from '@/utils/table';
import CoinType from '@/constants/enum/coinType';
import AddAddressModal from './components/AddAddressModal';
import { AddAddress } from '@/services/eth';
import { initWeb3, walletSign } from '@/utils/web3';
import { message, Select } from 'antd';
const AddressList = () => {
const [isModalVisible, setIsModalVisible] = useState(false);
const [selectValue, setSelectValue] = useState(CoinType.ETH);
const tableRef = useRef<ActionType>();
const columns: ProColumns<any>[] = [
{
title: '类型',
dataIndex: 'coinType',
hideInTable: true,
initialValue: CoinType.ETH,
renderFormItem: (item, { type, defaultRender, ...rest }, form) => {
return (
<Select
options={[
{
label: 'ETH',
value: CoinType.ETH,
},
{
label: 'TRON',
value: CoinType.TRON,
},
]}
onChange={(val) => {
setSelectValue(val);
}}
/>
);
},
},
{
title: '备注',
dataIndex: 'remarks',
hideInSearch: true,
width: '20%',
// hideInSearch: true,
},
{
title: '地址',
dataIndex: 'address',
hideInSearch: true,
ellipsis: true,
},
{
title: 'key',
dataIndex: 'key',
hideInSearch: true,
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
toolBarActions={[
{
type: 'add',
onConfirm: () => {
setIsModalVisible(true);
},
},
]}
request={async (params) => {
return fetchTableData(queryAddressList, params);
}}
/>
<AddAddressModal
visible={isModalVisible}
onCancel={function () {
setIsModalVisible(false);
}}
onOk={async function (val: any): Promise<void> {
try {
await initWeb3();
const signInfo = await walletSign();
val.key = signInfo.raw;
val.sign = signInfo.sign;
val.coinType = selectValue;
const params = { ...val };
await AddAddress(params);
message.success('操作成功');
} catch (e) {}
setIsModalVisible(false);
}}
/>
</div>
);
};
export default AddressList;

View File

View File

@ -0,0 +1,96 @@
import React, { useRef } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { queryRecordList } from '@/services/eth';
import { fetchTableData } from '@/utils/table';
import CoinType from '@/constants/enum/coinType';
import { Select } from 'antd';
import { getBalanceAmount } from '@/utils/formatBalance';
import BigNumber from 'bignumber.js';
const AddressList = () => {
const tableRef = useRef<ActionType>();
const columns: ProColumns<any>[] = [
{
title: '类型',
dataIndex: 'coinType',
hideInTable: true,
initialValue: CoinType.ETH,
renderFormItem: (item, { type, defaultRender, ...rest }, form) => {
return (
<Select
options={[
{
label: 'ETH',
value: CoinType.ETH,
},
{
label: 'TRON',
value: CoinType.TRON,
},
]}
/>
);
},
},
{
title: '交易哈希',
dataIndex: 'txHash',
hideInSearch: true,
ellipsis: true,
},
{
title: '金额',
dataIndex: 'amount',
width: '10%',
hideInSearch: true,
},
{
title: '位数',
dataIndex: 'decimals',
width: '5%',
hideInSearch: true,
},
{
title: '区块',
dataIndex: 'blockNumber',
width: '10%',
hideInSearch: true,
},
{
title: '发送地址',
dataIndex: 'from',
hideInSearch: true,
ellipsis: true,
},
{
title: '接受地址',
dataIndex: 'to',
hideInSearch: true,
ellipsis: true,
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
request={async (params) => {
const res = await fetchTableData(queryRecordList, params);
for (const key in res.data) {
if (Object.prototype.hasOwnProperty.call(res.data, key)) {
const element = res.data[key];
element.amount = getBalanceAmount(
new BigNumber(element.amount),
element.decimals,
).toNumber();
}
}
return res;
}}
/>
</div>
);
};
export default AddressList;

View File

@ -0,0 +1,70 @@
import React, { useState } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import { message, Spin } from 'antd';
import DetailPageContainer from '@/components/DetailPageContainer';
import { AddAddress } from '@/services/eth';
import { initWeb3, walletSign } from '../../../../utils/web3';
import { Form, FormItem, Input, FormButtonGroup, Submit } from '@formily/antd';
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
Submit,
},
});
const form = createForm({});
const WorkEdit = () => {
const [loading, setLoading] = useState(false);
const handleSubmit = async (val) => {
setLoading(true);
try {
await initWeb3();
const signInfo = await walletSign();
val.key = signInfo.raw;
val.sign = signInfo.sign;
val.coinType = 'eth';
val.num = parseInt(val.num);
const params = { ...val };
await AddAddress(params);
message.success('操作成功');
setLoading(false);
history.back();
} catch (e) {
setLoading(false);
}
};
return (
<DetailPageContainer>
<Spin spinning={loading}>
<Form form={form} labelCol={4} wrapperCol={18} onAutoSubmit={handleSubmit}>
<SchemaField>
<SchemaField.String
name="alias"
title="备注"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.Number
name="num"
title="数量"
required
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField>
<FormButtonGroup.FormItem>
<Submit block size="large">
</Submit>
</FormButtonGroup.FormItem>
</Form>
</Spin>
</DetailPageContainer>
);
};
export default WorkEdit;

View File

@ -0,0 +1,57 @@
// 选择作品弹窗
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 AddAddressModalPropsType extends ModalProps {
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
NumberPicker,
},
});
const form = createForm({});
const AddAddressModal = ({ onOk, onCancel, ...rest }: AddAddressModalPropsType) => {
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.String
name="alias"
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 AddAddressModal;

View File

@ -0,0 +1,77 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { queryAddressList } from '@/services/eth';
import { fetchTableData } from '@/utils/table';
import CoinType from '@/constants/enum/coinType';
import AddAddressModal from './components/AddAddressModal';
import { AddAddress } from '@/services/eth';
import { initWeb3, walletSign } from '@/utils/web3';
import { message } from 'antd';
const AddressList = () => {
const [isModalVisible, setIsModalVisible] = useState(false);
const tableRef = useRef<ActionType>();
const columns: ProColumns<any>[] = [
{
title: '备注',
dataIndex: 'remarks',
hideInSearch: true,
width: '20%',
// hideInSearch: true,
},
{
title: '地址',
dataIndex: 'address',
hideInSearch: true,
ellipsis: true,
},
{
title: 'key',
dataIndex: 'key',
hideInSearch: true,
},
];
return (
<div>
<Table
columns={columns}
// indexColumn
rowKey="id"
actionRef={tableRef}
toolBarActions={[
{
type: 'add',
onConfirm: () => {
setIsModalVisible(true);
},
},
]}
request={async (params) => {
params.coinType = CoinType.ETH;
return fetchTableData(queryAddressList, params);
}}
/>
<AddAddressModal
visible={isModalVisible}
onCancel={function () {
setIsModalVisible(false);
}}
onOk={async function (val: any): Promise<void> {
try {
await initWeb3();
const signInfo = await walletSign();
val.key = signInfo.raw;
val.sign = signInfo.sign;
val.coinType = CoinType.ETH;
await AddAddress(val);
message.success('操作成功');
} catch (e) {}
setIsModalVisible(false);
}}
/>
</div>
);
};
export default AddressList;

View File

View File

@ -0,0 +1,74 @@
import React, { useRef } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { queryRecordList } from '@/services/eth';
import { fetchTableData } from '@/utils/table';
import CoinType from '@/constants/enum/coinType';
import { getBalanceAmount } from '@/utils/formatBalance';
import BigNumber from 'bignumber.js';
const AddressList = () => {
const tableRef = useRef<ActionType>();
const columns: ProColumns<any>[] = [
{
title: '交易哈希',
dataIndex: 'txHash',
hideInSearch: true,
ellipsis: true,
},
{
title: '金额',
dataIndex: 'amount',
width: '10%',
hideInSearch: true,
},
{
title: '位数',
dataIndex: 'decimals',
width: '5%',
hideInSearch: true,
},
{
title: '区块',
dataIndex: 'blockNumber',
width: '10%',
hideInSearch: true,
},
{
title: '发送地址',
dataIndex: 'from',
hideInSearch: true,
ellipsis: true,
},
{
title: '接受地址',
dataIndex: 'to',
hideInSearch: true,
ellipsis: true,
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
request={async (params) => {
params.coinType = CoinType.ETH;
const res = await fetchTableData(queryRecordList, params);
for (const key in res.data) {
if (Object.prototype.hasOwnProperty.call(res.data, key)) {
const element = res.data[key];
element.amount = getBalanceAmount(
new BigNumber(element.amount),
element.decimals,
).toNumber();
}
}
return res;
}}
/>
</div>
);
};
export default AddressList;

View File

@ -3,6 +3,7 @@
.container { .container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center;
height: 100vh; height: 100vh;
overflow: auto; overflow: auto;
background: @layout-body-background; background: @layout-body-background;
@ -23,6 +24,10 @@
} }
} }
.button {
margin-top: 24px;
}
.content { .content {
flex: 1; flex: 1;
padding: 32px 0; padding: 32px 0;
@ -37,7 +42,8 @@
} }
.content { .content {
padding: 100px 0 24px; width: 40%;
padding: 300px 0 24px;
} }
} }

View File

@ -1,4 +1,4 @@
import { LockOutlined, UserOutlined } from '@ant-design/icons'; import { LeftSquareFilled, LockOutlined, UserOutlined } from '@ant-design/icons';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { ProFormText, LoginForm } from '@ant-design/pro-form'; import { ProFormText, LoginForm } from '@ant-design/pro-form';
import { useIntl, history, FormattedMessage, useModel } from 'umi'; import { useIntl, history, FormattedMessage, useModel } from 'umi';
@ -7,24 +7,42 @@ import defaultSettings from '../../../config/defaultSettings';
import styles from './index.less'; import styles from './index.less';
import { CACHE_TOKEN } from '@/constants/cacheKey'; import { CACHE_TOKEN } from '@/constants/cacheKey';
import { initWeb3, walletSign } from '../../utils/web3';
import { Button } from 'antd';
const Login: React.FC = () => { const Login: React.FC = () => {
const intl = useIntl(); const intl = useIntl();
const handleSubmit = async () => {
const handleSubmit = async (values: API.LoginParams) => {
// 登录 // 登录
const res: any = await login({ ...values }); await initWeb3();
localStorage.setItem(CACHE_TOKEN, res.token); const signInfo = await walletSign();
const res: any = await login({ data: signInfo.raw, sign: signInfo.sign });
localStorage.setItem(CACHE_TOKEN, res);
// const res: any = await login({ ...values });
// localStorage.setItem(CACHE_TOKEN, res.token);
/** 此方法会跳转到 redirect 参数所在的位置 */ /** 此方法会跳转到 redirect 参数所在的位置 */
if (!history) return; if (!history) return;
const { query } = history.location; const { query } = history.location;
const { redirect } = query as { redirect: string }; const { redirect } = query as { redirect: string };
history.push(redirect || '/'); history.push(redirect || '/');
}; };
const handleQRCodeSubmit = async () => {};
return ( return (
<div className={styles.container}> <div className={styles.container}>
<div className={styles.content}> <div className={styles.content}>
<LoginForm <Button size={'large'} type="primary" block onClick={handleSubmit}>
</Button>
<Button
className={styles.button}
size={'large'}
type="primary"
block
onClick={handleQRCodeSubmit}
>
</Button>
{/* <LoginForm
logo={<img alt="logo" src="/logo.svg" />} logo={<img alt="logo" src="/logo.svg" />}
title={defaultSettings.title as string} title={defaultSettings.title as string}
initialValues={{ initialValues={{
@ -78,7 +96,7 @@ const Login: React.FC = () => {
}, },
]} ]}
/> />
</LoginForm> </LoginForm> */}
</div> </div>
</div> </div>
); );

View File

@ -0,0 +1,55 @@
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 AddAddressModalPropsType extends ModalProps {
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
NumberPicker,
},
});
const form = createForm({});
const AddAddressModal = ({ onOk, onCancel, ...rest }: AddAddressModalPropsType) => {
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.String
name="alias"
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 AddAddressModal;

View File

@ -0,0 +1,77 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { message } from 'antd';
import AddAddressModal from '@/pages/Tron/Address/List/components/addAddressModal';
import { initWeb3, walletSign } from '@/utils/web3';
import { creatTronAddress, getAddressList } from '@/services/address';
import { fetchTronTableData } from '@/utils/table';
const Address: React.FC = () => {
const tableRef = useRef<ActionType>();
const [visible, setVisible] = useState(false);
const columns: ProColumns<any>[] = [
{
title: '备注',
dataIndex: 'alias',
width: '10%',
hideInSearch: true,
},
{
title: '地址',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: 'key',
dataIndex: 'key',
width: 150,
hideInSearch: true,
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
toolBarActions={[
{
type: 'add',
onConfirm: () => {
setVisible(true);
},
},
]}
request={async (params) => {
return fetchTronTableData(getAddressList, params);
}}
/>
<AddAddressModal
visible={visible}
onCancel={function () {
setVisible(false);
}}
onOk={async function (val: any): Promise<void> {
try {
await initWeb3();
const signInfo = await walletSign();
val.key = signInfo.raw;
val.sign = signInfo.sign;
val.coinType = 'tron';
const params = { ...val };
await creatTronAddress(params);
message.success('添加成功');
} catch (e) {
console.log(e);
message.success('发生错误');
setVisible(false);
}
}}
/>
</div>
);
};
export default Address;

View File

@ -0,0 +1,79 @@
import React, { useRef } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { fetchTronTableData } from '@/utils/table';
import { getTronRecord } from '@/services/address';
const Address: React.FC = () => {
const tableRef = useRef<ActionType>();
const columns: ProColumns<any>[] = [
{
title: '交易哈希',
dataIndex: 'txHash',
hideInSearch: true,
},
{
title: '金额',
dataIndex: 'amount',
hideInSearch: true,
},
{
title: '链名称',
dataIndex: 'chain',
hideInSearch: true,
},
{
title: '转换后的数量',
dataIndex: 'blockNumber',
hideInSearch: true,
},
{
title: '位数',
dataIndex: 'decimals',
hideInSearch: true,
},
{
title: '发送地址',
dataIndex: 'from',
hideInSearch: true,
},
{
title: '通知地址',
dataIndex: 'to',
hideInSearch: true,
},
{
title: '时间',
dataIndex: 'timeStamp',
hideInSearch: true,
},
{
title: '合约地址',
dataIndex: 'toAddress',
hideInSearch: true,
},
{
title: 'Token名称',
dataIndex: 'contract',
hideInSearch: true,
},
{
title: '状态',
dataIndex: 'contract',
hideInSearch: true,
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
request={async (params) => {
return fetchTronTableData(getTronRecord, params);
}}
/>
</div>
);
};
export default Address;

View File

@ -0,0 +1,93 @@
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 AddTokenModalPropsType extends ModalProps {
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
NumberPicker,
},
});
const form = createForm({});
const AddTokenModal = ({ onOk, onCancel, ...rest }: AddTokenModalPropsType) => {
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.String
name="network"
title="网络名称"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.String
name="rpcUrl"
title="RPC URL"
required
x-component-props={{ maxLength: 100, showCount: true }}
x-decorator="FormItem"
x-component="Input.TextArea"
/>
<SchemaField.String
name="apiKey"
title="API Key"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.Number
name="contract"
title="代币合约地址(放空则查找主代币"
required
x-validator="number"
x-decorator="FormItem"
x-component="NumberPicker"
/>
<SchemaField.String
name="symbol"
title="代币符号"
required
x-decorator="FormItem"
x-component="Editor"
/>
<SchemaField.String
name="decimals"
title="精度"
required
x-decorator="FormItem"
x-component="Editor"
/>
<SchemaField.String
name="browser"
title="区块浏览器"
required
x-decorator="FormItem"
x-component="Editor"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default AddTokenModal;

View File

@ -0,0 +1,101 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { message } from 'antd';
import AddTokenModal from '@/pages/Tron/Token/List/components/addTokenModal';
import { initWeb3, walletSign } from '@/utils/web3';
const Address: React.FC = () => {
const tableRef = useRef<ActionType>();
const [visible, setVisible] = useState(false);
const columns: ProColumns<any>[] = [
{
title: '网络名称',
dataIndex: 'network',
width: '10%',
hideInSearch: true,
},
{
title: '地址',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: 'API Key',
dataIndex: 'apiKey',
hideInSearch: true,
},
{
title: '代币符号',
dataIndex: 'symbol',
hideInSearch: true,
width: 150,
},
{
title: 'RPC URL',
dataIndex: 'rpcUrl',
width: 150,
hideInSearch: true,
},
{
title: '精度',
dataIndex: 'decimals',
hideInSearch: true,
width: 150,
},
{
title: '区块浏览器',
dataIndex: 'browser',
hideInSearch: true,
width: 150,
},
{
title: '代币合约地址',
dataIndex: 'contract',
hideInSearch: true,
width: 150,
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
toolBarActions={[
{
type: 'add',
onConfirm: () => {
setVisible(true);
},
},
]}
/>
<AddTokenModal
visible={visible}
onCancel={function () {
setVisible(false);
}}
onOk={async function (val: any): Promise<void> {
try {
await initWeb3();
const signInfo = await walletSign();
val.key = signInfo.raw;
val.sign = signInfo.sign;
val.coinType = 'tron';
const params = { ...val };
// await creatTronAddress(params);
message.success('添加成功');
} catch (e) {
console.log(e);
message.success('添加错误');
setVisible(false);
}
}}
/>
</div>
);
};
export default Address;

View File

@ -7,6 +7,7 @@ import RoutePath from '@/routes/routePath';
import { queryWorkList, deleteWork } from '@/services/work'; import { queryWorkList, deleteWork } from '@/services/work';
import { fetchTableData } from '@/utils/table'; import { fetchTableData } from '@/utils/table';
import TimeText from '@/components/Typography/TimeText'; import TimeText from '@/components/Typography/TimeText';
import WorkSelectModal from '@/widget/Work/WorkSelectModal';
const WorkList = () => { const WorkList = () => {
const tableRef = useRef<ActionType>(); const tableRef = useRef<ActionType>();
@ -74,6 +75,7 @@ const WorkList = () => {
}, },
]; ];
return ( return (
<div>
<Table <Table
columns={columns} columns={columns}
// indexColumn // indexColumn
@ -91,6 +93,13 @@ const WorkList = () => {
return fetchTableData(queryWorkList, params); return fetchTableData(queryWorkList, params);
}} }}
/> />
<WorkSelectModal
value={[1, 2, 3]}
onOk={function (val: any): void {
throw new Error('Function not implemented.');
}}
/>
</div>
); );
}; };

View File

@ -10,6 +10,108 @@ export default [
layout: false, layout: false,
component: './Login', component: './Login',
}, },
{
name: 'ALL',
path: RoutePath.ALL,
routes: [
{
path: RoutePath.ALL,
redirect: RoutePath.ALL_ADDRESS.LIST,
hideInMenu: true,
},
{
name: '地址管理',
path: RoutePath.ALL_ADDRESS.LIST,
component: './All/Address/List',
},
{
name: '地址详情',
path: RoutePath.ALL_ADDRESS.EDIT,
hideInMenu: true,
component: './All/Address/Edit',
},
{
name: '充值记录',
path: RoutePath.ALL_RECORD.LIST,
component: './All/Record/List',
},
{
name: '币种管理',
path: RoutePath.ALL_CURRENCY.LIST,
component: './Work/List',
},
{
name: '币种详情',
path: RoutePath.ALL_CURRENCY.EDIT,
hideInMenu: true,
component: './Work/Edit',
},
],
},
{
name: 'ETH',
path: RoutePath.ETH,
routes: [
{
path: RoutePath.ETH,
redirect: RoutePath.ETH_ADDRESS.LIST,
hideInMenu: true,
},
{
name: 'ETH地址管理',
path: RoutePath.ETH_ADDRESS.LIST,
component: './Eth/Address/List',
},
{
name: 'ETH地址详情',
path: RoutePath.ETH_ADDRESS.EDIT,
hideInMenu: true,
component: './Eth/Address/Edit',
},
{
name: 'ETH充值记录',
path: RoutePath.ETH_RECORD.LIST,
component: './Eth/Record/List',
},
{
name: 'ETH币种管理',
path: RoutePath.ETH_CURRENCY.LIST,
component: './Work/List',
},
{
name: 'ETH币种详情',
path: RoutePath.ETH_CURRENCY.EDIT,
hideInMenu: true,
component: './Work/Edit',
},
],
},
{
name: 'TRON',
path: RoutePath.TRON,
routes: [
{
path: RoutePath.TRON,
redirect: RoutePath.TRONRECORD.LIST,
hideInMenu: true,
},
{
name: 'TRON地址管理',
path: RoutePath.TRONADDRESS.LIST,
component: './Tron/Address/List',
},
{
name: 'TRON充值记录',
path: RoutePath.TRONRECORD.LIST,
component: './Tron/Record/List',
},
{
name: 'TRON币种类型',
path: RoutePath.TRONTOKEN.LIST,
component: './Tron/Token/List',
},
],
},
{ {
name: '设置', name: '设置',
path: RoutePath.SETTING, path: RoutePath.SETTING,
@ -35,7 +137,7 @@ export default [
}, },
{ {
path: '/', path: '/',
redirect: RoutePath.WORK.LIST, redirect: RoutePath.ETH_ADDRESS.LIST,
}, },
{ {

View File

@ -1,6 +1,46 @@
const SETTING = '/setting'; const SETTING = '/setting';
const ETH = '/eth';
const TRON = '/tron';
const ALL = '/all';
const RoutePath = { const RoutePath = {
LOGIN: '/login', LOGIN: '/login',
ALL: ALL,
ALL_ADDRESS: {
LIST: `${ALL}/address`,
EDIT: `${ALL}/address/edit`,
},
ALL_RECORD: {
LIST: `${ALL}/record`,
},
ALL_CURRENCY: {
LIST: `${ALL}/currency`,
EDIT: `${ALL}/currency/edit`,
},
ETH: ETH,
ETH_ADDRESS: {
LIST: `${ETH}/address`,
EDIT: `${ETH}/address/edit`,
},
ETH_RECORD: {
LIST: `${ETH}/record`,
},
ETH_CURRENCY: {
LIST: `${ETH}/currency`,
EDIT: `${ETH}/currency/edit`,
},
TRON: TRON,
TRONADDRESS: {
LIST: `${TRON}/tron/address/list`,
EDIT: `${TRON}/tron/address/edit`,
},
TRONRECORD: {
LIST: `${TRON}/tron/record`,
EDIT: `${TRON}/tron/record/edit`,
},
TRONTOKEN: {
LIST: `${TRON}/token`,
EDIT: `${TRON}/token/edit`,
},
SETTING: SETTING, SETTING: SETTING,
WORK: { WORK: {
LIST: `${SETTING}/work`, LIST: `${SETTING}/work`,

31
src/services/address.ts Normal file
View File

@ -0,0 +1,31 @@
import request from '@/utils/request';
// export const login = (data) => {
// return request.request({
// url: '/admin/login',
// method: 'post',
// data,
// });
// };
export const getAddressList = (data) => {
return request.request({
url: '/admin/api/v1/address/get',
method: 'post',
data,
});
};
export const getTronRecord = (data) => {
return request.request({
url: 'admin/api/v1/records/get',
method: 'post',
data,
});
};
export const creatTronAddress = (data) => {
return request.request({
url: 'admin/api/v1/address/mask',
method: 'post',
data,
});
};

25
src/services/eth.ts Normal file
View File

@ -0,0 +1,25 @@
import request from '@/utils/request';
export const queryAddressList = (data) => {
return request.request({
url: '/admin/api/v1/address/get',
method: 'post',
data,
});
};
export const AddAddress = (data) => {
return request.request({
url: '/admin/api/v1/address/mask',
method: 'post',
data,
});
};
export const queryRecordList = (data) => {
return request.request({
url: '/admin/api/v1/records/get',
method: 'post',
data,
});
};

View File

@ -1,8 +1,24 @@
import request from '@/utils/request'; import request from '@/utils/request';
// export const login = (data) => {
// return request.request({
// url: '/admin/login',
// method: 'post',
// data,
// });
// };
export const getData = (params) => {
return request.request({
url: '/admin/api/v1/sign',
method: 'get',
params,
});
};
export const login = (data) => { export const login = (data) => {
return request.request({ return request.request({
url: '/admin/login', url: '/admin/api/v1/login',
method: 'post', method: 'post',
data, data,
}); });

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));
};

View File

@ -6,7 +6,7 @@ import { CACHE_TOKEN } from '@/constants/cacheKey';
// create an axios instance // create an axios instance
const request = axios.create({ const request = axios.create({
baseURL: '', // baseURL: 'http://192.168.31.249:8888', //
timeout: 10000, // request timeout timeout: 10000, // request timeout
}); });
// request interceptor // request interceptor

View File

@ -9,8 +9,29 @@ export const fetchTableData = async (
delete params.current; delete params.current;
delete params.pageSize; delete params.pageSize;
const res = (await fetch(params)) || {}; const res = (await fetch(params)) || {};
const data = res.data; const data = res;
data?.forEach((n: any) => {
for (const key in formatObj) {
n[key] = n[formatObj[key]];
}
});
return {
success: true,
data: data,
total: res.total,
};
};
export const fetchTronTableData = async (
fetch: (params: any) => Promise<any>,
params: any,
formatObj: any = {},
) => {
params.page = params.page;
params.size = params.size;
params.coinType = 'tron';
const res = (await fetch(params)) || {};
const data = res;
data.forEach((n: any) => {});
data?.forEach((n: any) => { data?.forEach((n: any) => {
for (const key in formatObj) { for (const key in formatObj) {
n[key] = n[formatObj[key]]; n[key] = n[formatObj[key]];

30
src/utils/tronweb.ts Normal file
View File

@ -0,0 +1,30 @@
//@ts-ignore
import TronWeb from 'tronweb';
let tronWeb: any;
export function getTronWeb() {
return tronWeb;
}
//
// window.setTronRpc = setTronRpc;
export function setTronRpc(rpcUrl = 'https://api.trongrid.io', apiKey = '') {
const HttpProvider = TronWeb.providers.HttpProvider;
const fullNode = new HttpProvider(rpcUrl);
const solidityNode = new HttpProvider(rpcUrl);
const eventServer = new HttpProvider(rpcUrl);
// 这个私钥是随机生成的,可以自己生成
tronWeb = new TronWeb(
fullNode,
solidityNode,
eventServer,
'5B1E0F83361AD79F8B676CE205B50A6A036876D25F969F290F8017F87CAA9BA3',
);
tronWeb.setHeader({ 'TRON-PRO-API-KEY': apiKey });
}
export async function getTronBalance(account: any) {
return await tronWeb.trx.getBalance(account);
}

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

@ -0,0 +1,58 @@
import Web3 from 'web3';
import detectEthereumProvider from '@metamask/detect-provider';
import { getData } from '@/services/login';
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;
}
export async function walletSign() {
const res: any = await getData({});
// const data = '1234';
const rawData: any = web3.utils.fromUtf8(res);
const { result } = await provider.send('personal_sign', [
rawData,
web3.eth.defaultAccount!.toLowerCase(),
]);
return {
raw: res,
sign: result,
};
}

26572
yarn.lock

File diff suppressed because it is too large Load Diff