diff --git a/package.json b/package.json index e9317c5..7e9c237 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,11 @@ "tronweb": "^4.2.0", "umi": "^3.5.0", "umi-serve": "^1.9.10", - "web3": "^1.7.0" + "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": { "@ant-design/pro-cli": "^2.0.2", diff --git a/src/constants/enum/coinType.ts b/src/constants/enum/coinType.ts new file mode 100644 index 0000000..58343a1 --- /dev/null +++ b/src/constants/enum/coinType.ts @@ -0,0 +1,5 @@ +export enum CoinType { + ETH = 'eth', + TRON = 'tron', +} +export default CoinType; diff --git a/src/pages/All/Address/Edit/index.tsx b/src/pages/All/Address/Edit/index.tsx new file mode 100644 index 0000000..2300ac8 --- /dev/null +++ b/src/pages/All/Address/Edit/index.tsx @@ -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 ( + + +
+ + + + + + + 提交 + + +
+
+
+ ); +}; + +export default WorkEdit; diff --git a/src/pages/All/Address/List/components/AddAddressModal.tsx b/src/pages/All/Address/List/components/AddAddressModal.tsx new file mode 100644 index 0000000..835286f --- /dev/null +++ b/src/pages/All/Address/List/components/AddAddressModal.tsx @@ -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 ( + +
+ + + + +
+
+ ); +}; + +export default AddAddressModal; diff --git a/src/pages/All/Address/List/index.tsx b/src/pages/All/Address/List/index.tsx new file mode 100644 index 0000000..4380990 --- /dev/null +++ b/src/pages/All/Address/List/index.tsx @@ -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(); + const columns: ProColumns[] = [ + { + title: '类型', + dataIndex: 'coinType', + hideInTable: true, + initialValue: CoinType.ETH, + renderFormItem: (item, { type, defaultRender, ...rest }, form) => { + return ( + + ); + }, + }, + { + 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 ( +
+ { + 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; + }} + /> + + ); +}; + +export default AddressList; diff --git a/src/pages/Eth/Address/Edit/index.tsx b/src/pages/Eth/Address/Edit/index.tsx new file mode 100644 index 0000000..4946e4f --- /dev/null +++ b/src/pages/Eth/Address/Edit/index.tsx @@ -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 ( + + +
+ + + + + + + 提交 + + + +
+
+ ); +}; + +export default WorkEdit; diff --git a/src/pages/Eth/Address/List/components/AddAddressModal.tsx b/src/pages/Eth/Address/List/components/AddAddressModal.tsx new file mode 100644 index 0000000..835286f --- /dev/null +++ b/src/pages/Eth/Address/List/components/AddAddressModal.tsx @@ -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 ( + +
+ + + + + +
+ ); +}; + +export default AddAddressModal; diff --git a/src/pages/Eth/Address/List/index.tsx b/src/pages/Eth/Address/List/index.tsx index e69de29..221b14f 100644 --- a/src/pages/Eth/Address/List/index.tsx +++ b/src/pages/Eth/Address/List/index.tsx @@ -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(); + const columns: ProColumns[] = [ + { + title: '备注', + dataIndex: 'remarks', + hideInSearch: true, + width: '20%', + // hideInSearch: true, + }, + { + title: '地址', + dataIndex: 'address', + hideInSearch: true, + ellipsis: true, + }, + + { + title: 'key', + dataIndex: 'key', + hideInSearch: true, + }, + ]; + return ( +
+
{ + setIsModalVisible(true); + }, + }, + ]} + request={async (params) => { + params.coinType = CoinType.ETH; + return fetchTableData(queryAddressList, params); + }} + /> + { + 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); + }} + /> + + ); +}; + +export default AddressList; diff --git a/src/pages/Eth/Record/List/index.tsx b/src/pages/Eth/Record/List/index.tsx index e69de29..593279e 100644 --- a/src/pages/Eth/Record/List/index.tsx +++ b/src/pages/Eth/Record/List/index.tsx @@ -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(); + const columns: ProColumns[] = [ + { + 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 ( +
+
{ + 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; + }} + /> + + ); +}; + +export default AddressList; diff --git a/src/pages/Login/index.less b/src/pages/Login/index.less index 88b73ab..a44d4be 100644 --- a/src/pages/Login/index.less +++ b/src/pages/Login/index.less @@ -3,6 +3,7 @@ .container { display: flex; flex-direction: column; + align-items: center; height: 100vh; overflow: auto; background: @layout-body-background; @@ -23,6 +24,10 @@ } } +.button { + margin-top: 24px; +} + .content { flex: 1; padding: 32px 0; @@ -37,7 +42,8 @@ } .content { - padding: 100px 0 24px; + width: 40%; + padding: 300px 0 24px; } } diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx index b412865..b26c785 100644 --- a/src/pages/Login/index.tsx +++ b/src/pages/Login/index.tsx @@ -8,10 +8,11 @@ import styles from './index.less'; import { CACHE_TOKEN } from '@/constants/cacheKey'; import { initWeb3, walletSign } from '../../utils/web3'; +import { Button } from 'antd'; const Login: React.FC = () => { const intl = useIntl(); - const handleSubmit = async (values: API.LoginParams) => { + const handleSubmit = async () => { // 登录 await initWeb3(); const signInfo = await walletSign(); @@ -25,11 +26,23 @@ const Login: React.FC = () => { const { redirect } = query as { redirect: string }; history.push(redirect || '/'); }; - + const handleQRCodeSubmit = async () => {}; return (
- + 登录 + + + {/* } title={defaultSettings.title as string} initialValues={{ @@ -83,7 +96,7 @@ const Login: React.FC = () => { }, ]} /> - + */}
); diff --git a/src/pages/Work/List/index.tsx b/src/pages/Work/List/index.tsx index 0ad4609..a584a44 100644 --- a/src/pages/Work/List/index.tsx +++ b/src/pages/Work/List/index.tsx @@ -7,6 +7,7 @@ import RoutePath from '@/routes/routePath'; import { queryWorkList, deleteWork } from '@/services/work'; import { fetchTableData } from '@/utils/table'; import TimeText from '@/components/Typography/TimeText'; +import WorkSelectModal from '@/widget/Work/WorkSelectModal'; const WorkList = () => { const tableRef = useRef(); @@ -74,23 +75,31 @@ const WorkList = () => { }, ]; return ( -
{ - history.push(RoutePath.WORK.EDIT); +
+
{ + history.push(RoutePath.WORK.EDIT); + }, }, - }, - ]} - request={async (params) => { - return fetchTableData(queryWorkList, params); - }} - /> + ]} + request={async (params) => { + return fetchTableData(queryWorkList, params); + }} + /> + + ); }; diff --git a/src/routes/index.ts b/src/routes/index.ts index abd983e..359d2cd 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -10,45 +10,77 @@ export default [ layout: false, 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.ADDRESS.LIST, + redirect: RoutePath.ETH_ADDRESS.LIST, hideInMenu: true, }, { name: 'ETH地址管理', - path: RoutePath.ADDRESS.LIST, - component: './Work/List', + path: RoutePath.ETH_ADDRESS.LIST, + component: './Eth/Address/List', }, { name: 'ETH地址详情', - path: RoutePath.ADDRESS.EDIT, + path: RoutePath.ETH_ADDRESS.EDIT, hideInMenu: true, - component: './Work/Edit', + component: './Eth/Address/Edit', }, { name: 'ETH充值记录', - path: RoutePath.RECORD.LIST, - component: './Work/List', - }, - { - name: 'ETH充值详情', - path: RoutePath.RECORD.EDIT, - hideInMenu: true, - component: './Work/Edit', + path: RoutePath.ETH_RECORD.LIST, + component: './Eth/Record/List', }, { name: 'ETH币种管理', - path: RoutePath.CURRENCY.LIST, + path: RoutePath.ETH_CURRENCY.LIST, component: './Work/List', }, { name: 'ETH币种详情', - path: RoutePath.CURRENCY.EDIT, + path: RoutePath.ETH_CURRENCY.EDIT, hideInMenu: true, component: './Work/Edit', }, @@ -105,7 +137,7 @@ export default [ }, { path: '/', - redirect: RoutePath.WORK.LIST, + redirect: RoutePath.ETH_ADDRESS.LIST, }, { diff --git a/src/routes/routePath.ts b/src/routes/routePath.ts index cc65633..0105ac5 100644 --- a/src/routes/routePath.ts +++ b/src/routes/routePath.ts @@ -1,18 +1,30 @@ const SETTING = '/setting'; const ETH = '/eth'; const TRON = '/tron'; +const ALL = '/all'; const RoutePath = { 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, - ADDRESS: { + ETH_ADDRESS: { LIST: `${ETH}/address`, EDIT: `${ETH}/address/edit`, }, - RECORD: { + ETH_RECORD: { LIST: `${ETH}/record`, - EDIT: `${ETH}/record/edit`, }, - CURRENCY: { + ETH_CURRENCY: { LIST: `${ETH}/currency`, EDIT: `${ETH}/currency/edit`, }, diff --git a/src/services/eth.ts b/src/services/eth.ts new file mode 100644 index 0000000..d794879 --- /dev/null +++ b/src/services/eth.ts @@ -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, + }); +}; diff --git a/src/utils/bigNumber.ts b/src/utils/bigNumber.ts new file mode 100644 index 0000000..2c96fcf --- /dev/null +++ b/src/utils/bigNumber.ts @@ -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); diff --git a/src/utils/formatBalance.ts b/src/utils/formatBalance.ts new file mode 100644 index 0000000..8cbe1b5 --- /dev/null +++ b/src/utils/formatBalance.ts @@ -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)); +}; diff --git a/src/utils/table.ts b/src/utils/table.ts index 393a477..8e60598 100644 --- a/src/utils/table.ts +++ b/src/utils/table.ts @@ -9,8 +9,7 @@ export const fetchTableData = async ( delete params.current; delete params.pageSize; 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]]; diff --git a/src/utils/web3.ts b/src/utils/web3.ts index b64adcb..d5f47a7 100644 --- a/src/utils/web3.ts +++ b/src/utils/web3.ts @@ -1,6 +1,7 @@ import Web3 from 'web3'; import detectEthereumProvider from '@metamask/detect-provider'; import { getData } from '@/services/login'; +import { message } from 'antd'; let provider: any; @@ -28,14 +29,14 @@ export async function initWeb3() { } catch (reason) { switch (reason) { case 'Already processing eth_requestAccounts. Please wait.': // 已有请求存在 - console.log('请打开MetaMask完成授权'); + message.warning('请打开MetaMask完成授权'); break; case 'User rejected provider access': //如果用户拒绝了登录请求 - console.log('请同意登录请求'); + message.warning('请同意登录请求'); break; default: // 本不该执行到这里,但是真到这里了,说明发生了意外 - console.log('登录出错!err:' + reason); + message.warning('登录出错!err:' + reason); break; } }