Charge coin

This commit is contained in:
saplf
2019-09-03 21:05:19 +08:00
parent 1dc9a95835
commit da6d2c0e68
11 changed files with 388 additions and 23 deletions

View File

@@ -3,22 +3,103 @@
* Routes:
* - ./src/pages/routes
*/
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { connect, DispatchProp } from 'dva';
import { Spin, Tabs } from 'antd';
import { Spin, Input, Select, message } from 'antd';
import { formatMessage } from 'umi-plugin-locale';
import FittedImage from 'react-fitted-image';
import { QRCanvas } from 'qrcanvas-react';
import Clipboard from 'react-clipboard.js';
import { MineModelState } from './model';
import { PageState, EstateType } from '@/types/common';
import { PageState } from '@/types/common';
import Header from '@/components/Header';
import Info, { InfoItem } from '@/components/Info';
import Failure from '@/components/Failure';
import styles from './style.less';
const Option = Select.Option;
const MinePage: React.FC<DispatchProp & MineModelState> = ({
dispatch,
pageState,
coin,
coins,
srcValue,
dstValue,
}) => {
useEffect(() => {
dispatch({ type: 'mine/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 onAddressCopied = () => {
message.success(formatMessage({ id: 'common.copied' }));
};
body = (
<>
<div className={styles.section}>
<div className="coin-selection">
<span>{formatMessage({ id: 'mine.origin' })}</span>
<Select
size="small"
value={coin && coin.id}
onChange={(payload: any) => dispatch({ type: 'mine/selectCoin', payload })}
>
{coins.map(it => (
<Option
key={it.id}
value={it.id}
>{it.label.toUpperCase()}</Option>
))}
</Select>
</div>
<div className="rate-trans">
<span>{formatMessage({ id: 'mine.calc' })}</span>
<Input
size="small"
value={srcValue}
onChange={e => dispatch({ type: 'mine/changeSrc', payload: e.target.value })}
/> {coin && coin.label.toUpperCase()} = {dstValue} {formatMessage({ id: 'mine.mine' })}
</div>
</div>
<div
className={`${styles.section} ${styles.main}`}
>
<div>{formatMessage({ id: 'mine.addressTitle' }, { label: coin && coin.label.toUpperCase() })}</div>
<Clipboard
component="div"
data-clipboard-text={coin && coin.address}
onSuccess={onAddressCopied}
>
<QRCanvas options={{
data: coin ? coin.address : '',
size: 240,
}} />
</Clipboard>
<Clipboard
component="div"
className="address"
data-clipboard-text={coin && coin.address}
onSuccess={onAddressCopied}
>
{coin && coin.address}
</Clipboard>
</div>
<div className={styles.section}>
<span>{formatMessage({ id: 'mine.info' })}</span>
</div>
</>
);
}
return (
<div className={styles.container}>
@@ -26,6 +107,10 @@ const MinePage: React.FC<DispatchProp & MineModelState> = ({
className="header"
title={formatMessage({ id: 'home.mine.retrieve' })}
/>
<div className={styles.body}>
<div className={styles.card}>{body}</div>
</div>
</div>
);
};

View File

@@ -1,13 +1,24 @@
import { Model, routerRedux } from "dva";
import _ from 'lodash';
import { message } from 'antd';
import { Estate, PageState } from '@/types/common';
import { getEstateDetail } from './service';
import { Estate, PageState, CoinUnion } from '@/types/common';
import { getCoins, calcRate } from './service';
import { isDev } from '@/utils/utils';
export interface MineModelState {
coins: CoinUnion[];
pageState: PageState;
coin: CoinUnion | null;
srcValue: string;
dstValue: string;
}
const initState: MineModelState = {
coins: [],
coin: null,
pageState: PageState.Pending,
srcValue: '1',
dstValue: '1',
};
const model: Model = {
@@ -16,6 +27,46 @@ const model: Model = {
state: initState,
effects: {
*load(_, { put, call }) {
yield put({ type: 'onUpdateState', pageState: PageState.Pending });
try {
const coins: CoinUnion[] = yield call(getCoins);
const coin = coins[0];
yield put({
type: 'onUpdateState',
pageState: PageState.Success,
coins,
coin: coins[0],
srcValue: '1',
dstValue: calcRate('1', coin && coin.rate),
});
} catch (e) {
yield put({
type: 'onUpdateState',
pageState: PageState.Failure,
});
}
},
*selectCoin({ payload: id }, { put, select }) {
const { coins, srcValue } = yield select((state: any) => state.mine);
const coin = _.find(coins, ['id', id]);
yield put({
type: 'onUpdateState',
coin,
dstValue: calcRate(srcValue, coin && coin.rate),
});
},
*changeSrc({ payload }, { put, select }) {
if (isNaN(payload)) return;
const { coin } = yield select((state: any) => state.mine);
yield put({
type: 'onUpdateState',
srcValue: payload,
dstValue: calcRate(payload, coin && coin.rate),
});
},
},
reducers: {

View File

@@ -1,18 +1,42 @@
import http from '@/utils/http';
import { Estate, EstateType } from '@/types/common';
import _ from 'lodash';
import BigNumber from 'bignumber.js';
import { Estate, EstateType, CoinUnion, CoinSupport, CoinAddress } from '@/types/common';
import { calcEstate } from '@/utils/transform';
import { loadCoins, storeCoins } from '@/utils/storage';
export async function getEstateDetail(id: string) {
const {
ok,
data,
msg,
} = await http.get<Estate>('/estateorder/detail', { id });
if (ok && data) {
data.type = data.outTime ? EstateType.Sold : EstateType.Hold;
calcEstate(data);
return data;
export async function getCoins(): Promise<CoinUnion[]> {
const coinsCached = loadCoins();
if (coinsCached && coinsCached.length) {
return coinsCached;
}
throw Error(msg);
const [
{ ok: sOk, data: sData, msg: sMsg },
{ ok: aOk, data: aData, msg: aMsg },
] = await Promise.all([
http.get<CoinSupport[]>('/fund/coins'),
http.get<CoinAddress[]>('/fund/queryAddress'),
]);
if (!sOk || !sData) {
throw Error(sMsg);
}
if (!aOk || !aData) {
throw Error(aMsg);
}
const result = sData.map(it => Object.assign(
it,
_.find(aData, ['id', it.id]),
));
storeCoins(result);
return result;
}
export function calcRate(input: string, rate?: string): string {
if (!input || !rate) return '';
return new BigNumber(input).times(rate).decimalPlaces(2).toString();
}

View File

@@ -5,6 +5,8 @@
height: 100%;
background: linear-gradient(to bottom, @purple-one, @purple-two);
position: relative;
display: flex;
flex-direction: column;
:global {
.header {
@@ -13,3 +15,86 @@
}
}
}
.body {
flex-grow: 1;
overflow: auto;
}
.none {
text-align: center;
width: 100%;
margin-top: 40%;
}
.card {
background-color: #fff;
margin: 10px;
box-shadow: 1px 1px 8px rgba(0, 0, 0, .2);
min-height: 60%;
}
.section {
@pLeft: 48px;
@pTop: 20px;
padding-left: @pLeft;
padding-right: 30px;
padding-top: @pTop;
position: relative;
&::before {
content: '';
display: block;
position: absolute;
@size: 10px;
width: @size;
height: @size;
background-color: @purple-two;
border-radius: @size / 2;
top: @pTop + @size / 2 + 2px;
left: @pLeft / 2 - @size / 2;
}
:global {
.coin-selection {
display: flex;
align-items: baseline;
.ant-select {
flex-grow: 1;
margin: 0 4px;
}
}
.rate-trans {
display: flex;
align-items: baseline;
padding: 4px 8px;
background-color: #eee3f9;
margin: 8px 0;
font-size: 0.8rem;
.ant-input {
width: 60px;
margin: 0 4px;
}
}
.address {
font-family: monospace;
word-break: break-all;
}
}
}
.main {
display: flex;
flex-direction: column;
align-items: center;
:global {
canvas {
margin: 10px 0;
}
}
}