提取
This commit is contained in:
@@ -20,6 +20,7 @@ export default {
|
|||||||
'common.ok': 'Ok',
|
'common.ok': 'Ok',
|
||||||
'common.modify': 'Modify',
|
'common.modify': 'Modify',
|
||||||
'common.copied': '复制成功',
|
'common.copied': '复制成功',
|
||||||
|
'common.prompt': 'Prompt',
|
||||||
|
|
||||||
'check.login': 'Sign In',
|
'check.login': 'Sign In',
|
||||||
'check.register': 'Sign Up',
|
'check.register': 'Sign Up',
|
||||||
@@ -69,6 +70,7 @@ export default {
|
|||||||
'home.fund.type1': '挖矿收益',
|
'home.fund.type1': '挖矿收益',
|
||||||
'home.fund.type2': '社区奖励',
|
'home.fund.type2': '社区奖励',
|
||||||
'home.fund.type3': '提币',
|
'home.fund.type3': '提币',
|
||||||
|
'home.fund.prompt': '尚未设置资金密码,是否前往设置?',
|
||||||
|
|
||||||
'home.community.total': '累计社区奖励',
|
'home.community.total': '累计社区奖励',
|
||||||
'home.community.totalInvited': '成功邀请:{num}人',
|
'home.community.totalInvited': '成功邀请:{num}人',
|
||||||
@@ -96,6 +98,24 @@ export default {
|
|||||||
'mine.addressTitle': '充值 {label} 到您的专属地址,即可获得矿晶。扫描二维码或点击地址复制:',
|
'mine.addressTitle': '充值 {label} 到您的专属地址,即可获得矿晶。扫描二维码或点击地址复制:',
|
||||||
'mine.info': '当你完成充值后,必须回到上一页矿场,点击刷新矿晶列表,平台查账确认后,您才能获赠矿晶。',
|
'mine.info': '当你完成充值后,必须回到上一页矿场,点击刷新矿晶列表,平台查账确认后,您才能获赠矿晶。',
|
||||||
|
|
||||||
|
'draw.title': '提币转账',
|
||||||
|
'draw.selection': '请选择提取币种',
|
||||||
|
'draw.target': '输入目标地址',
|
||||||
|
'draw.placeholder': '通常一到二行的随机字符串',
|
||||||
|
'draw.amount': '输入提币金额',
|
||||||
|
'draw.avaliable': '可用余额:{num} {coin}',
|
||||||
|
'draw.fee': '转账费用 {num} {coin}',
|
||||||
|
'draw.actual': '预计到账 {num} {coin}',
|
||||||
|
'draw.drawHint': '提币金额最低1000起/提币每周仅限一次',
|
||||||
|
'draw.draw': 'Draw',
|
||||||
|
'draw.qrPrompt': '请安装 App 扫描二维码',
|
||||||
|
'draw.targetPrompt': '请填写目标地址',
|
||||||
|
'draw.amountLt': '提币金额最低1000起',
|
||||||
|
'draw.amountGt': '提币金额超出余额',
|
||||||
|
'draw.summary': '你正在提取 {num} {coin} 到 {address}',
|
||||||
|
'draw.pwdPrompt': '请输入资金密码',
|
||||||
|
'draw.success': '您的提币请求正在处理中!经过区块链网络确认后即可到账,请注意查收。',
|
||||||
|
|
||||||
'profile.account': 'Account Settings',
|
'profile.account': 'Account Settings',
|
||||||
'profile.system': 'System Settings',
|
'profile.system': 'System Settings',
|
||||||
'profile.identity': '实名认证',
|
'profile.identity': '实名认证',
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export default {
|
|||||||
'common.ok': '确定',
|
'common.ok': '确定',
|
||||||
'common.modify': '修改',
|
'common.modify': '修改',
|
||||||
'common.copied': '复制成功',
|
'common.copied': '复制成功',
|
||||||
|
'common.prompt': '提示',
|
||||||
|
|
||||||
'check.login': '登录',
|
'check.login': '登录',
|
||||||
'check.register': '注册',
|
'check.register': '注册',
|
||||||
@@ -69,6 +70,7 @@ export default {
|
|||||||
'home.fund.type1': '挖矿收益',
|
'home.fund.type1': '挖矿收益',
|
||||||
'home.fund.type2': '社区奖励',
|
'home.fund.type2': '社区奖励',
|
||||||
'home.fund.type3': '提币',
|
'home.fund.type3': '提币',
|
||||||
|
'home.fund.prompt': '尚未设置资金密码,是否前往设置?',
|
||||||
|
|
||||||
'home.community.total': '累计社区奖励',
|
'home.community.total': '累计社区奖励',
|
||||||
'home.community.totalInvited': '成功邀请:{num}人',
|
'home.community.totalInvited': '成功邀请:{num}人',
|
||||||
@@ -96,6 +98,24 @@ export default {
|
|||||||
'mine.addressTitle': '充值 {label} 到您的专属地址,即可获得矿晶。扫描二维码或点击地址复制:',
|
'mine.addressTitle': '充值 {label} 到您的专属地址,即可获得矿晶。扫描二维码或点击地址复制:',
|
||||||
'mine.info': '当你完成充值后,必须回到上一页矿场,点击刷新矿晶列表,平台查账确认后,您才能获赠矿晶。',
|
'mine.info': '当你完成充值后,必须回到上一页矿场,点击刷新矿晶列表,平台查账确认后,您才能获赠矿晶。',
|
||||||
|
|
||||||
|
'draw.title': '提币转账',
|
||||||
|
'draw.selection': '请选择提取币种',
|
||||||
|
'draw.target': '输入目标地址',
|
||||||
|
'draw.placeholder': '通常一到二行的随机字符串',
|
||||||
|
'draw.amount': '输入提币金额',
|
||||||
|
'draw.avaliable': '可用余额:{num} {coin}',
|
||||||
|
'draw.fee': '转账费用 {num} {coin}',
|
||||||
|
'draw.actual': '预计到账 {num} {coin}',
|
||||||
|
'draw.drawHint': '提币金额最低1000起/提币每周仅限一次',
|
||||||
|
'draw.draw': '提币',
|
||||||
|
'draw.qrPrompt': '请安装 App 扫描二维码',
|
||||||
|
'draw.targetPrompt': '请填写目标地址',
|
||||||
|
'draw.amountLt': '提币金额最低1000起',
|
||||||
|
'draw.amountGt': '提币金额超出余额',
|
||||||
|
'draw.summary': '你正在提取 {num} {coin} 到 {address}',
|
||||||
|
'draw.pwdPrompt': '请输入资金密码',
|
||||||
|
'draw.success': '您的提币请求正在处理中!经过区块链网络确认后即可到账,请注意查收。',
|
||||||
|
|
||||||
'profile.account': '账户管理',
|
'profile.account': '账户管理',
|
||||||
'profile.system': '系统设置',
|
'profile.system': '系统设置',
|
||||||
'profile.identity': '实名认证',
|
'profile.identity': '实名认证',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Model } from 'dva';
|
import { Model } from 'dva';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { getLocale, setLocale } from 'umi-plugin-locale'
|
import { getLocale, setLocale } from 'umi-plugin-locale'
|
||||||
import { loadUser, storeUser, clearUser, clearMineList, clearCoins } from '@/utils/storage';
|
import { loadUser, storeUser, clearUser, clearMineList, clearCoins, clearDrawCoins } from '@/utils/storage';
|
||||||
import { User } from '@/types/common';
|
import { User } from '@/types/common';
|
||||||
|
|
||||||
const initState = () => {
|
const initState = () => {
|
||||||
@@ -47,6 +47,7 @@ const model: Model = {
|
|||||||
*logout(_, { put, all }) {
|
*logout(_, { put, all }) {
|
||||||
clearMineList();
|
clearMineList();
|
||||||
clearCoins();
|
clearCoins();
|
||||||
|
clearDrawCoins();
|
||||||
yield all([
|
yield all([
|
||||||
put({ type: 'clearUser' }),
|
put({ type: 'clearUser' }),
|
||||||
put({ type: 'home/clear' }),
|
put({ type: 'home/clear' }),
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export async function signIn(areacode: AreaCode, phone: string, password: string
|
|||||||
|
|
||||||
export async function signUp(areacode: AreaCode, phone: string, password: string, code: string, inviter: string): Promise<string> {
|
export async function signUp(areacode: AreaCode, phone: string, password: string, code: string, inviter: string): Promise<string> {
|
||||||
const { ok, msg, data } = await http.post(
|
const { ok, msg, data } = await http.post(
|
||||||
'/sign/up',
|
'/sign/upwi',
|
||||||
{
|
{
|
||||||
phone: encodePhone(areacode, phone),
|
phone: encodePhone(areacode, phone),
|
||||||
password,
|
password,
|
||||||
|
|||||||
166
src/pages/draw/index.tsx
Normal file
166
src/pages/draw/index.tsx
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* Title: 获取矿晶
|
||||||
|
* Routes:
|
||||||
|
* - ./src/pages/routes
|
||||||
|
*/
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { connect, DispatchProp } from 'dva';
|
||||||
|
import { Spin, Input, Select, Icon, Button, message, Modal } from 'antd';
|
||||||
|
import { formatMessage } from 'umi-plugin-locale';
|
||||||
|
import { DrawModelState } from './model';
|
||||||
|
import { PageState } from '@/types/common';
|
||||||
|
import Header from '@/components/Header';
|
||||||
|
|
||||||
|
import styles from './style.less';
|
||||||
|
|
||||||
|
const Option = Select.Option;
|
||||||
|
|
||||||
|
const DrawPage: React.FC<DispatchProp & DrawModelState> = ({
|
||||||
|
dispatch,
|
||||||
|
pageState,
|
||||||
|
coin,
|
||||||
|
coins,
|
||||||
|
balance,
|
||||||
|
address,
|
||||||
|
amount,
|
||||||
|
fee,
|
||||||
|
actual,
|
||||||
|
confirmVisible,
|
||||||
|
confirmLoading,
|
||||||
|
password,
|
||||||
|
}) => {
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch({ type: 'draw/load' });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
let body;
|
||||||
|
if (pageState === PageState.Pending) {
|
||||||
|
body = (
|
||||||
|
<Spin className={styles.none} />
|
||||||
|
);
|
||||||
|
} else if (pageState === PageState.Failure) {
|
||||||
|
body = (
|
||||||
|
<div className={styles.none}>{formatMessage({ id: 'common.loadError' })}</div>
|
||||||
|
);
|
||||||
|
} else if (pageState === PageState.Success) {
|
||||||
|
const coinLabel = coin && coin.label.toUpperCase();
|
||||||
|
const onScan = () => {
|
||||||
|
message.warn(formatMessage({ id: 'draw.qrPrompt' }));
|
||||||
|
}
|
||||||
|
const confirmContent = (
|
||||||
|
<Modal
|
||||||
|
visible={confirmVisible}
|
||||||
|
onCancel={() => dispatch({ type: 'draw/onUpdateState', confirmVisible: false })}
|
||||||
|
onOk={() => dispatch({ type: 'draw/draw' })}
|
||||||
|
okText={formatMessage({ id: 'common.ok' })}
|
||||||
|
cancelText={formatMessage({ id: 'common.cancel' })}
|
||||||
|
confirmLoading={confirmLoading}
|
||||||
|
title={formatMessage({ id: 'common.prompt' })}
|
||||||
|
maskClosable={false}
|
||||||
|
>
|
||||||
|
<span style={{ wordBreak: 'break-all' }}>
|
||||||
|
{formatMessage(
|
||||||
|
{ id: 'draw.summary' },
|
||||||
|
{ num: amount, coin: coinLabel, address },
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<div style={{ marginTop: 24 }}>
|
||||||
|
<span>{formatMessage({ id: 'draw.pwdPrompt' })}</span>
|
||||||
|
<Input
|
||||||
|
style={{ marginTop: 8 }}
|
||||||
|
value={password}
|
||||||
|
onChange={e => dispatch({ type: 'draw/onUpdateState', password: e.target.value })}
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
body = (
|
||||||
|
<>
|
||||||
|
<div className={styles.section}>
|
||||||
|
<div className="coin-selection">
|
||||||
|
<span>{formatMessage({ id: 'draw.selection' })}</span>
|
||||||
|
<Select
|
||||||
|
size="small"
|
||||||
|
value={coin && coin.id}
|
||||||
|
onChange={(payload: any) => dispatch({ type: 'draw/selectCoin', payload })}
|
||||||
|
>
|
||||||
|
{coins.map(it => (
|
||||||
|
<Option
|
||||||
|
key={it.id}
|
||||||
|
value={it.id}
|
||||||
|
>{it.label.toUpperCase()}</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`${styles.section}`}
|
||||||
|
>
|
||||||
|
<div>{formatMessage({ id: 'draw.target' }, { label: coinLabel })}</div>
|
||||||
|
<Input
|
||||||
|
value={address}
|
||||||
|
className="app-input"
|
||||||
|
placeholder={formatMessage({ id: 'draw.placeholder' })}
|
||||||
|
suffix={<Icon onClick={onScan} type="scan" />}
|
||||||
|
onChange={e => dispatch({ type: 'draw/changeAddress', payload: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.section}>
|
||||||
|
<div className="with-postfix">
|
||||||
|
<span>{formatMessage({ id: 'draw.amount' })}</span>
|
||||||
|
<span className="small-info">
|
||||||
|
{formatMessage(
|
||||||
|
{ id: 'draw.avaliable' },
|
||||||
|
{ num: balance || 0, coin: coinLabel },
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
className="app-input"
|
||||||
|
placeholder={formatMessage({ id: 'draw.drawHint' })}
|
||||||
|
addonAfter={coinLabel}
|
||||||
|
type="number"
|
||||||
|
value={amount}
|
||||||
|
onChange={e => dispatch({ type: 'draw/changeAmount', payload: e.target.value })}
|
||||||
|
/>
|
||||||
|
<div className="small-info right">
|
||||||
|
{formatMessage({ id: 'draw.fee' }, { num: fee || 0, coin: coinLabel })}
|
||||||
|
</div>
|
||||||
|
<div className="small-info right">
|
||||||
|
{formatMessage({ id: 'draw.actual' }, { num: actual || 0, coin: coinLabel })}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
className={styles.btn}
|
||||||
|
onClick={() => dispatch({ type: 'draw/performDraw' })}
|
||||||
|
>
|
||||||
|
{formatMessage({ id: 'draw.draw' })}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{confirmContent}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<Header
|
||||||
|
className="header"
|
||||||
|
title={formatMessage({ id: 'draw.title' })}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className={styles.body}>
|
||||||
|
<div className={styles.card}>{body}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
({ draw }: any) => ({ ...draw }),
|
||||||
|
)(DrawPage);
|
||||||
169
src/pages/draw/model.ts
Normal file
169
src/pages/draw/model.ts
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
import { Model, routerRedux } from "dva";
|
||||||
|
import _ from 'lodash';
|
||||||
|
import { PageState, CoinDrawn, FundBasic } from '@/types/common';
|
||||||
|
import { getCoins, retrieveBalance, calcFee, withdraw } from './service';
|
||||||
|
import { getFundBasic } from '@/services/common';
|
||||||
|
import { message, Modal } from 'antd';
|
||||||
|
import { formatMessage } from 'umi-plugin-locale';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import { dispatch } from '@/utils/utils';
|
||||||
|
|
||||||
|
export interface DrawModelState {
|
||||||
|
coins: CoinDrawn[];
|
||||||
|
pageState: PageState;
|
||||||
|
coin: CoinDrawn | null;
|
||||||
|
|
||||||
|
basic: FundBasic | null,
|
||||||
|
balance: string;
|
||||||
|
address: string;
|
||||||
|
amount: string;
|
||||||
|
fee: string; // 转账费用
|
||||||
|
actual: string; // 实际到账
|
||||||
|
|
||||||
|
confirmVisible: boolean;
|
||||||
|
password: string;
|
||||||
|
confirmLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initState: DrawModelState = {
|
||||||
|
coins: [],
|
||||||
|
coin: null,
|
||||||
|
pageState: PageState.Pending,
|
||||||
|
|
||||||
|
basic: null,
|
||||||
|
balance: '',
|
||||||
|
address: '',
|
||||||
|
amount: '',
|
||||||
|
fee: '',
|
||||||
|
actual: '',
|
||||||
|
|
||||||
|
confirmVisible: false,
|
||||||
|
password: '',
|
||||||
|
confirmLoading: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const model: Model = {
|
||||||
|
namespace: 'draw',
|
||||||
|
|
||||||
|
state: initState,
|
||||||
|
|
||||||
|
effects: {
|
||||||
|
*load(_, { put, call, all }) {
|
||||||
|
yield put({ type: 'onUpdateState', pageState: PageState.Pending });
|
||||||
|
try {
|
||||||
|
const [coins, basic]: [CoinDrawn[], FundBasic] = yield all([
|
||||||
|
call(getCoins),
|
||||||
|
call(getFundBasic),
|
||||||
|
]);
|
||||||
|
const coin = coins[0];
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
pageState: PageState.Success,
|
||||||
|
coins,
|
||||||
|
coin,
|
||||||
|
basic,
|
||||||
|
balance: retrieveBalance(basic, coin),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
pageState: PageState.Failure,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
*selectCoin({ payload: id }, { put, select }) {
|
||||||
|
const { coins, basic } = yield select((state: any) => state.draw);
|
||||||
|
const coin = _.find(coins, ['id', id]);
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
coin,
|
||||||
|
balance: retrieveBalance(basic, coin),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
*changeAddress({ payload }, { put }) {
|
||||||
|
if (!/^[\da-zA-Z]*$/.test(payload)) return;
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
address: payload,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
*changeAmount({ payload }, { put, select }) {
|
||||||
|
if (isNaN(payload)) return;
|
||||||
|
const { coin } = (yield select((state: any) => state.draw)) as DrawModelState;
|
||||||
|
if (!coin) return;
|
||||||
|
const { fee, actual } = calcFee(coin, payload);
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
amount: payload,
|
||||||
|
fee,
|
||||||
|
actual,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
*performDraw(_, { put, select }) {
|
||||||
|
const {
|
||||||
|
coin,
|
||||||
|
amount,
|
||||||
|
address,
|
||||||
|
balance,
|
||||||
|
} = (yield select((state: any) => state.draw)) as DrawModelState;
|
||||||
|
if (!coin || !balance) return;
|
||||||
|
if (!address) return message.warn(formatMessage({ id: 'draw.targetPrompt' }));
|
||||||
|
const inputAmount = new BigNumber(amount || 0);
|
||||||
|
if (inputAmount.lt(1000)) return message.warn(formatMessage({ id: 'draw.amountLt' }));
|
||||||
|
if (inputAmount.gt(balance)) return message.warn(formatMessage({ id: 'draw.amountGt' }));
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
confirmVisible: true,
|
||||||
|
confirmLoading: false,
|
||||||
|
password: '',
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
*draw(_, { put, select, call }) {
|
||||||
|
const {
|
||||||
|
coin,
|
||||||
|
amount,
|
||||||
|
address,
|
||||||
|
password,
|
||||||
|
} = (yield select((state: any) => state.draw)) as DrawModelState;
|
||||||
|
if (!coin) return;
|
||||||
|
if (!address) return message.warn(formatMessage({ id: 'draw.targetPrompt' }));
|
||||||
|
if (!password) return;
|
||||||
|
yield put({ type: 'onUpdateState', confirmLoading: true });
|
||||||
|
try {
|
||||||
|
yield call(withdraw, coin, address, amount, password);
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
confirmVisible: false,
|
||||||
|
});
|
||||||
|
Modal.info({
|
||||||
|
title: formatMessage({ id: 'common.prompt' }),
|
||||||
|
content: formatMessage({ id: 'draw.success' }),
|
||||||
|
okText: formatMessage({ id: 'common.ok' }),
|
||||||
|
cancelText: formatMessage({ id: 'common.cancel' }),
|
||||||
|
maskClosable: false,
|
||||||
|
onOk: () => dispatch(routerRedux.goBack()),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
message.error(e.message);
|
||||||
|
} finally {
|
||||||
|
yield put({ type: 'onUpdateState', confirmLoading: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
reducers: {
|
||||||
|
onUpdateState(state, payload) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...payload,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default model;
|
||||||
55
src/pages/draw/service.ts
Normal file
55
src/pages/draw/service.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import http from '@/utils/http';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import { CoinDrawn, FundBasic } from '@/types/common';
|
||||||
|
import { loadDrawCoins, storeDrawCoins } from '@/utils/storage';
|
||||||
|
|
||||||
|
export async function getCoins(): Promise<CoinDrawn[]> {
|
||||||
|
const coinsCached = loadDrawCoins();
|
||||||
|
if (coinsCached && coinsCached.length) {
|
||||||
|
return coinsCached;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { ok, msg, data } = await http.get<CoinDrawn[]>('/fund/withdrawCoins');
|
||||||
|
|
||||||
|
if (!ok || !data) {
|
||||||
|
throw Error(msg);
|
||||||
|
}
|
||||||
|
storeDrawCoins(data);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function withdraw(
|
||||||
|
coin: CoinDrawn,
|
||||||
|
targetAddress: string,
|
||||||
|
amount: string,
|
||||||
|
password: string,
|
||||||
|
): Promise<void> {
|
||||||
|
const { ok, msg } = await http.post('/fund/withdraw', {
|
||||||
|
coinId: coin.id,
|
||||||
|
targetAddress,
|
||||||
|
amount,
|
||||||
|
password,
|
||||||
|
});
|
||||||
|
if (ok) return
|
||||||
|
throw Error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function retrieveBalance(basic?: FundBasic, coin?: CoinDrawn): string {
|
||||||
|
if (!basic || !coin) return '';
|
||||||
|
return (basic as any)[coin.label.toLocaleLowerCase()]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calcFee(coin: CoinDrawn, input: string): { fee: string, actual: string } {
|
||||||
|
const fail = { fee: '', actual: '' };
|
||||||
|
if (!coin || !input) return fail;
|
||||||
|
const i = new BigNumber(input);
|
||||||
|
const f = new BigNumber(coin.fee);
|
||||||
|
if (i.isNaN() || f.isNaN()) return fail;
|
||||||
|
const fee = i.times(f);
|
||||||
|
return {
|
||||||
|
fee: fee.decimalPlaces(2).toString(),
|
||||||
|
actual: i.minus(fee).decimalPlaces(2).toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
117
src/pages/draw/style.less
Normal file
117
src/pages/draw/style.less
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
|
||||||
|
@import '../../global.less';
|
||||||
|
|
||||||
|
.container {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(to bottom, @blue-one, @blue-two);
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.header {
|
||||||
|
background-color: rgba(255,255,255,0);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn-primary {
|
||||||
|
border-width: 0;
|
||||||
|
background-color: @blue-one;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.none {
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: #fff;
|
||||||
|
margin: 10px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
box-shadow: 1px 1px 8px rgba(0, 0, 0, .2);
|
||||||
|
min-height: 60%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@pLeft: 48px;
|
||||||
|
@pRight: 30px;
|
||||||
|
@pTop: 20px;
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin-left: @pLeft;
|
||||||
|
margin-right: @pRight;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
padding-left: @pLeft;
|
||||||
|
padding-right: @pRight;
|
||||||
|
padding-top: @pTop;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
@size: 10px;
|
||||||
|
width: @size;
|
||||||
|
height: @size;
|
||||||
|
background-color: @blue-two;
|
||||||
|
border-radius: @size / 2;
|
||||||
|
top: @pTop + @size / 2 + 2px;
|
||||||
|
left: @pLeft / 2 - @size / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.app-input {
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.with-postfix {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-info {
|
||||||
|
font-weight: lighter;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.coin-selection {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
|
||||||
|
.ant-select {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
canvas {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ import { HomeFundModelState } from './model';
|
|||||||
import { Journal, JournalOp } from '@/types/common';
|
import { Journal, JournalOp } from '@/types/common';
|
||||||
import Label from '@/components/Label';
|
import Label from '@/components/Label';
|
||||||
import List from '@/components/List';
|
import List from '@/components/List';
|
||||||
import { dispatch } from '@/utils/utils';
|
|
||||||
|
|
||||||
import styles from './style.less';
|
import styles from './style.less';
|
||||||
|
|
||||||
@@ -19,6 +18,7 @@ const FundPage: React.FC<HomeFundModelState & DispatchProp> = ({
|
|||||||
data,
|
data,
|
||||||
refreshingBasic,
|
refreshingBasic,
|
||||||
errorBasic,
|
errorBasic,
|
||||||
|
loadingDraw,
|
||||||
info,
|
info,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
@@ -42,25 +42,26 @@ const FundPage: React.FC<HomeFundModelState & DispatchProp> = ({
|
|||||||
<div className="bg" />
|
<div className="bg" />
|
||||||
<span className="title">{formatMessage({ id: 'home.fund.total' })}</span>
|
<span className="title">{formatMessage({ id: 'home.fund.total' })}</span>
|
||||||
<span className="content">
|
<span className="content">
|
||||||
<span className="large">{info ? info.balanceSum : 0}</span>
|
<span className="large">{info ? info.sum : 0}</span>
|
||||||
VIC
|
VIC
|
||||||
</span>
|
</span>
|
||||||
<div className={styles.card}>
|
<div className={styles.card}>
|
||||||
<div className="left">
|
<div className="left">
|
||||||
<span className="line">
|
<span className="line">
|
||||||
<span className="title2">{formatMessage({ id: 'home.fund.totalVIC' })}</span>
|
<span className="title2">{formatMessage({ id: 'home.fund.totalVIC' })}</span>
|
||||||
<span className="value">+{info ? info.balanceVIC : 0} VIC</span>
|
<span className="value">+{info ? info.vic : 0} VIC</span>
|
||||||
</span>
|
</span>
|
||||||
<span className="line">
|
<span className="line">
|
||||||
<span className="title2">{formatMessage({ id: 'home.fund.totalUSDT' })}</span>
|
<span className="title2">{formatMessage({ id: 'home.fund.totalUSDT' })}</span>
|
||||||
<span className="value">+{info ? info.balanceUSDT : 0} USDT</span>
|
<span className="value">+{info ? info.usdt : 0} USDT</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="right app-btn"
|
className="right app-btn"
|
||||||
|
onClick={() => dispatch({ type: 'homeFund/gotoDraw' })}
|
||||||
>
|
>
|
||||||
{formatMessage({ id: 'home.fund.draw' })}
|
{loadingDraw ? <Spin size="small" /> : formatMessage({ id: 'home.fund.draw' })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -79,7 +80,7 @@ const FundPage: React.FC<HomeFundModelState & DispatchProp> = ({
|
|||||||
<div className="detail">
|
<div className="detail">
|
||||||
{it.detail}
|
{it.detail}
|
||||||
</div>
|
</div>
|
||||||
<div className="small">{it.timestamp}</div>
|
<div className="small">{it.createdAt}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { Model, routerRedux } from 'dva';
|
import { Model, routerRedux } from 'dva';
|
||||||
import { message } from 'antd';
|
import { message, Modal } from 'antd';
|
||||||
import { Journal, FundBasic } from '@/types/common';
|
import { Journal, FundBasic } from '@/types/common';
|
||||||
import { getFundList, getFundBasic } from './service';
|
import { getFundList } from './service';
|
||||||
|
import { checkFundPwd, getFundBasic } from '@/services/common';
|
||||||
|
import { formatMessage } from 'umi-plugin-locale';
|
||||||
|
|
||||||
export interface HomeFundModelState {
|
export interface HomeFundModelState {
|
||||||
refreshingList: boolean;
|
refreshingList: boolean;
|
||||||
@@ -15,6 +17,7 @@ export interface HomeFundModelState {
|
|||||||
info: FundBasic | null;
|
info: FundBasic | null;
|
||||||
|
|
||||||
pageIndex: number;
|
pageIndex: number;
|
||||||
|
loadingDraw: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FundModel: Model = {
|
const FundModel: Model = {
|
||||||
@@ -32,6 +35,7 @@ const FundModel: Model = {
|
|||||||
info: null,
|
info: null,
|
||||||
|
|
||||||
pageIndex: 1,
|
pageIndex: 1,
|
||||||
|
loadingDraw: false,
|
||||||
} as HomeFundModelState,
|
} as HomeFundModelState,
|
||||||
|
|
||||||
effects: {
|
effects: {
|
||||||
@@ -99,6 +103,30 @@ const FundModel: Model = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
*gotoDraw(_, { put, select, call }) {
|
||||||
|
const { loadingDraw } = yield select((state: any) => state.homeFund);
|
||||||
|
if (loadingDraw) return;
|
||||||
|
yield put({ type: 'onUpdateState', loadingDraw: true });
|
||||||
|
try {
|
||||||
|
const settled = yield call(checkFundPwd);
|
||||||
|
if (settled) {
|
||||||
|
yield put(routerRedux.push('/draw'));
|
||||||
|
} else {
|
||||||
|
Modal.confirm({
|
||||||
|
okText: formatMessage({ id: 'common.ok' }),
|
||||||
|
cancelText: formatMessage({ id: 'common.cancel' }),
|
||||||
|
title: formatMessage({ id: 'common.prompt' }),
|
||||||
|
content: formatMessage({ id: 'home.fund.prompt' }),
|
||||||
|
onOk: () => { message.warn('暂未完成,请再次点击'); },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
message.error(e.message);
|
||||||
|
} finally {
|
||||||
|
yield put({ type: 'onUpdateState', loadingDraw: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
reducers: {
|
reducers: {
|
||||||
|
|||||||
@@ -1,24 +1,12 @@
|
|||||||
import http from '@/utils/http';
|
import http from '@/utils/http';
|
||||||
import { Journal, FundBasic } from '@/types/common';
|
import { Journal } from '@/types/common';
|
||||||
|
|
||||||
export async function getFundBasic(): Promise<FundBasic> {
|
|
||||||
const {
|
|
||||||
ok,
|
|
||||||
data,
|
|
||||||
msg,
|
|
||||||
} = await http.get<FundBasic>('/fund/detail');
|
|
||||||
if (ok && data) {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
throw Error(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getFundList(pageIndex: number, pageSize: number): Promise<Journal[]> {
|
export async function getFundList(pageIndex: number, pageSize: number): Promise<Journal[]> {
|
||||||
const {
|
const {
|
||||||
ok,
|
ok,
|
||||||
data,
|
data,
|
||||||
msg,
|
msg,
|
||||||
} = await http.get<Journal[]>('/fund/journals', {
|
} = await http.get<Journal[]>('/fund/actions', {
|
||||||
pageIndex,
|
pageIndex,
|
||||||
pageSize,
|
pageSize,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -86,6 +86,8 @@
|
|||||||
.app-btn {
|
.app-btn {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
width: 80px;
|
||||||
|
text-align: center;
|
||||||
background-color: @blue-one;
|
background-color: @blue-one;
|
||||||
@btn-height: 28px;
|
@btn-height: 28px;
|
||||||
height: @btn-height;
|
height: @btn-height;
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ const MinePage: React.FC<DispatchProp & MineModelState> = ({
|
|||||||
size="small"
|
size="small"
|
||||||
value={srcValue}
|
value={srcValue}
|
||||||
onChange={e => dispatch({ type: 'mine/changeSrc', payload: e.target.value })}
|
onChange={e => dispatch({ type: 'mine/changeSrc', payload: e.target.value })}
|
||||||
|
type="number"
|
||||||
/> {coin && coin.label.toUpperCase()} = {dstValue} {formatMessage({ id: 'mine.mine' })}
|
/> {coin && coin.label.toUpperCase()} = {dstValue} {formatMessage({ id: 'mine.mine' })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import { Model, routerRedux } from "dva";
|
import { Model, routerRedux } from "dva";
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { message } from 'antd';
|
import { PageState, CoinUnion } from '@/types/common';
|
||||||
import { Estate, PageState, CoinUnion } from '@/types/common';
|
|
||||||
import { getCoins, calcRate } from './service';
|
import { getCoins, calcRate } from './service';
|
||||||
import { isDev } from '@/utils/utils';
|
|
||||||
|
|
||||||
export interface MineModelState {
|
export interface MineModelState {
|
||||||
coins: CoinUnion[];
|
coins: CoinUnion[];
|
||||||
@@ -36,7 +34,7 @@ const model: Model = {
|
|||||||
type: 'onUpdateState',
|
type: 'onUpdateState',
|
||||||
pageState: PageState.Success,
|
pageState: PageState.Success,
|
||||||
coins,
|
coins,
|
||||||
coin: coins[0],
|
coin,
|
||||||
srcValue: '1',
|
srcValue: '1',
|
||||||
dstValue: calcRate('1', coin && coin.rate),
|
dstValue: calcRate('1', coin && coin.rate),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import http from '@/utils/http';
|
import http from '@/utils/http';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
import { Estate, EstateType, CoinUnion, CoinSupport, CoinAddress } from '@/types/common';
|
import { CoinUnion, CoinSupport, CoinAddress } from '@/types/common';
|
||||||
import { calcEstate } from '@/utils/transform';
|
|
||||||
import { loadCoins, storeCoins } from '@/utils/storage';
|
import { loadCoins, storeCoins } from '@/utils/storage';
|
||||||
|
|
||||||
export async function getCoins(): Promise<CoinUnion[]> {
|
export async function getCoins(): Promise<CoinUnion[]> {
|
||||||
@@ -15,7 +14,7 @@ export async function getCoins(): Promise<CoinUnion[]> {
|
|||||||
{ ok: sOk, data: sData, msg: sMsg },
|
{ ok: sOk, data: sData, msg: sMsg },
|
||||||
{ ok: aOk, data: aData, msg: aMsg },
|
{ ok: aOk, data: aData, msg: aMsg },
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
http.get<CoinSupport[]>('/fund/coins'),
|
http.get<CoinSupport[]>('/fund/depositCoins'),
|
||||||
http.get<CoinAddress[]>('/fund/queryAddress'),
|
http.get<CoinAddress[]>('/fund/queryAddress'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
.none {
|
.none {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 40%;
|
padding-top: 40%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AreaCodes, User } from '@/types/common';
|
import { AreaCodes, User, FundBasic } from '@/types/common';
|
||||||
import { loadAreaCode, storeAreaCode, loadUser, storeUser } from '@/utils/storage';
|
import { loadAreaCode, storeAreaCode, loadUser, storeUser } from '@/utils/storage';
|
||||||
import http from '@/utils/http';
|
import http from '@/utils/http';
|
||||||
|
|
||||||
@@ -43,3 +43,25 @@ export async function getUserInfo(refresh = false): Promise<User> {
|
|||||||
|
|
||||||
throw Error(msg);
|
throw Error(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getFundBasic(): Promise<FundBasic> {
|
||||||
|
const {
|
||||||
|
ok,
|
||||||
|
data,
|
||||||
|
msg,
|
||||||
|
} = await http.get<FundBasic>('/fund/detail');
|
||||||
|
if (ok && data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
throw Error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkFundPwd(): Promise<boolean> {
|
||||||
|
const { ok, msg, data } = await http.get<number>('/user/checkFundPwd');
|
||||||
|
if (ok) {
|
||||||
|
// 0 未设置;1 已设置
|
||||||
|
return data === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Error(msg);
|
||||||
|
}
|
||||||
|
|||||||
@@ -141,6 +141,9 @@ export interface Mine {
|
|||||||
|
|
||||||
export type Mines = Mine[];
|
export type Mines = Mine[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取矿晶页面,支持的币种
|
||||||
|
*/
|
||||||
export interface CoinSupport {
|
export interface CoinSupport {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -155,21 +158,30 @@ export interface CoinAddress {
|
|||||||
|
|
||||||
export interface CoinUnion extends CoinSupport, CoinAddress { };
|
export interface CoinUnion extends CoinSupport, CoinAddress { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提币页面,支持的币种
|
||||||
|
*/
|
||||||
|
export interface CoinDrawn {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
fee: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface FundBasic {
|
export interface FundBasic {
|
||||||
/**
|
/**
|
||||||
* 总余额(以VIC计算)
|
* 总余额(以VIC计算)
|
||||||
*/
|
*/
|
||||||
balanceSum: string;
|
sum: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当前VIC余额
|
* 当前VIC余额
|
||||||
*/
|
*/
|
||||||
balanceVIC: string;
|
vic: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当前USDT余额
|
* 当前USDT余额
|
||||||
*/
|
*/
|
||||||
balanceUSDT: string;
|
usdt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum JournalType {
|
export enum JournalType {
|
||||||
@@ -207,7 +219,7 @@ export interface Journal {
|
|||||||
|
|
||||||
op: JournalOp;
|
op: JournalOp;
|
||||||
|
|
||||||
timestamp: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommunityBasic {
|
export interface CommunityBasic {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ const userKey = 'user-key';
|
|||||||
const areaCodeKey = 'area-code-key';
|
const areaCodeKey = 'area-code-key';
|
||||||
const mineListKey = 'mine-list-key';
|
const mineListKey = 'mine-list-key';
|
||||||
const coinsKey = 'coin-key';
|
const coinsKey = 'coin-key';
|
||||||
|
const drawCoinsKey = 'draw-coin-key';
|
||||||
|
|
||||||
// user info from now on
|
// user info from now on
|
||||||
function storeUser(user: any = undefined) {
|
function storeUser(user: any = undefined) {
|
||||||
@@ -72,6 +73,23 @@ function clearCoins() {
|
|||||||
localStorage.removeItem(coinsKey);
|
localStorage.removeItem(coinsKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// coin list info of draw page from now on
|
||||||
|
function storeDrawCoins(coins: any) {
|
||||||
|
localStorage.setItem(drawCoinsKey, JSON.stringify(coins));
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadDrawCoins() {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem(drawCoinsKey) || 'null');
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearDrawCoins() {
|
||||||
|
localStorage.removeItem(drawCoinsKey);
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
storeUser,
|
storeUser,
|
||||||
loadUser,
|
loadUser,
|
||||||
@@ -87,4 +105,8 @@ export {
|
|||||||
storeCoins,
|
storeCoins,
|
||||||
loadCoins,
|
loadCoins,
|
||||||
clearCoins,
|
clearCoins,
|
||||||
|
|
||||||
|
storeDrawCoins,
|
||||||
|
loadDrawCoins,
|
||||||
|
clearDrawCoins,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user