-
4.env
-
5env-temp
-
11package.json
-
BINpublic/favicon.ico
-
2public/index.html
-
BINpublic/login192.png
-
BINpublic/login512.png
-
BINpublic/logo192.png
-
BINpublic/logo512.png
-
15src/App.tsx
-
BINsrc/assets/_line2.png
-
BINsrc/assets/login.jpg
-
BINsrc/assets/login.png
-
BINsrc/assets/root.png
-
27src/components/Button.tsx
-
71src/components/CountdownTimer.tsx
-
302src/components/MyTable/index.tsx
-
49src/components/MyTable/tableHook.tsx
-
78src/components/SearchForm/index.tsx
-
151src/components/layout/Header.tsx
-
42src/components/layout/Slider.tsx
-
111src/components/profitChart.tsx
-
49src/http/api.ts
-
31src/http/axios_config.ts
-
65src/http/service.ts
-
20src/http/sign.ts
-
38src/http/sort.js
-
66src/http/types.ts
-
113src/pages/account/index.tsx
-
97src/pages/create-proxy/index.tsx
-
203src/pages/deposit/index.tsx
-
50src/pages/forget/index.tsx
-
103src/pages/home/index.tsx
-
51src/pages/link/index.tsx
-
93src/pages/login/index.tsx
-
53src/pages/personal/index.tsx
-
216src/pages/record/assets/index.tsx
-
111src/pages/record/bonus/index.tsx
-
75src/pages/record/deposit/index.tsx
-
67src/pages/record/escrow/index.tsx
-
81src/pages/record/withdraw/index.tsx
-
75src/pages/security/index.tsx
-
78src/pages/submit/index.tsx
-
110src/pages/team/index.tsx
-
73src/pages/transfer/index.tsx
-
95src/pages/withdraw/index.tsx
-
43src/router/index.tsx
-
3src/router/renderRouter.tsx
-
46src/router/routes.tsx
-
38src/store/index.ts
-
12src/styles/app.scss
-
4src/styles/components.scss
-
23src/styles/global.scss
-
114src/styles/home.scss
-
10src/styles/login.scss
-
27src/types/index.ts
-
47src/utils/index.ts
-
68yarn.lock
@ -0,0 +1,4 @@ |
|||
SKIP_PREFLIGHT_CHECK=true |
|||
GENERATE_SOURCEMAP=false |
|||
REACT_APP_BASEURL='https://matontrading.com' |
|||
REACT_APP_ORIGIN='https://matontrading.com' |
@ -0,0 +1,5 @@ |
|||
REACT_APP_BASEURL='https://matontrading.com' |
|||
REACT_APP_ORIGIN='https://matontrading.com' |
|||
|
|||
REACT_APP_BASEURL='http://203.161.61.234:8082' |
|||
REACT_APP_ORIGIN='http://162.254.37.253:8086' |
After Width: 564 | Height: 442 | Size: 100 KiB |
After Width: 564 | Height: 442 | Size: 100 KiB |
Before Width: 192 | Height: 192 | Size: 5.2 KiB |
Before Width: 512 | Height: 512 | Size: 9.4 KiB |
After Width: 41 | Height: 41 | Size: 178 B |
Before Width: 200 | Height: 200 | Size: 5.8 KiB |
After Width: 564 | Height: 442 | Size: 100 KiB |
After Width: 24 | Height: 22 | Size: 1.7 KiB |
@ -1,19 +1,28 @@ |
|||
import { forwardRef } from 'react'; |
|||
import '../styles/components.scss' |
|||
|
|||
interface ButtonProps { |
|||
children?: string | JSX.Element; |
|||
className?: string; |
|||
style?: React.CSSProperties, |
|||
onClick?: Function |
|||
style?: React.CSSProperties; |
|||
onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void; |
|||
htmlType?: "button" | "submit" | "reset" | undefined |
|||
} |
|||
|
|||
const Button = (props: ButtonProps) => { |
|||
|
|||
const { children, style, className, onClick } = props |
|||
const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => { |
|||
const { children, style, className, onClick, htmlType = "button" } = props; |
|||
|
|||
return ( |
|||
<div onClick={() => onClick && onClick()} className={`default-button ${className}`} style={style}>{children}</div> |
|||
) |
|||
} |
|||
<button |
|||
ref={ref} |
|||
type={htmlType} |
|||
onClick={onClick} |
|||
className={`default-button ${className}`} |
|||
style={style} |
|||
> |
|||
{children} |
|||
</button> |
|||
); |
|||
}); |
|||
|
|||
export default Button |
|||
export default Button; |
@ -0,0 +1,71 @@ |
|||
import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react'; |
|||
|
|||
interface CountdownTimerProps { |
|||
initialSeconds: number, |
|||
onComplete?: Function |
|||
} |
|||
|
|||
export interface CountdownTimerRef { |
|||
handleStop: Function, |
|||
handleStart: Function, |
|||
handleReset: Function, |
|||
isActive: boolean |
|||
} |
|||
|
|||
const CountdownTimer = forwardRef<CountdownTimerRef, CountdownTimerProps>((props, ref) => { |
|||
|
|||
const { initialSeconds, onComplete } = props |
|||
|
|||
const [seconds, setSeconds] = useState(initialSeconds); |
|||
const [isActive, setIsActive] = useState(false); |
|||
const [title, setTitle] = useState('发送验证码') |
|||
|
|||
useImperativeHandle(ref, () => ({ |
|||
handleStop, |
|||
handleReset, |
|||
handleStart, |
|||
isActive |
|||
})) |
|||
|
|||
const handleStop = () => { |
|||
setIsActive(false); |
|||
setTitle('发送验证码') |
|||
}; |
|||
|
|||
const handleStart = () => { |
|||
if (isActive) return |
|||
setSeconds(initialSeconds) |
|||
setIsActive(true); |
|||
}; |
|||
|
|||
const handleReset = () => { |
|||
setSeconds(initialSeconds); |
|||
setIsActive(true); |
|||
}; |
|||
|
|||
useEffect(() => { |
|||
let interval: any = null; |
|||
console.log(seconds); |
|||
console.log(isActive); |
|||
|
|||
if (isActive && seconds > 0) { |
|||
interval = setInterval(() => { |
|||
setSeconds(seconds => seconds - 1); |
|||
}, 1000); |
|||
} else { |
|||
clearInterval(interval) |
|||
seconds <= 0 && setTitle('重新发送') |
|||
setIsActive(false) |
|||
// onComplete && onComplete(seconds)
|
|||
} |
|||
console.log(1); |
|||
|
|||
return () => clearInterval(interval); |
|||
}, [isActive, seconds, onComplete]); |
|||
|
|||
return ( |
|||
<div>{isActive ? <span className='fz-18 fw550'>{seconds}s</span> : title}</div> |
|||
); |
|||
}); |
|||
|
|||
export default CountdownTimer; |
@ -0,0 +1,302 @@ |
|||
import React, { |
|||
useState, |
|||
forwardRef, |
|||
useImperativeHandle, |
|||
useRef, |
|||
ReactNode, |
|||
FC |
|||
} from 'react' |
|||
import { Table } from 'antd' |
|||
import useService from './tableHook' |
|||
import SearchView from '../SearchForm' |
|||
|
|||
/** |
|||
* 封装列表、分页、多选、搜索组件 |
|||
* @param {RefType} ref 表格的实例,用于调用内部方法 |
|||
* @param {object[]} columns 表格列的配置 |
|||
* @param {function} apiFun 表格数据的请求方法 |
|||
* @param {object[]} searchConfigList 搜索栏配置 |
|||
* @param {function} beforeSearch 搜索前的操作(如处理一些特殊数据) |
|||
* @param {function} onFieldsChange 处理搜索栏表单联动事件 |
|||
* @param {object} extraProps 额外的搜索参数(不在搜索配置内的) |
|||
* @param {function} onSelectRow 复选框操作回调 |
|||
* @param {string} rowKey 表格行的key |
|||
* @param {function} sortConfig 自定义表格排序字段 |
|||
* @param {function} expandedRowRender 额外的展开行 |
|||
* @param {function} onExpand 点击展开图标时触发 |
|||
* @param {string} rowClassName 表格行的样式名 |
|||
* @param {boolean} small 表格和分页的展示大小 |
|||
* @param {string[]} extraPagation 额外的分页大小 |
|||
*/ |
|||
|
|||
interface TableProps { |
|||
columns: object[]; |
|||
apiFun: (arg0?: unknown[]) => Promise<{}>; |
|||
ref?: any; |
|||
searchConfigList?: object[]; |
|||
extraProps?: object; |
|||
rowKey?: string; |
|||
rowClassName?: string; |
|||
small?: boolean; |
|||
showHeader?: boolean; |
|||
extraPagation?: string[]; |
|||
beforeSearch?: (arg0?: unknown) => void; |
|||
onSelectRow?: (arg0?: string[], arg1?: string[]) => void; |
|||
onFieldsChange?: (arg0?: unknown, arg1?: unknown) => void; |
|||
sortConfig?: (arg0?: object) => any; |
|||
expandedRowRender?: () => ReactNode; |
|||
onExpand?: () => void; |
|||
header?: JSX.Element |
|||
} |
|||
|
|||
const MyTable: FC<TableProps> = forwardRef( |
|||
(props: TableProps, ref: any) => { |
|||
/** |
|||
* @forwardRef |
|||
* 引用父组件的ref实例,成为子组件的一个参数 |
|||
* 可以引用父组件的ref绑定到子组件自身的节点上. |
|||
*/ |
|||
const searchForm: any = useRef(null) |
|||
const { |
|||
columns, |
|||
apiFun, |
|||
searchConfigList, |
|||
extraProps, |
|||
rowKey, |
|||
rowClassName, |
|||
small, |
|||
showHeader, |
|||
extraPagation, |
|||
beforeSearch, |
|||
onSelectRow, |
|||
onFieldsChange, |
|||
sortConfig, |
|||
expandedRowRender, |
|||
onExpand, |
|||
header |
|||
} = props |
|||
|
|||
// 搜索参数,如果有特殊需要处理的参数,就处理
|
|||
const searchObj = searchConfigList && searchConfigList.reduce( |
|||
(prev: any, next: any) => { |
|||
return Object.assign(prev, { |
|||
[next.key]: next.fn ? next.fn(next.initialValue) : next.initialValue |
|||
}) |
|||
}, |
|||
{} |
|||
) |
|||
|
|||
// 初始参数
|
|||
const initParams = { |
|||
...searchObj, |
|||
...extraProps, |
|||
page: 1, |
|||
page_size: 20 |
|||
} |
|||
|
|||
// 多选框的选择值
|
|||
const [selectedKeys, setSelectedKeys] = useState([]) |
|||
// 列表所有的筛选参数(包括搜索、分页、排序等)
|
|||
const [tableParams, setTableParams] = useState(initParams) |
|||
// 列表搜索参数
|
|||
const [searchParams, setSearchParams] = useState(searchObj) |
|||
// 列表排序参数
|
|||
const [sortParams, setSortParams] = useState({}) |
|||
// 列表分页参数
|
|||
const [curPageNo, setCurPageNo] = useState(initParams.page) |
|||
const [curPageSize, setCurPageSize] = useState(initParams.page_size) |
|||
|
|||
const { loading = false, response = {} }: any = useService( |
|||
apiFun, |
|||
tableParams |
|||
); |
|||
|
|||
let tableData = [] as any[]; |
|||
let total = 0; |
|||
const validData = response?.data ? response.data : []; |
|||
if (Array.isArray(validData) && validData.length >= 0) { |
|||
tableData = validData; |
|||
total = validData.length; |
|||
} |
|||
|
|||
if (validData.rows) { |
|||
tableData = validData.rows; |
|||
total = validData.total; |
|||
} |
|||
|
|||
// const validData = response?.data ? response.data : {}
|
|||
// const { rows: tableData = [], total } = validData
|
|||
|
|||
// 执行搜索操作
|
|||
const handleSearch = (val: any): void => { |
|||
if (val.login) { |
|||
val.login = Number(val.login) |
|||
} |
|||
|
|||
const obj = { |
|||
...val |
|||
} |
|||
if (Array.isArray(obj.chain_id)) { |
|||
obj.chain_id = Number(obj.chain_id[0]) |
|||
} |
|||
|
|||
setSearchParams(obj) |
|||
|
|||
setTableParams({ ...tableParams, ...obj, page: 1 }) |
|||
} |
|||
|
|||
// 重置列表部分状态
|
|||
const resetAction = (page?: number): void => { |
|||
setSelectedKeys([]) |
|||
const nextPage = page || curPageNo |
|||
const nextParmas = page === 1 ? {} : { ...searchParams, ...sortParams } |
|||
setCurPageNo(nextPage) |
|||
setTableParams({ |
|||
...initParams, |
|||
...nextParmas, |
|||
page: nextPage, |
|||
page_size: curPageSize |
|||
}) |
|||
} |
|||
|
|||
// 列表复选框选中变化
|
|||
const onSelectChange = ( |
|||
selectedRowKeys: any[], |
|||
selectedRows: any[] |
|||
): void => { |
|||
setSelectedKeys(selectedRowKeys as any) |
|||
onSelectRow && onSelectRow(selectedRowKeys, selectedRows) |
|||
} |
|||
// 复选框配置
|
|||
const rowSelection = { |
|||
selectedRowKeys: selectedKeys, |
|||
onChange: onSelectChange |
|||
} |
|||
// 判断是否有复选框显示
|
|||
const showCheckbox = onSelectRow ? { rowSelection } : {} |
|||
|
|||
// 展开配置
|
|||
const expendConfig = { |
|||
expandedRowRender, |
|||
onExpand, |
|||
rowClassName |
|||
} |
|||
// 判断是否有展开行
|
|||
const showExpend = expandedRowRender ? expendConfig : {} |
|||
|
|||
// 表格和分页的大小
|
|||
const tableSize = small ? 'small' : 'middle' |
|||
const pagationSize = small ? 'small' : 'default' |
|||
|
|||
// 分页、筛选、排序变化时触发
|
|||
const onTableChange = ( |
|||
pagination: any, |
|||
filters: any, |
|||
sorter: object |
|||
): void => { |
|||
// 如果有sort排序并且sort参数改变时,优先排序
|
|||
const sortObj = sortConfig ? sortConfig(sorter) : {} |
|||
setSortParams(sortObj) |
|||
|
|||
const { current: page, pageSize } = pagination |
|||
|
|||
setCurPageNo(page) |
|||
setCurPageSize(pageSize) |
|||
setTableParams({ |
|||
...initParams, |
|||
...searchParams, |
|||
...sortObj, |
|||
page, |
|||
page_size: pageSize |
|||
}) |
|||
} |
|||
|
|||
/** |
|||
* @useImperativeHandle |
|||
* 第一个参数,接收一个通过forwardRef引用父组件的ref实例 |
|||
* 第二个参数一个回调函数,返回一个对象,对象里面存储需要暴露给父组件的属性或方法 |
|||
*/ |
|||
useImperativeHandle(ref, () => ({ |
|||
// 更新列表
|
|||
update(page?: number): void { |
|||
resetAction(page) |
|||
}, |
|||
// 更新列表,并重置搜索字段
|
|||
resetForm(page?: number): void { |
|||
if (searchForm.current) searchForm.current.resetFields() |
|||
setSearchParams({}) |
|||
resetAction(page) |
|||
}, |
|||
// 仅重置搜索字段
|
|||
resetField(field?: string[]): void { |
|||
return field |
|||
? searchForm.current.resetFields([...field]) |
|||
: searchForm.current.resetFields() |
|||
}, |
|||
// 获取当前列表数据
|
|||
getTableData(): any[] { |
|||
return tableData |
|||
} |
|||
})) |
|||
|
|||
return ( |
|||
<div> |
|||
{/* 搜索栏 */} |
|||
{searchConfigList && searchConfigList.length > 0 && ( |
|||
<div style={{ display: 'flex', justifyContent: 'space-between' }}> |
|||
<SearchView |
|||
ref={searchForm} |
|||
config={searchConfigList} |
|||
beforeSearch={beforeSearch} |
|||
handleSearch={handleSearch} |
|||
onFieldsChange={onFieldsChange} |
|||
/> |
|||
{header} |
|||
</div> |
|||
)} |
|||
{/* 列表 */} |
|||
<Table |
|||
// {...showCheckbox}
|
|||
{...showExpend} |
|||
rowKey={rowKey} |
|||
loading={loading} |
|||
dataSource={tableData} |
|||
columns={columns} |
|||
onChange={onTableChange} |
|||
size={tableSize} |
|||
showHeader={showHeader} |
|||
style={{ minWidth: 1000 }} |
|||
pagination={{ |
|||
size: pagationSize, |
|||
total, |
|||
pageSize: tableParams.page_size, |
|||
current: tableParams.page, |
|||
showQuickJumper: true, |
|||
showSizeChanger: true, |
|||
pageSizeOptions: ['20', '50', '100', '200', ...extraPagation as any], |
|||
showTotal: (all) => `共 ${all} 条`, |
|||
}} |
|||
/> |
|||
</div> |
|||
) |
|||
} |
|||
) |
|||
|
|||
MyTable.defaultProps = { |
|||
searchConfigList: [], |
|||
ref: null, |
|||
extraProps: {}, |
|||
rowKey: 'id', |
|||
rowClassName: '', |
|||
small: false, |
|||
showHeader: true, |
|||
extraPagation: [], |
|||
beforeSearch: () => { }, |
|||
onSelectRow: () => { }, |
|||
onFieldsChange: () => { }, |
|||
sortConfig: () => { }, |
|||
expandedRowRender: null as any, |
|||
onExpand: () => { } |
|||
} |
|||
|
|||
export default MyTable |
@ -0,0 +1,49 @@ |
|||
/** |
|||
* tableHook.js 用于处理 Table组件的分页事件 |
|||
* 自定义的一个 server hook,该 hook 封装了 ajax 请求中的 { loading, error, response } 三个基础逻辑; |
|||
* 有了这个 hook 我们就能很轻松的在每次网络请求里面去处理各种异常逻辑了 |
|||
*/ |
|||
import { useEffect, useState, useCallback } from 'react' |
|||
|
|||
export const useServiceCallback = ( |
|||
service: (arg0?: any) => Promise<{}> |
|||
): any[] => { |
|||
const [loading, setLoading] = useState(false) |
|||
const [error, setError] = useState(null) |
|||
const [response, setResponse] = useState(null as any) |
|||
|
|||
// 使用 useCallback,来判断 service 是否改变
|
|||
const callback = useCallback( |
|||
(params: any) => { |
|||
|
|||
setLoading(true) |
|||
setError(null) |
|||
service(params) |
|||
.then((res) => { |
|||
setLoading(false) |
|||
setResponse(res) |
|||
}) |
|||
.catch(() => { |
|||
setLoading(false) |
|||
}) |
|||
}, |
|||
[service] |
|||
) |
|||
return [callback, { loading, error, response }] |
|||
} |
|||
|
|||
const useService = ( |
|||
service: (arg0?: any) => Promise<{}>, |
|||
params?: any |
|||
): object => { |
|||
const [callback, { loading, error, response }]: any[] = useServiceCallback( |
|||
service |
|||
) |
|||
useEffect(() => { |
|||
callback(params) |
|||
return () => { } |
|||
}, [callback, params]) |
|||
return { loading, error, response } |
|||
} |
|||
|
|||
export default useService |
@ -0,0 +1,78 @@ |
|||
import React, { forwardRef, useImperativeHandle, FC } from 'react' |
|||
import { Form, Button } from 'antd' |
|||
|
|||
interface SearchProps { |
|||
config: object[]; |
|||
handleSearch: (arg0?: object) => void; |
|||
ref: any; |
|||
beforeSearch?: (arg0?: object) => void; |
|||
onFieldsChange?: (arg0?: unknown, arg1?: unknown) => void; |
|||
} |
|||
|
|||
const SearchForm: FC<SearchProps> = forwardRef( |
|||
(props: SearchProps, ref) => { |
|||
const { config, handleSearch, beforeSearch, onFieldsChange } = props |
|||
const [form] = Form.useForm() |
|||
const getFields = (): JSX.Element[] => { |
|||
return config.map((item: any) => { |
|||
return ( |
|||
<Form.Item |
|||
label={item.label} |
|||
key={item.key} |
|||
name={item.key} |
|||
rules={item.rules} |
|||
style={{ marginBottom: '6px' }} |
|||
> |
|||
{item.slot} |
|||
</Form.Item> |
|||
) |
|||
}) |
|||
} |
|||
|
|||
const emitSearch = (values: object): void => { |
|||
// beforeSearch用于处理一些特殊情况
|
|||
beforeSearch && beforeSearch(values) |
|||
handleSearch(values) |
|||
} |
|||
|
|||
const initialValues = config.reduce( |
|||
(prev: any, next: any) => ({ |
|||
...prev, |
|||
[next.key]: next.initialValue |
|||
}), |
|||
{} |
|||
) |
|||
|
|||
useImperativeHandle(ref, () => ({ |
|||
// 重置搜索字段
|
|||
resetFields(field: string[]) { |
|||
return field ? form.resetFields([...field]) : form.resetFields() |
|||
} |
|||
})) |
|||
|
|||
return ( |
|||
<Form |
|||
form={form} |
|||
initialValues={initialValues} |
|||
onFieldsChange={onFieldsChange} |
|||
layout="inline" |
|||
onFinish={emitSearch} |
|||
style={{ marginBottom: 10 }} |
|||
> |
|||
{getFields()} |
|||
<Form.Item> |
|||
<Button htmlType="submit" type="primary"> |
|||
搜索 |
|||
</Button> |
|||
</Form.Item> |
|||
</Form> |
|||
) |
|||
} |
|||
) |
|||
|
|||
SearchForm.defaultProps = { |
|||
beforeSearch: () => { }, |
|||
onFieldsChange: () => { } |
|||
} |
|||
|
|||
export default SearchForm |
@ -0,0 +1,111 @@ |
|||
import React, { useEffect, useRef } from 'react'; |
|||
import ReactECharts from 'echarts-for-react'; |
|||
import echarts from 'echarts/types/dist/echarts'; |
|||
|
|||
const ProfitChart = (props: any) => { |
|||
|
|||
const { data } = props |
|||
const reactECharts = useRef<ReactECharts>(null) |
|||
|
|||
const option = { |
|||
title: { |
|||
text: '我的收益走势', |
|||
left: 'left', |
|||
textStyle: { |
|||
color: '#333', |
|||
fontSize: 14, |
|||
fontWeight: 'normal' |
|||
} |
|||
}, |
|||
grid: { |
|||
top: '15%', |
|||
left: '0%', |
|||
right: '3%', |
|||
bottom: '15%', |
|||
containLabel: true |
|||
}, |
|||
tooltip: { |
|||
trigger: 'axis', |
|||
formatter: '{b0}<br />Profit: {c0}', |
|||
backgroundColor: 'rgba(255, 255, 255, 0.9)', |
|||
borderColor: '#ccc', |
|||
borderWidth: 1, |
|||
textStyle: { |
|||
color: '#000' |
|||
}, |
|||
axisPointer: { |
|||
type: 'line' |
|||
} |
|||
}, |
|||
xAxis: { |
|||
type: 'category', |
|||
data: data.times, |
|||
axisLine: { |
|||
lineStyle: { |
|||
color: '#ccc' |
|||
} |
|||
}, |
|||
axisTick: { |
|||
alignWithLabel: true |
|||
} |
|||
}, |
|||
yAxis: { |
|||
type: 'value', |
|||
axisLine: { |
|||
lineStyle: { |
|||
color: '#ccc' |
|||
} |
|||
}, |
|||
splitLine: { |
|||
lineStyle: { |
|||
type: 'dashed', |
|||
color: '#ddd' |
|||
} |
|||
} |
|||
}, |
|||
series: [ |
|||
{ |
|||
name: 'Profit', |
|||
type: 'line', |
|||
smooth: true, |
|||
data: data.values, |
|||
symbol: 'circle', |
|||
symbolSize: 8, |
|||
itemStyle: { |
|||
color: '#5470c6', |
|||
borderColor: '#fff', |
|||
borderWidth: 2 |
|||
}, |
|||
lineStyle: { |
|||
width: 10, |
|||
}, |
|||
emphasis: { |
|||
itemStyle: { |
|||
borderColor: '#5470c6', |
|||
borderWidth: 3 |
|||
} |
|||
}, |
|||
showAllSymbol: true, // 显示所有数据点
|
|||
} |
|||
] |
|||
}; |
|||
|
|||
useEffect(() => { |
|||
|
|||
const handleResize = () => { |
|||
if (!reactECharts.current) return |
|||
const instance = reactECharts.current.getEchartsInstance() |
|||
instance.resize() |
|||
} |
|||
window.addEventListener('resize', handleResize) |
|||
return () => { |
|||
window.removeEventListener('resize', handleResize) |
|||
} |
|||
}, []) |
|||
|
|||
return ( |
|||
<ReactECharts option={option} style={{ height: '100%', width: '100%' }} ref={reactECharts} /> |
|||
); |
|||
}; |
|||
|
|||
export default ProfitChart; |
@ -0,0 +1,49 @@ |
|||
import request from './service' |
|||
import { HttpRequestPs } from './types' |
|||
|
|||
export const http_register = (data: HttpRequestPs.Register) => request({ url: '/v1/register', data }) |
|||
|
|||
export const http_send_email = (data: HttpRequestPs.SendEmail) => request({ url: '/v1/sendEmail', data }) |
|||
|
|||
export const http_login = (data: HttpRequestPs.Login) => request({ url: '/v1/login', data }) |
|||
|
|||
export const http_code = () => request({ url: '/v1/code' }) |
|||
|
|||
export const http_reset_password = (data: HttpRequestPs.ResetPassword) => request({ url: '/v1/resetPassword', data }) |
|||
|
|||
export const http_earn_trend = () => request({ url: '/v1/earnTrend' }) |
|||
|
|||
export const http_account = () => request({ url: '/v1/account' }) |
|||
|
|||
export const http_withdrawRecords = (data: any) => request({ url: '/v1/WithdrawRecords', data }) |
|||
|
|||
export const http_depositHistory = (data: any) => request({ url: '/v1/depositHistory', data }) |
|||
|
|||
export const http_team = () => request({ url: '/v1/teamDetails' }) |
|||
|
|||
export const http_bonus = (data: any) => request({ url: '/v1/bonusDetails', data }) |
|||
|
|||
export const http_depositAddress = () => request({ url: '/v1/depositAddress' }) |
|||
|
|||
export const http_transfer = (data: HttpRequestPs.Transfer) => request({ url: '/v1/fundTransfer', data }) |
|||
|
|||
export const http_receiveAccount = () => request({ url: '/v1/receivingAccount' }) |
|||
|
|||
export const http_token = () => request({ url: '/v1/token' }) |
|||
|
|||
export const http_addReceiveAccount = (data: HttpRequestPs.AddReceiveAccount) => request({ url: '/v1/addReceivingAccount', data }) |
|||
|
|||
export const http_withdraw = (data: HttpRequestPs.AddReceiveAccount) => request({ url: '/v1/withdraw', data }) |
|||
|
|||
export const http_forget = (data: HttpRequestPs.Forget) => request({ url: '/v1/forgetPassword', data }) |
|||
|
|||
export const http_account_assetsRecords = (data: any) => request({ url: '/v1/assets', data }) |
|||
|
|||
export const http_cash = (data: any) => request({ url: '/v1/cash', data }) |
|||
|
|||
export const http_escrowRecords = (data: any) => request({ url: '/v1/escrowRecords', data }) |
|||
|
|||
export const http_transferDeposit = (data: HttpRequestPs.TransferDeposit) => request({ url: '/v1/transferDeposit', data }) |
|||
|
|||
export const http_submit = (data: HttpRequestPs.Submit) => request({ url: '/v1/submitData', data }) |
|||
|
@ -0,0 +1,31 @@ |
|||
export default { |
|||
// baseURL: 'http://203.161.61.234:8082/api',
|
|||
baseURL: `${process.env.REACT_APP_BASEURL}/api`, |
|||
method: 'post', |
|||
//`timeout`选项定义了请求发出的延迟毫秒数
|
|||
//如果请求花费的时间超过延迟的时间,那么请求会被终止
|
|||
timeout: 60 * 1000, |
|||
//发送请求前允许修改数据
|
|||
transformRequest: [function (data: any) { |
|||
return data; |
|||
}], |
|||
//数据发送到then/catch方法之前允许数据改动
|
|||
transformResponse: [function (data: any) { |
|||
return data; |
|||
}], |
|||
// headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|||
headers: { 'Content-Type': 'application/json; charset=UTF-8' }, |
|||
// withCredentials: false,//跨域请求时是否携带cookie
|
|||
responseType: 'json',//响应数据类型
|
|||
// xsrfCookieName: 'XSRF-TOKEN',
|
|||
// xsrfHeaderName: 'X-XSRF-TOKEN',
|
|||
// onUploadProgress: function (progressEvent: any) { },//上传进度事件
|
|||
// onDownloadProgress: function (progressEvent: any) { },//下载进度事件
|
|||
//`validateStatus`定义了是否根据http相应状态码,来resolve或者reject promise
|
|||
//如果`validateStatus`返回true(或者设置为`null`或者`undefined`),那么promise的状态将会是resolved,否则其状态就是rejected
|
|||
validateStatus: function (status: number) { |
|||
return status >= 200 && status < 300 // 默认的
|
|||
}, |
|||
//`maxRedirects`定义了在nodejs中重定向的最大数量
|
|||
maxRedirects: 5 |
|||
} as any; |
@ -0,0 +1,65 @@ |
|||
import axiosConfig from './axios_config'; |
|||
import axios from 'axios'; |
|||
import sortParam from './sort'; |
|||
import signGenerator from './sign'; |
|||
import qs from 'qs'; |
|||
import store from '../store'; |
|||
import { notification } from 'antd'; |
|||
|
|||
const service = axios.create(axiosConfig); |
|||
|
|||
// 请求拦截
|
|||
service.interceptors.request.use( |
|||
config => { |
|||
let ps = config.params ? sortParam(config.params) : ''; |
|||
let timestamp = new Date().getTime(); |
|||
let signData = { |
|||
uri: '/api' + config.url, |
|||
timestamp: timestamp, |
|||
args: ps, |
|||
}; |
|||
|
|||
let sign = signGenerator(signData); |
|||
(config.headers as any).sign = sign; |
|||
(config.headers as any).timestamp = timestamp; |
|||
(config.headers as any).Token = store.state.token; |
|||
if (config.data) { |
|||
config.data = JSON.stringify(config.data); |
|||
} |
|||
if (config.method?.toLocaleLowerCase() === 'get') { |
|||
config.paramsSerializer = function (params) { |
|||
return qs.stringify(params, { arrayFormat: 'repeat' }); |
|||
}; |
|||
} |
|||
return config; |
|||
}, |
|||
error => { |
|||
return Promise.reject(error); |
|||
}, |
|||
); |
|||
|
|||
// 响应拦截
|
|||
service.interceptors.response.use( |
|||
(res: any) => { |
|||
|
|||
let data = JSON.parse(res.data); |
|||
if (data && data.code !== 0) { |
|||
notification.warning({ |
|||
message: data.msg |
|||
}) |
|||
} |
|||
if (data && data.code === 101) { |
|||
store.setToken('') |
|||
} |
|||
return data; |
|||
}, |
|||
error => { |
|||
if (error && error.response && error.response.data) { |
|||
let data = JSON.parse(error.response.data); |
|||
return data; |
|||
} |
|||
return error |
|||
}, |
|||
); |
|||
|
|||
export default service; |
@ -0,0 +1,20 @@ |
|||
const md5 = require('js-md5'); |
|||
var signkey = 'asfdnlnilsjbnlasskdbnf'; |
|||
|
|||
var signGenerator = (data: any) => { |
|||
var keys: any[] = []; |
|||
for (var key in data) { |
|||
keys.push(key); |
|||
} |
|||
keys.sort(); |
|||
|
|||
var ptxt = ''; |
|||
for (var i = 0; i < keys.length; i++) { |
|||
ptxt += keys[i] + data[keys[i]]; |
|||
} |
|||
ptxt = signkey + ptxt + signkey; |
|||
var signval = md5(ptxt).toLowerCase(); |
|||
return signval; |
|||
}; |
|||
|
|||
export default signGenerator; |
@ -0,0 +1,38 @@ |
|||
|
|||
var sortParam = data => { |
|||
var keys = []; |
|||
for (var key in data) { |
|||
keys.push(key); |
|||
} |
|||
|
|||
//keys.sort();
|
|||
//console.log(keys);
|
|||
|
|||
var ptxt = ""; |
|||
for (var i = 0; i < keys.length; i++) { |
|||
if (data[keys[i]] instanceof Array) { |
|||
if (i === 0) { |
|||
data[keys[i]].forEach((v, index) => { |
|||
if (index === 1) { |
|||
ptxt += keys[i] + "=" + v; |
|||
} else { |
|||
ptxt += "&" + keys[i] + "=" + v; |
|||
} |
|||
}) |
|||
} else { |
|||
data[keys[i]].forEach(v => { |
|||
ptxt += "&" + keys[i] + "=" + v; |
|||
}) |
|||
} |
|||
} else { |
|||
if (i === 0) { |
|||
ptxt += keys[i] + "=" + data[keys[i]]; |
|||
} else { |
|||
ptxt += "&" + keys[i] + "=" + data[keys[i]]; |
|||
} |
|||
} |
|||
} |
|||
// console.log(ptxt);
|
|||
return ptxt; |
|||
} |
|||
export default sortParam; |
@ -0,0 +1,66 @@ |
|||
export namespace HttpRequestPs { |
|||
|
|||
export interface Transfer { |
|||
email: string, |
|||
amount: string, |
|||
type: number |
|||
} |
|||
|
|||
export interface Register { |
|||
"code": string, |
|||
"email": string, |
|||
"inviti_code": string, |
|||
"name": string, |
|||
"password": string |
|||
} |
|||
|
|||
export interface SendEmail { |
|||
email: string, |
|||
type: 1 | 2 | 3 //1.register 2.withdraw 3.resetPwd
|
|||
} |
|||
|
|||
export interface Login { |
|||
"code": string, |
|||
"email": string, |
|||
"id": string, |
|||
"password": string |
|||
} |
|||
|
|||
export interface ResetPassword { |
|||
"code": string, |
|||
"password": string |
|||
} |
|||
|
|||
export interface Record { |
|||
page: number, |
|||
page_size: number |
|||
} |
|||
|
|||
export interface AddReceiveAccount { |
|||
"address": string, |
|||
"token_id": 0, |
|||
"type": string |
|||
} |
|||
|
|||
export interface AddReceiveAccount { |
|||
"amount": string, |
|||
"code": number, |
|||
"receiving_account_id": number |
|||
} |
|||
|
|||
export interface Forget { |
|||
code: string; |
|||
email: string; |
|||
id: string |
|||
} |
|||
|
|||
export interface TransferDeposit { |
|||
amount: string; |
|||
url: string |
|||
} |
|||
|
|||
export interface Submit { |
|||
"account": string, |
|||
"password": string |
|||
} |
|||
} |
@ -1,31 +1,114 @@ |
|||
import { QRCode } from 'antd' |
|||
import { Button, Form, Input, Modal, QRCode, Select, notification } from 'antd' |
|||
import '../../styles/home.scss' |
|||
import { observer } from 'mobx-react' |
|||
import store from '../../store' |
|||
import { getTime } from '../../utils' |
|||
import { useEffect, useState } from 'react' |
|||
import { http_addReceiveAccount, http_token } from '../../http/api' |
|||
|
|||
const Account = () => { |
|||
|
|||
const { receiveAccount } = store.state |
|||
const [form] = Form.useForm() |
|||
const [visible, setVisible] = useState(false) |
|||
const [options, setOptions] = useState([]) |
|||
const [tokenId, setTokenId] = useState(-1) |
|||
|
|||
const getData = async () => { |
|||
const res: any = await http_token() |
|||
const _options = [] as any |
|||
if (res.code === 0) { |
|||
res.data.forEach((item: any) => { |
|||
_options.push({ |
|||
label: `${item.symbol}取款 - 币种:${item.symbol}`, |
|||
value: item.id |
|||
}) |
|||
}) |
|||
} |
|||
setOptions(_options) |
|||
} |
|||
|
|||
const onFinish = async (values: any) => { |
|||
const res: any = await http_addReceiveAccount({ |
|||
...values, |
|||
token_id: tokenId |
|||
}) |
|||
if (res.code === 0) { |
|||
store.getReceiveAccount() |
|||
notification.success({ |
|||
message: '添加成功' |
|||
}) |
|||
form.resetFields() |
|||
setTokenId(-1) |
|||
setVisible(false) |
|||
} |
|||
} |
|||
|
|||
useEffect(() => { |
|||
getData() |
|||
}, []) |
|||
|
|||
return ( |
|||
<div className='account'> |
|||
<div className="row-between mb-2"> |
|||
<div className="row-items"> |
|||
<div className='text-white fz-22'>办理存款</div> |
|||
<div className="fz-22 ml-1 fz-wb-550 text-blue">3条 </div> |
|||
<div className="fz-22 ml-1 fz-wb-550 text-blue">{receiveAccount.length}条 </div> |
|||
<div className="fz-14 text-white">已绑定</div> |
|||
</div> |
|||
<div className="fz-wb-550 text-white">填写收款账户</div> |
|||
</div> |
|||
<div className='container p-2'> |
|||
<div className='text-success'>有效</div> |
|||
<div className='border p-2 mt-2 fz-14'> |
|||
<div>BSC Main</div> |
|||
<div className='text-sub'>钱包类型</div> |
|||
<div className='mt-2'>asdakjsdlkajsdasdjk</div> |
|||
<div className='text-sub'>钱包地址</div> |
|||
<QRCode value='asdakjsdlkajsdasdjk' bordered={false} /> |
|||
</div> |
|||
<div className='fz-14 mt-2'>时间 <span className='fz-wb-550 text-blue'>2024-06-14 10:28:47</span></div> |
|||
<div className="fz-wb-550 text-white tp" onClick={() => setVisible(true)}>填写收款账户</div> |
|||
</div> |
|||
{ |
|||
receiveAccount.map(item => ( |
|||
<div className='container p-2 mb-2' key={item.id}> |
|||
<div className='text-success'>有效</div> |
|||
<div className='border p-2 mt-2 fz-14'> |
|||
<div>{item.type}</div> |
|||
<div className='text-sub'>钱包类型</div> |
|||
<div className='mt-2'>{item.address}</div> |
|||
<div className='text-sub'>钱包地址</div> |
|||
<QRCode value={item.address} bordered={false} /> |
|||
</div> |
|||
<div className='fz-14 mt-2'>时间 <span className='fz-wb-550 text-blue'>{getTime(item.time * 1000)}</span></div> |
|||
</div> |
|||
)) |
|||
} |
|||
<Modal |
|||
open={visible} |
|||
onClose={() => setVisible(false)} |
|||
title="添加收款账户" |
|||
footer={null} |
|||
> |
|||
<Form |
|||
form={form} |
|||
layout='vertical' |
|||
className='mt-2' |
|||
onFinish={onFinish} |
|||
> |
|||
<Form.Item label={<span className='fz-wb-550'>收款方式</span>}> |
|||
<Select style={{ height: 45 }} options={options} placeholder="请选择收款方式" onSelect={val => setTokenId(val)} /> |
|||
</Form.Item> |
|||
{ |
|||
tokenId >= 0 && ( |
|||
<> |
|||
<Form.Item name="type" rules={[{ required: true, message: '请输入链类型' }]} label={<span className='fz-wb-550'>链类型</span>}> |
|||
<Input style={{ height: 45 }} placeholder='请输入链类型' /> |
|||
</Form.Item> |
|||
<Form.Item name="address" rules={[{ required: true, message: '请输入钱包地址' }]} label={<span className='fz-wb-550'>钱包地址</span>}> |
|||
<Input style={{ height: 45 }} placeholder='请输入钱包地址' /> |
|||
</Form.Item> |
|||
</> |
|||
) |
|||
} |
|||
|
|||
<div className='row-justify-end'> |
|||
<Button onClick={() => setVisible(false)}>取消</Button> |
|||
<Button className='ml-1' type='primary' htmlType='submit'>保存</Button> |
|||
</div> |
|||
</Form> |
|||
</Modal> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default Account |
|||
export default observer(Account) |
@ -1,36 +1,185 @@ |
|||
import { Button, Form, Input, Select } from 'antd' |
|||
import { Button, Form, Input, QRCode, Select, Tag, Upload, notification } from 'antd' |
|||
import '../../styles/home.scss' |
|||
import { http_depositAddress, http_transferDeposit } from '../../http/api' |
|||
import { useEffect, useRef, useState } from 'react' |
|||
import { observer } from 'mobx-react' |
|||
import store from '../../store' |
|||
import { copy } from '../../utils' |
|||
import signGenerator from '../../http/sign' |
|||
|
|||
const Deposit = () => { |
|||
|
|||
const { userInfo, token } = store.state |
|||
const [form] = Form.useForm() |
|||
const [receiveAdds, setReceiveAdds] = useState([] as any) |
|||
const [visibles, setVisibles] = useState(false) |
|||
const [urls, setUrls] = useState([] as any) |
|||
const amount = useRef('0') |
|||
|
|||
const getData = async () => { |
|||
const res: any = await http_depositAddress() |
|||
if (res.code === 0) { |
|||
setReceiveAdds(res.data) |
|||
} |
|||
} |
|||
|
|||
const onFinish = (values: any) => { |
|||
amount.current = values.amount; |
|||
form.resetFields() |
|||
setVisibles(true) |
|||
} |
|||
|
|||
const validateAmount = (_: any, value: string) => { |
|||
if (!value) { |
|||
return Promise.reject(new Error('请输入转账金额')) |
|||
} |
|||
if (Number(value) <= 0) { |
|||
return Promise.reject(new Error('转账金额必须大于 0')) |
|||
} |
|||
return Promise.resolve(); |
|||
} |
|||
|
|||
const uploadProps = { |
|||
name: 'file', |
|||
action: `${process.env.REACT_APP_BASEURL}/api/v1/uploadImg`, |
|||
headers: { |
|||
sign: signGenerator({ |
|||
uri: '/api/v1/uploadImg', |
|||
timestamp: Date.now(), |
|||
args: '', |
|||
}), |
|||
Token: token, |
|||
timestamp: Date.now() as any |
|||
}, |
|||
} |
|||
|
|||
const onChange = (info: any, index: number) => { |
|||
|
|||
if (info.file.status === 'removed') { |
|||
urls[index] && urls.splice(index, 1) |
|||
} |
|||
|
|||
if (info.file.status === 'done') { |
|||
if (info.file.response && info.file.response.code === 0) { |
|||
urls[index] = info.file.response.data.url |
|||
setUrls([...urls]) |
|||
} |
|||
notification.success({ |
|||
message: `凭证上传成功` |
|||
}); |
|||
} else if (info.file.status === 'error') { |
|||
notification.error({ |
|||
message: `凭证上传失败` |
|||
}); |
|||
} |
|||
} |
|||
|
|||
const submit = async (index: number) => { |
|||
const url = urls[index] |
|||
if (!url) return notification.warning({ message: '请上传付款凭证' }); |
|||
const res: any = await http_transferDeposit({ |
|||
url, |
|||
amount: amount.current |
|||
}) |
|||
if (res.code === 0) { |
|||
resetData() |
|||
setVisibles(false) |
|||
notification.success({ |
|||
message: '提交成功' |
|||
}) |
|||
} |
|||
} |
|||
|
|||
const resetData = () => { |
|||
setUrls([]) |
|||
amount.current = '0' |
|||
} |
|||
|
|||
useEffect(() => { |
|||
getData() |
|||
}, []) |
|||
|
|||
useEffect(() => { |
|||
if (!visibles) { |
|||
resetData() |
|||
} |
|||
}, [visibles]) |
|||
|
|||
return ( |
|||
<div className="deposit"> |
|||
<div className='text-white fz-22 mb-2'>办理存款</div> |
|||
<div className="flex-1 container"> |
|||
<div className='p-2'> |
|||
<div className='fz-wb-550'>策略量化存款-存款注意事项</div> |
|||
<div className='text-sub fz-14 mt-1'>尊敬的客户,请您转账时务必仔细检查钱包地址全部一致。转账完毕后,请及时上传转账凭证。</div> |
|||
</div> |
|||
<div className='divider' style={{ backgroundColor: '#8492a6' }}></div> |
|||
<div className='p-2'> |
|||
<Form |
|||
layout="vertical" |
|||
> |
|||
<Form.Item vertical={true} label={<span className='fz-wb-550'>存款到</span>}> |
|||
<Select style={{ height: 50 }} value="(CFD)658532 余额:10291.51" className='tac' /> |
|||
</Form.Item> |
|||
<Form.Item vertical={true} label={<span className='fz-wb-550'>存款到</span>}> |
|||
<Input style={{ height: 50 }} placeholder='请输入存款金额' /> |
|||
</Form.Item> |
|||
<Form.Item> |
|||
<div className='row-justify-end'> |
|||
<Button type='primary' style={{ borderRadius: 30 }}>下一步</Button> |
|||
<div className='text-white fz-22 mb-2 tp' onClick={() => setVisibles(false)}>办理存款</div> |
|||
|
|||
{ |
|||
!visibles && ( |
|||
<div className="flex-1 container"> |
|||
<div className='p-2'> |
|||
<div className='fz-wb-550'>策略量化存款-存款注意事项</div> |
|||
<div className='text-sub fz-14 mt-1'>尊敬的客户,请您转账时务必仔细检查钱包地址全部一致。转账完毕后,请及时上传转账凭证。</div> |
|||
</div> |
|||
<div className='divider' style={{ backgroundColor: '#8492a6' }}></div> |
|||
<div className='p-2'> |
|||
<Form |
|||
layout="vertical" |
|||
onFinish={onFinish} |
|||
form={form} |
|||
> |
|||
<Form.Item label={<span className='fz-wb-550'>存款到</span>}> |
|||
<Select style={{ height: 50 }} value={`(CFD)${userInfo.account} 余额:${userInfo.account_balance}`} className='tac' /> |
|||
</Form.Item> |
|||
<Form.Item name="amount" rules={[{ validator: validateAmount }]} label={<span className='fz-wb-550'>存款金额</span>}> |
|||
<Input style={{ height: 50 }} placeholder='请输入存款金额' /> |
|||
</Form.Item> |
|||
<Form.Item> |
|||
<div className='row-justify-end'> |
|||
<Button type='primary' htmlType='submit' style={{ borderRadius: 30 }}>下一步</Button> |
|||
</div> |
|||
</Form.Item> |
|||
</Form> |
|||
</div> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
{ |
|||
visibles && receiveAdds.map((item: any, index: number) => ( |
|||
<div key={item.id} className="flex-1 container mb-2"> |
|||
<div className='p-2'> |
|||
<div className='fz-wb-550'>策略量化存款-存款注意事项</div> |
|||
<div className='text-sub fz-14 mt-1'>尊敬的客户,请您转账时务必仔细检查钱包地址全部一致。转账完毕后,请及时上传转账凭证。</div> |
|||
</div> |
|||
<div className='divider' style={{ backgroundColor: '#8492a6' }}></div> |
|||
<div className='p-2'> |
|||
<div className='text-sub'>请向下方账户转账</div> |
|||
<div className='plr-1'> |
|||
<div className='row-items mt-2'> |
|||
<div className='fz-wb-550 fz-14'>钱包类型</div> |
|||
<Tag color='#87d068' className='ml-1'>{item.type}</Tag> |
|||
</div> |
|||
<div className='mt-2 fz-wb-550 fz-14'>钱包地址</div> |
|||
<Tag color='#87d068' className='mt-1' onClick={() => copy(item.address)}>{item.address}</Tag> |
|||
<div className='mt-2'> |
|||
<QRCode value={item.address} /> |
|||
</div> |
|||
<div className='mt-2 row-items'> |
|||
<div className='fz-14 fz-wb-550 mr-2'>上传付款凭证</div> |
|||
<Upload |
|||
{...uploadProps} |
|||
maxCount={1} |
|||
onChange={(file: any) => onChange(file, index)} |
|||
> |
|||
<Button type='primary'>上传付款凭证</Button> |
|||
</Upload> |
|||
</div> |
|||
<div className='row-center mt-4'> |
|||
<Button type='primary' disabled={!urls[index]} onClick={() => submit(index)}>提交</Button> |
|||
</div> |
|||
</div> |
|||
</Form.Item> |
|||
</Form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
)) |
|||
} |
|||
</div > |
|||
) |
|||
} |
|||
|
|||
export default Deposit |
|||
export default observer(Deposit) |
@ -1,30 +1,113 @@ |
|||
import { Progress } from 'antd' |
|||
import '../../styles/home.scss' |
|||
import ProfitChart from '../../components/profitChart' |
|||
import { HiDotsVertical } from "react-icons/hi"; |
|||
import { useEffect, useState } from 'react'; |
|||
import store from '../../store'; |
|||
import { http_earn_trend } from '../../http/api'; |
|||
import { observer } from 'mobx-react'; |
|||
|
|||
const Home = () => { |
|||
|
|||
const { token, userInfo } = store.state |
|||
const [chartData, setChartData] = useState({ |
|||
times: [], |
|||
values: [] |
|||
}) |
|||
|
|||
const getTime = (value: number, type?: string) => { |
|||
let date = new Date(value); |
|||
let mm: number | string = date.getMonth() + 1; |
|||
let dd: number | string = date.getDate(); |
|||
mm = mm >= 10 ? mm : "0" + mm; |
|||
dd = dd >= 10 ? dd : "0" + dd; |
|||
return `${mm}/${dd}`; |
|||
}; |
|||
|
|||
const getData = async () => { |
|||
const res: any = await http_earn_trend() |
|||
const times = [] as any |
|||
const values = [] as any |
|||
if (res.code === 0) { |
|||
res.data.forEach((item: any) => { |
|||
times.push(getTime(item.time * 1000)) |
|||
values.push(Number(item.amount)) |
|||
}) |
|||
} |
|||
setChartData({ times, values }) |
|||
} |
|||
|
|||
useEffect(() => { |
|||
token && getData() |
|||
}, [token]) |
|||
|
|||
return ( |
|||
<div className="home"> |
|||
<div className="text-white fz-22 fz-wb-500">仪表盘</div> |
|||
<div className="row-between"> |
|||
<div className="income"></div> |
|||
<div className="total-income"></div> |
|||
<div className="text-white fz-22 fz-wb-500 mb-2">仪表盘</div> |
|||
<div className='box'> |
|||
<div className="income p-1"> |
|||
<ProfitChart data={chartData} /> |
|||
</div> |
|||
<div className="total-income row-center"> |
|||
<div> |
|||
<div className='tac fz-22 mb-1'>总收益</div> |
|||
<Progress type="circle" percent={50} size={150} strokeColor="#f7b93f" format={() => `$${userInfo.total_income || 0}`} /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className='CFD-box mt-2'> |
|||
<div className='CFD-box mt-2 p-2'> |
|||
<div className='row-between'> |
|||
<div>CFD 账户 {userInfo.account}</div> |
|||
<HiDotsVertical /> |
|||
</div> |
|||
<div className='mt-2'> |
|||
<table border={0} style={{ width: '100%', background: '#eff2f7' }} cellSpacing={5}> |
|||
<tbody> |
|||
<tr style={{ background: '#fff' }}> |
|||
<td className='tac'> |
|||
<p>资本</p> |
|||
<p className='text-success'>${userInfo.account_capital || 0}</p> |
|||
</td> |
|||
</tr> |
|||
|
|||
<tr style={{ background: '#fff' }}> |
|||
<td className='tac'> |
|||
<p>余额</p> |
|||
<p className='text-success'>${userInfo.account_balance || 0}</p> |
|||
</td> |
|||
</tr> |
|||
|
|||
<tr style={{ background: '#fff' }}> |
|||
<td className='tac'> |
|||
<p>信用度</p> |
|||
<p className='text-success'>${userInfo.account_credit || 0}</p> |
|||
</td> |
|||
</tr> |
|||
|
|||
<tr style={{ background: '#fff' }}> |
|||
<td className='tac'> |
|||
<p>净值</p> |
|||
<p className='text-success'>${userInfo.account_net_worth || 0}</p> |
|||
</td> |
|||
</tr> |
|||
|
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<div className='mt-2'>账户状态:{userInfo.account_status}</div> |
|||
</div> |
|||
|
|||
<div className='mt-2'> |
|||
<iframe frameBorder="0" width="100%" height="500" scrolling="yes" src="https://www.jin10.com/example/jin10.com.html?fontSize=14px&theme=white"> |
|||
|
|||
</iframe> |
|||
<iframe frameBorder="0" width="100%" height="500" scrolling="yes" src="https://www.jin10.com/example/jin10.com.html?fontSize=14px&theme=white"></iframe> |
|||
</div> |
|||
|
|||
<div className='mt-2'> |
|||
<iframe id="iframe" frameBorder="0" width="100%" height="500" src="https://rili-test2.jin10.com?quote=1&fontSize=14px&theme=primary"></iframe> |
|||
|
|||
</div> |
|||
|
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default Home |
|||
export default observer(Home) |
@ -0,0 +1,51 @@ |
|||
import { Input, QRCode } from 'antd' |
|||
import '../../styles/home.scss' |
|||
import Button from '../../components/Button' |
|||
import { IoCopyOutline } from "react-icons/io5"; |
|||
import { copy } from '../../utils'; |
|||
import { observer } from 'mobx-react'; |
|||
import store from '../../store'; |
|||
|
|||
const Link = () => { |
|||
|
|||
const { userInfo } = store.state |
|||
|
|||
return ( |
|||
<div className='link'> |
|||
<div className='text-white fz-22 mb-2'>推广链接</div> |
|||
|
|||
<div className='container ptb-2'> |
|||
<div className='plr-2'>代理推广链接</div> |
|||
<div className='divider mt-2'></div> |
|||
<div className='plr-2'> |
|||
<div className='row-center'> |
|||
<QRCode value={`${window.origin}/#/createProxy?code=${userInfo.inviti_code}`} bordered={false} /> |
|||
</div> |
|||
<div className='tac fz-14 fz-wb-550 mt-1 row-center'> |
|||
<span>推荐码:</span> |
|||
<span className='text-success'>{userInfo.inviti_code}</span> |
|||
<IoCopyOutline className='ml-1 tp' onClick={() => copy(userInfo.inviti_code)} /> |
|||
</div> |
|||
<div className='mt-1'> |
|||
<Input className='tac text-sub' disabled style={{ |
|||
backgroundColor: '#eff2f7', |
|||
height: 40 |
|||
}} value={`${window.origin}/#/createProxy?code=${userInfo.inviti_code}`} /> |
|||
</div> |
|||
<div className='mt-2 row-center'> |
|||
<Button |
|||
style={{ |
|||
width: 150, |
|||
height: 50, |
|||
backgroundColor: '#007DBA' |
|||
}} |
|||
onClick={() => copy(`${window.origin}/#/createProxy?code=${userInfo.inviti_code}`)} |
|||
>链接复制</Button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default observer(Link) |
@ -0,0 +1,53 @@ |
|||
import { Form, Input } from "antd" |
|||
import "../../styles/home.scss" |
|||
|
|||
const Personal = () => { |
|||
return ( |
|||
<div className="widthdraw"> |
|||
<div className='text-white fz-22 mb-2'>个人资料</div> |
|||
<div className="container p-2"> |
|||
<Form |
|||
layout="vertical" |
|||
> |
|||
|
|||
<div className='form-box'> |
|||
<Form.Item className='input' label={<span className='fz-wb-550'>姓名</span>}> |
|||
<Input style={{ height: 40 }} placeholder='' disabled value={"-"} /> |
|||
</Form.Item> |
|||
<Form.Item className='input' label={<span className='fz-wb-550'>国家</span>}> |
|||
<div className='row-items'> |
|||
<Input style={{ height: 40 }} placeholder='' disabled value="-" /> |
|||
</div> |
|||
</Form.Item> |
|||
</div> |
|||
|
|||
<div className='form-box'> |
|||
<Form.Item className='input' label={<span className='fz-wb-550'>省</span>}> |
|||
<Input style={{ height: 40 }} placeholder='' disabled value="-" /> |
|||
</Form.Item> |
|||
<Form.Item className='input' label={<span className='fz-wb-550'>地址</span>}> |
|||
<div className='row-items'> |
|||
<Input style={{ height: 40 }} placeholder='' disabled value="-" /> |
|||
</div> |
|||
</Form.Item> |
|||
</div> |
|||
|
|||
<div className='form-box'> |
|||
<Form.Item className='input' label={<span className='fz-wb-550'>邮箱</span>}> |
|||
<Input style={{ height: 40 }} placeholder='' disabled value="9317088@qq.com" /> |
|||
</Form.Item> |
|||
<Form.Item className='input' label={<span className='fz-wb-550'>电话</span>}> |
|||
<div className='row-items'> |
|||
<Input style={{ height: 40 }} placeholder='' disabled value="-" /> |
|||
</div> |
|||
</Form.Item> |
|||
</div> |
|||
|
|||
</Form> |
|||
</div> |
|||
|
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default Personal |
@ -0,0 +1,216 @@ |
|||
import { title } from 'process' |
|||
import MyTable from '../../../components/MyTable' |
|||
import '../../../styles/home.scss' |
|||
import { observer } from 'mobx-react' |
|||
import store from '../../../store' |
|||
import { useMemo } from 'react' |
|||
import { ConfigProvider, Tabs } from 'antd' |
|||
import { http_account_assetsRecords, http_cash } from '../../../http/api' |
|||
import { getTime } from '../../../utils' |
|||
|
|||
const AssetsRecords = () => { |
|||
|
|||
const { screenWidth } = store.state |
|||
|
|||
const containerWidth = useMemo(() => { |
|||
if (screenWidth > 1420) { |
|||
return 1420 - 310 |
|||
} |
|||
if (screenWidth > 1000) { |
|||
return screenWidth - 320 |
|||
} |
|||
return screenWidth - 30 |
|||
}, [screenWidth]) |
|||
|
|||
const AccountRecord = () => ( |
|||
<div className="container p-2" style={{ '--screen': `${containerWidth}px` } as React.CSSProperties}> |
|||
<MyTable |
|||
apiFun={http_account_assetsRecords} |
|||
columns={[ |
|||
{ |
|||
title: '状态', |
|||
dataIndex: 'status', |
|||
width: 150 |
|||
}, |
|||
{ |
|||
title: '时间', |
|||
dataIndex: 'time', |
|||
width: 200, |
|||
render: (time: number) => ( |
|||
<span>{getTime(time * 1000)}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '事件', |
|||
dataIndex: 'event', |
|||
width: 150 |
|||
}, |
|||
{ |
|||
title: '订单', |
|||
dataIndex: 'order', |
|||
width: 120 |
|||
}, |
|||
{ |
|||
title: '账户', |
|||
dataIndex: 'account' |
|||
}, |
|||
{ |
|||
title: '金额', |
|||
dataIndex: 'amount', |
|||
render: (val: string) => ( |
|||
<span>${val}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '描述', |
|||
dataIndex: 'describe' |
|||
}, |
|||
|
|||
]} |
|||
/> |
|||
</div> |
|||
) |
|||
|
|||
const WalletToAsseetRecord = () => ( |
|||
<div className="container p-2" style={{ '--screen': `${containerWidth}px` } as React.CSSProperties}> |
|||
<MyTable |
|||
apiFun={http_cash} |
|||
extraProps={{ type: 1 }} |
|||
columns={[ |
|||
{ |
|||
title: '时间', |
|||
dataIndex: 'time', |
|||
width: 200, |
|||
render: (time: number) => ( |
|||
<span>{getTime(time * 1000)}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '订单', |
|||
dataIndex: 'order', |
|||
}, |
|||
{ |
|||
title: '描述', |
|||
dataIndex: 'describe', |
|||
}, |
|||
{ |
|||
title: '剩余', |
|||
dataIndex: 'balance', |
|||
render: (val: string) => ( |
|||
<span>${val}</span> |
|||
) |
|||
}, |
|||
]} |
|||
/> |
|||
</div> |
|||
) |
|||
|
|||
const AssetToVipRecords = () => ( |
|||
<div className="container p-2" style={{ '--screen': `${containerWidth}px` } as React.CSSProperties}> |
|||
<MyTable |
|||
apiFun={http_cash} |
|||
extraProps={{ type: 2 }} |
|||
columns={[ |
|||
{ |
|||
title: '时间', |
|||
dataIndex: 'time', |
|||
width: 200, |
|||
render: (time: number) => ( |
|||
<span>{getTime(time * 1000)}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '订单', |
|||
dataIndex: 'order', |
|||
}, |
|||
{ |
|||
title: '描述', |
|||
dataIndex: 'describe', |
|||
}, |
|||
{ |
|||
title: '剩余', |
|||
dataIndex: 'balance', |
|||
render: (val: string) => ( |
|||
<span>${val}</span> |
|||
) |
|||
}, |
|||
]} |
|||
/> |
|||
</div> |
|||
) |
|||
|
|||
const AssetToWalletRecords = () => ( |
|||
<div className="container p-2" style={{ '--screen': `${containerWidth}px` } as React.CSSProperties}> |
|||
<MyTable |
|||
apiFun={http_cash} |
|||
extraProps={{ type: 3 }} |
|||
columns={[ |
|||
{ |
|||
title: '时间', |
|||
dataIndex: 'time', |
|||
width: 200, |
|||
render: (time: number) => ( |
|||
<span>{getTime(time * 1000)}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '订单', |
|||
dataIndex: 'order', |
|||
}, |
|||
{ |
|||
title: '描述', |
|||
dataIndex: 'describe', |
|||
}, |
|||
{ |
|||
title: '剩余', |
|||
dataIndex: 'balance', |
|||
render: (val: string) => ( |
|||
<span>${val}</span> |
|||
) |
|||
}, |
|||
]} |
|||
/> |
|||
</div> |
|||
) |
|||
|
|||
return ( |
|||
<div className="records" > |
|||
<div className='text-white fz-22'>账单记录</div> |
|||
<ConfigProvider theme={{ |
|||
components: { |
|||
Tabs: { |
|||
itemColor: '#fff', |
|||
itemSelectedColor: '#f7b93f', |
|||
inkBarColor: '#f7b93f' |
|||
} |
|||
} |
|||
}}> |
|||
<Tabs defaultActiveKey='1' tabPosition="top" items={[ |
|||
{ |
|||
key: '1', |
|||
label: '资金账户记录', |
|||
children: AccountRecord(), |
|||
}, |
|||
{ |
|||
key: '2', |
|||
label: '现金钱包->资金账户', |
|||
children: WalletToAsseetRecord(), |
|||
}, |
|||
{ |
|||
key: '3', |
|||
label: '现金钱包->其他会员', |
|||
children: AssetToVipRecords(), |
|||
}, |
|||
{ |
|||
key: '4', |
|||
label: '资产账户->现金钱包', |
|||
children: AssetToWalletRecords(), |
|||
} |
|||
]} /> |
|||
</ConfigProvider> |
|||
|
|||
</div > |
|||
) |
|||
} |
|||
|
|||
export default observer(AssetsRecords) |
@ -0,0 +1,111 @@ |
|||
import { title } from 'process' |
|||
import MyTable from '../../../components/MyTable' |
|||
import '../../../styles/home.scss' |
|||
import { observer } from 'mobx-react' |
|||
import store from '../../../store' |
|||
import { useMemo } from 'react' |
|||
import { http_bonus } from '../../../http/api' |
|||
import { getTime } from '../../../utils' |
|||
import { Input, Select } from 'antd' |
|||
|
|||
const BonusRecords = () => { |
|||
|
|||
const { screenWidth } = store.state |
|||
|
|||
const containerWidth = useMemo(() => { |
|||
if (screenWidth > 1420) { |
|||
return 1420 - 310 |
|||
} |
|||
if (screenWidth > 1000) { |
|||
return screenWidth - 320 |
|||
} |
|||
return screenWidth - 30 |
|||
}, [screenWidth]) |
|||
|
|||
const options = [ |
|||
{ label: '请选择类型', value: 0 }, |
|||
{ label: '交易利润', value: 1 }, |
|||
{ label: '盈利分红', value: 2 }, |
|||
{ label: '超级奖励', value: 3 }, |
|||
{ label: '交易佣金', value: 4 }, |
|||
{ label: '星际奖励', value: 5 }, |
|||
] |
|||
|
|||
const searchConfigList = [ |
|||
{ |
|||
key: 'type', |
|||
name: 'type', |
|||
label: '全部类型', |
|||
initialValue: 0, |
|||
slot: <Select style={{ maxWidth: 200, minWidth: 150 }} placeholder="请选择类型" options={options} /> |
|||
}, |
|||
{ |
|||
key: 'order', |
|||
name: 'order', |
|||
label: '订单号', |
|||
initialValue: '', |
|||
slot: <Input style={{ maxWidth: 200, minWidth: 150 }} placeholder='请输入订单号' /> |
|||
}, |
|||
{ |
|||
key: 'assets', |
|||
name: 'assets', |
|||
label: '资金账户', |
|||
initialValue: '', |
|||
slot: <Input style={{ maxWidth: 200, minWidth: 150 }} placeholder='请输入资金账户' /> |
|||
} |
|||
] |
|||
|
|||
return ( |
|||
<div className="records" > |
|||
<div className='text-white fz-22 mb-2'>奖金明细</div> |
|||
<div className="container p-2" style={{ '--screen': `${containerWidth}px` } as React.CSSProperties}> |
|||
<MyTable |
|||
searchConfigList={searchConfigList} |
|||
apiFun={http_bonus} |
|||
columns={[ |
|||
{ |
|||
title: '时间', |
|||
dataIndex: 'time', |
|||
width: 200, |
|||
render: (time: number) => ( |
|||
<span>{getTime(time * 1000)}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '订单', |
|||
dataIndex: 'order' |
|||
}, |
|||
{ |
|||
title: '事件', |
|||
dataIndex: 'event', |
|||
width: 200 |
|||
}, |
|||
{ |
|||
title: '来自', |
|||
dataIndex: 'source' |
|||
}, |
|||
// {
|
|||
// title: '受益'
|
|||
// },
|
|||
{ |
|||
title: '金额', |
|||
dataIndex: 'amount', |
|||
render: (val: string) => ( |
|||
<span>${val}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '余额', |
|||
dataIndex: 'balance', |
|||
render: (val: string) => ( |
|||
<span>${val}</span> |
|||
) |
|||
}, |
|||
]} |
|||
/> |
|||
</div> |
|||
</div > |
|||
) |
|||
} |
|||
|
|||
export default observer(BonusRecords) |
@ -0,0 +1,75 @@ |
|||
import { title } from 'process' |
|||
import MyTable from '../../../components/MyTable' |
|||
import '../../../styles/home.scss' |
|||
import { observer } from 'mobx-react' |
|||
import store from '../../../store' |
|||
import { useMemo } from 'react' |
|||
import { http_depositHistory, http_withdrawRecords } from '../../../http/api' |
|||
import { getTime } from '../../../utils' |
|||
|
|||
const DepositRecords = () => { |
|||
|
|||
const { screenWidth } = store.state |
|||
|
|||
const containerWidth = useMemo(() => { |
|||
if (screenWidth > 1420) { |
|||
return 1420 - 310 |
|||
} |
|||
if (screenWidth > 1000) { |
|||
return screenWidth - 320 |
|||
} |
|||
return screenWidth - 30 |
|||
}, [screenWidth]) |
|||
|
|||
return ( |
|||
<div className="records" > |
|||
<div className='text-white fz-22 mb-2'>存款记录</div> |
|||
<div className="container p-2" style={{ '--screen': `${containerWidth}px` } as React.CSSProperties}> |
|||
<MyTable |
|||
apiFun={http_depositHistory} |
|||
columns={[ |
|||
{ |
|||
title: '状态', |
|||
dataIndex: 'status', |
|||
width: 150, |
|||
}, |
|||
{ |
|||
title: '时间', |
|||
dataIndex: 'time', |
|||
width: 200, |
|||
render: (time: number) => ( |
|||
<span>{getTime(time * 1000)}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '订单', |
|||
dataIndex: 'order' |
|||
}, |
|||
{ |
|||
title: '到账金额', |
|||
dataIndex: 'amount', |
|||
width: 160, |
|||
render: (val: string) => ( |
|||
<span>${val}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '付款金额', |
|||
dataIndex: 'payment_amount', |
|||
width: 160, |
|||
render: (val: string) => ( |
|||
<span>{val} USDT</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '账号', |
|||
dataIndex: 'account' |
|||
} |
|||
]} |
|||
/> |
|||
</div> |
|||
</div > |
|||
) |
|||
} |
|||
|
|||
export default observer(DepositRecords) |
@ -0,0 +1,67 @@ |
|||
import { title } from 'process' |
|||
import MyTable from '../../../components/MyTable' |
|||
import '../../../styles/home.scss' |
|||
import { observer } from 'mobx-react' |
|||
import store from '../../../store' |
|||
import { useMemo } from 'react' |
|||
import { http_escrowRecords } from '../../../http/api' |
|||
import { getTime } from '../../../utils' |
|||
|
|||
const EscrowRecords = () => { |
|||
|
|||
const { screenWidth } = store.state |
|||
|
|||
const containerWidth = useMemo(() => { |
|||
if (screenWidth > 1420) { |
|||
return 1420 - 310 |
|||
} |
|||
if (screenWidth > 1000) { |
|||
return screenWidth - 320 |
|||
} |
|||
return screenWidth - 30 |
|||
}, [screenWidth]) |
|||
|
|||
return ( |
|||
<div className="records" > |
|||
<div className='text-white fz-22 mb-2'>托管记录</div> |
|||
<div className="container p-2" style={{ '--screen': `${containerWidth}px` } as React.CSSProperties}> |
|||
<MyTable |
|||
apiFun={http_escrowRecords} |
|||
columns={[ |
|||
{ |
|||
title: '账号', |
|||
dataIndex: 'account', |
|||
width: 200 |
|||
}, |
|||
{ |
|||
title: '开始时间', |
|||
dataIndex: 'start_time', |
|||
width: 200, |
|||
render: (time: number) => ( |
|||
<span>{getTime(time * 1000)}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '结束时间', |
|||
dataIndex: 'end_time', |
|||
width: 200, |
|||
render: (time: number) => ( |
|||
<span>{getTime(time * 1000)}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '金额', |
|||
dataIndex: 'amount', |
|||
width: 160, |
|||
render: (val: string) => ( |
|||
<span>${val}</span> |
|||
) |
|||
} |
|||
]} |
|||
/> |
|||
</div> |
|||
</div > |
|||
) |
|||
} |
|||
|
|||
export default observer(EscrowRecords) |
@ -0,0 +1,81 @@ |
|||
import { title } from 'process' |
|||
import MyTable from '../../../components/MyTable' |
|||
import '../../../styles/home.scss' |
|||
import { observer } from 'mobx-react' |
|||
import store from '../../../store' |
|||
import { useMemo } from 'react' |
|||
import { http_withdrawRecords } from '../../../http/api' |
|||
import { getTime } from '../../../utils' |
|||
|
|||
const WithdrawRecords = () => { |
|||
|
|||
const { screenWidth } = store.state |
|||
|
|||
const containerWidth = useMemo(() => { |
|||
if (screenWidth > 1420) { |
|||
return 1420 - 310 |
|||
} |
|||
if (screenWidth > 1000) { |
|||
return screenWidth - 320 |
|||
} |
|||
return screenWidth - 30 |
|||
}, [screenWidth]) |
|||
|
|||
return ( |
|||
<div className="records" > |
|||
<div className='text-white fz-22 mb-2'>取款记录</div> |
|||
<div className="container p-2" style={{ '--screen': `${containerWidth}px` } as React.CSSProperties}> |
|||
<MyTable |
|||
apiFun={http_withdrawRecords} |
|||
columns={[ |
|||
{ |
|||
title: '状态', |
|||
dataIndex: 'status', |
|||
width: 150, |
|||
}, |
|||
{ |
|||
title: '时间', |
|||
dataIndex: 'time', |
|||
width: 200, |
|||
render: (time: number) => ( |
|||
<span>{getTime(time * 1000)}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '订单', |
|||
dataIndex: 'order' |
|||
}, |
|||
{ |
|||
title: '取款金额', |
|||
dataIndex: 'amount', |
|||
render: (val: string) => ( |
|||
<span>${val}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '到账金额', |
|||
dataIndex: 'receipt_amount', |
|||
render: (val: string) => ( |
|||
<span>${val}</span> |
|||
) |
|||
}, |
|||
{ |
|||
title: '账号', |
|||
dataIndex: 'account' |
|||
}, |
|||
{ |
|||
title: '收款户名', |
|||
dataIndex: 'type' |
|||
}, |
|||
{ |
|||
title: '收款地址', |
|||
dataIndex: 'address' |
|||
} |
|||
]} |
|||
/> |
|||
</div> |
|||
</div > |
|||
) |
|||
} |
|||
|
|||
export default observer(WithdrawRecords) |
@ -0,0 +1,78 @@ |
|||
import { Form, FormProps, Input, notification } from 'antd' |
|||
import '../../styles/login.scss' |
|||
import Button from '../../components/Button' |
|||
import { useRouter } from '../../hooks/useRouter' |
|||
import store from '../../store' |
|||
import { useEffect, useState } from 'react' |
|||
import { http_code, http_login, http_submit } from '../../http/api' |
|||
type FieldType = { |
|||
account: string; |
|||
password: string; |
|||
}; |
|||
|
|||
const Submit = () => { |
|||
|
|||
const [form] = Form.useForm() |
|||
|
|||
const onFinish: FormProps<FieldType>['onFinish'] = async (values) => { |
|||
const res: any = await http_submit({ |
|||
...values |
|||
}) |
|||
if (res.code === 0) { |
|||
notification.success({ |
|||
message: '提交成功' |
|||
}) |
|||
form.resetFields() |
|||
} |
|||
}; |
|||
|
|||
|
|||
useEffect(() => { |
|||
}, []) |
|||
|
|||
return ( |
|||
<div className='login'> |
|||
<div className='container'> |
|||
<div className='row-center'> |
|||
<img src={require('../../assets/login.png')} className='img' alt="" /> |
|||
</div> |
|||
<div className='row-center'> |
|||
<div style={{ maxWidth: 500, width: '100%', padding: '0 20px' }}> |
|||
<Form |
|||
onFinish={onFinish} |
|||
form={form} |
|||
> |
|||
<Form.Item |
|||
name="account" |
|||
rules={[ |
|||
{ |
|||
required: true, |
|||
message: '请输入账号!', |
|||
}, |
|||
]} |
|||
> |
|||
<Input className='input' placeholder='账号' ></Input> |
|||
</Form.Item> |
|||
<Form.Item |
|||
name="password" |
|||
rules={[ |
|||
{ |
|||
required: true, |
|||
message: '请输入密码!', |
|||
}, |
|||
]} |
|||
> |
|||
<Input.Password className='input' placeholder='请输入密码' /> |
|||
</Form.Item> |
|||
<Form.Item> |
|||
<Button htmlType="submit">提交</Button> |
|||
</Form.Item> |
|||
</Form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default Submit |
@ -0,0 +1,110 @@ |
|||
import { QRCode } from 'antd' |
|||
import '../../styles/home.scss' |
|||
import { useEffect, useState } from 'react' |
|||
import { http_team } from '../../http/api' |
|||
import { observer } from 'mobx-react' |
|||
import store from '../../store' |
|||
|
|||
const Team = () => { |
|||
|
|||
const { token } = store.state |
|||
const [rootAccount, setRootState] = useState('') |
|||
const [teamAccount, setTeamAccount] = useState([]) |
|||
|
|||
useEffect(() => { |
|||
const getData = async () => { |
|||
const res: any = await http_team() |
|||
if (res.code === 0) { |
|||
setRootState(res.data.email) |
|||
setTeamAccount(res.data.team) |
|||
} |
|||
} |
|||
|
|||
token && getData() |
|||
}, [token]) |
|||
|
|||
return ( |
|||
<div className='team'> |
|||
<div className='text-white fz-22 mb-2'>团队详情</div> |
|||
|
|||
<div className='container p-2'> |
|||
|
|||
<div className='row-items'> |
|||
<img src={require('../../assets/root.png')} alt="" /> |
|||
<div className='ml-1 tp'>{rootAccount}</div> |
|||
</div> |
|||
|
|||
{ |
|||
teamAccount.map((item: any) => ( |
|||
<div className='row-items' key={item.id}> |
|||
<img src={require('../../assets/_line2.png')} alt="" /> |
|||
<div className='ml-1 tp'>{item.account}</div> |
|||
</div> |
|||
)) |
|||
} |
|||
|
|||
</div> |
|||
|
|||
{/* <div className='container p-2 mt-2'> |
|||
<div className='text-sub fz-wb-550'>CE08,658532</div> |
|||
<div className='row-items mt-2 flex-wrap'> |
|||
<div className='box-width'> |
|||
<div>$0</div> |
|||
<div className='text-sub'>总存款</div> |
|||
</div> |
|||
<div className='box-width'> |
|||
<div>$0</div> |
|||
<div className='text-sub'>总取款</div> |
|||
</div> |
|||
<div className='box-width'> |
|||
<div>$0</div> |
|||
<div className='text-sub'>总交易量</div> |
|||
</div> |
|||
<div className='box-width'> |
|||
<div>$0</div> |
|||
<div className='text-sub'>总利润</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
|
|||
<div className='container p-2 mt-2'> |
|||
<div className='text-sub fz-wb-550'>团队统计数量</div> |
|||
<div className='row-items mt-2 flex-wrap'> |
|||
<div className='box-width'> |
|||
<div>$0</div> |
|||
<div className='text-sub'>团队总资本金</div> |
|||
</div> |
|||
<div className='box-width'> |
|||
<div>$0</div> |
|||
<div className='text-sub'>团队总余额</div> |
|||
</div> |
|||
<div className='box-width'> |
|||
<div>1</div> |
|||
<div className='text-sub'>团队人数</div> |
|||
</div> |
|||
<div className='box-width'> |
|||
<div>$0</div> |
|||
<div className='text-sub'>团队存款</div> |
|||
</div> |
|||
<div className='box-width'> |
|||
<div>$0</div> |
|||
<div className='text-sub'>团队取款</div> |
|||
</div> |
|||
<div className='box-width'> |
|||
<div>0</div> |
|||
<div className='text-sub'>团队总交易量</div> |
|||
</div> |
|||
<div className='box-width'> |
|||
<div>$0</div> |
|||
<div className='text-sub'>团队总利润</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> */} |
|||
|
|||
</div> |
|||
) |
|||
} |
|||
|
|||
export default observer(Team) |
@ -0,0 +1,27 @@ |
|||
export namespace Store { |
|||
export interface AccountDetails { |
|||
account: string; |
|||
account_balance: string; |
|||
account_capital: string; |
|||
account_credit: string; |
|||
account_net_worth: string; |
|||
account_observer_password: string; |
|||
account_prepaid: string; |
|||
account_status: string; |
|||
balance: string; |
|||
email: string; |
|||
inviti_code: string; |
|||
name: string; |
|||
total_assets: string; |
|||
total_income: string; |
|||
} |
|||
|
|||
export interface ReceiveAccount { |
|||
"address": string, |
|||
"fee": string, |
|||
"id": number, |
|||
"time": number, |
|||
"type": string |
|||
} |
|||
} |
|||
|
@ -0,0 +1,47 @@ |
|||
import { notification } from "antd" |
|||
|
|||
export function copy(value: string, cb?: Function) { |
|||
// 动态创建 textarea 标签
|
|||
const textarea: any = document.createElement('textarea') |
|||
// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
|
|||
textarea.readOnly = 'readonly' |
|||
textarea.style.position = 'absolute' |
|||
textarea.style.left = '-9999px' |
|||
// 将要 copy 的值赋给 textarea 标签的 value 属性
|
|||
// 网上有些例子是赋值给innerText,这样也会赋值成功,但是识别不了\r\n的换行符,赋值给value属性就可以
|
|||
textarea.value = value |
|||
// 将 textarea 插入到 body 中
|
|||
document.body.appendChild(textarea) |
|||
// 选中值并复制
|
|||
textarea.select() |
|||
textarea.setSelectionRange(0, textarea.value.length) |
|||
document.execCommand('Copy') |
|||
document.body.removeChild(textarea) |
|||
notification.success({ |
|||
message: '复制成功' |
|||
}) |
|||
if (cb && Object.prototype.toString.call(cb) === '[object Function]') { |
|||
cb() |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* @param value |
|||
* @returns |
|||
*/ |
|||
export const getTime = (value: number, type?: string) => { |
|||
let date = new Date(value); |
|||
let yy: number | string = date.getFullYear(); |
|||
let mm: number | string = date.getMonth() + 1; |
|||
let dd: number | string = date.getDate(); |
|||
let xs: number | string = date.getHours(); |
|||
let ff: number | string = date.getMinutes(); |
|||
let ss: number | string = date.getSeconds(); |
|||
mm = mm >= 10 ? mm : "0" + mm; |
|||
dd = dd >= 10 ? dd : "0" + dd; |
|||
xs = xs >= 10 ? xs : "0" + xs; |
|||
ff = ff >= 10 ? ff : "0" + ff; |
|||
ss = ss >= 10 ? ss : "0" + ss; |
|||
if (type === "day") return `${yy}-${mm}-${dd}`; |
|||
return `${yy}-${mm}-${dd} ${xs}:${ff}`; |
|||
}; |