Charge coin
This commit is contained in:
@@ -19,6 +19,7 @@ export default {
|
||||
'common.cancel': 'Cancel',
|
||||
'common.ok': 'Ok',
|
||||
'common.modify': 'Modify',
|
||||
'common.copied': '复制成功',
|
||||
|
||||
'check.login': 'Sign In',
|
||||
'check.register': 'Sign Up',
|
||||
@@ -73,6 +74,12 @@ export default {
|
||||
'estate.actualProfit': '实际收益',
|
||||
'estate.outTime': '卖出时间',
|
||||
|
||||
'mine.origin': '请选择来源币种',
|
||||
'mine.calc': '汇率计算器:',
|
||||
'mine.mine': '矿晶',
|
||||
'mine.addressTitle': '充值 {label} 到您的专属地址,即可获得矿晶。扫描二维码或点击地址复制:',
|
||||
'mine.info': '当你完成充值后,必须回到上一页矿场,点击刷新矿晶列表,平台查账确认后,您才能获赠矿晶。',
|
||||
|
||||
'profile.account': 'Account Settings',
|
||||
'profile.system': 'System Settings',
|
||||
'profile.identity': '实名认证',
|
||||
|
||||
@@ -19,6 +19,7 @@ export default {
|
||||
'common.cancel': '取消',
|
||||
'common.ok': '确定',
|
||||
'common.modify': '修改',
|
||||
'common.copied': '复制成功',
|
||||
|
||||
'check.login': '登录',
|
||||
'check.register': '注册',
|
||||
@@ -73,6 +74,12 @@ export default {
|
||||
'estate.actualProfit': '实际收益',
|
||||
'estate.outTime': '卖出时间',
|
||||
|
||||
'mine.origin': '请选择来源币种',
|
||||
'mine.calc': '汇率计算器:',
|
||||
'mine.mine': '矿晶',
|
||||
'mine.addressTitle': '充值 {label} 到您的专属地址,即可获得矿晶。扫描二维码或点击地址复制:',
|
||||
'mine.info': '当你完成充值后,必须回到上一页矿场,点击刷新矿晶列表,平台查账确认后,您才能获赠矿晶。',
|
||||
|
||||
'profile.account': '账户管理',
|
||||
'profile.system': '系统设置',
|
||||
'profile.identity': '实名认证',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Model } from 'dva';
|
||||
import _ from 'lodash';
|
||||
import { getLocale, setLocale } from 'umi-plugin-locale'
|
||||
import { loadUser, storeUser, clearUser, clearMineList } from '@/utils/storage';
|
||||
import { loadUser, storeUser, clearUser, clearMineList, clearCoins } from '@/utils/storage';
|
||||
import { User } from '@/types/common';
|
||||
|
||||
const initState = () => {
|
||||
@@ -46,6 +46,7 @@ const model: Model = {
|
||||
|
||||
*logout(_, { put, all }) {
|
||||
clearMineList();
|
||||
clearCoins();
|
||||
yield all([
|
||||
put({ type: 'clearUser' }),
|
||||
put({ type: 'home/clear' }),
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +141,20 @@ export interface Mine {
|
||||
|
||||
export type Mines = Mine[];
|
||||
|
||||
export interface CoinSupport {
|
||||
id: string;
|
||||
label: string;
|
||||
rate: string;
|
||||
}
|
||||
|
||||
export interface CoinAddress {
|
||||
id: string;
|
||||
label: string;
|
||||
address: string;
|
||||
}
|
||||
|
||||
export interface CoinUnion extends CoinSupport, CoinAddress {};
|
||||
|
||||
export enum MarketEstateAvailable {
|
||||
No = 0,
|
||||
Yes = 1,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
const userKey = 'user-key';
|
||||
const areaCodeKey = 'area-code-key';
|
||||
const mineListKey = 'mine-list-key';
|
||||
const coinsKey = 'coin-key';
|
||||
|
||||
// user info from now on
|
||||
function storeUser(user: any = undefined) {
|
||||
@@ -54,6 +55,23 @@ function clearMineList() {
|
||||
localStorage.removeItem(mineListKey);
|
||||
}
|
||||
|
||||
// coin list info from now on
|
||||
function storeCoins(coins: any) {
|
||||
localStorage.setItem(coinsKey, JSON.stringify(coins));
|
||||
}
|
||||
|
||||
function loadCoins() {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem(coinsKey) || 'null');
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function clearCoins() {
|
||||
localStorage.removeItem(coinsKey);
|
||||
}
|
||||
|
||||
export {
|
||||
storeUser,
|
||||
loadUser,
|
||||
@@ -65,4 +83,8 @@ export {
|
||||
storeMineList,
|
||||
loadMineList,
|
||||
clearMineList,
|
||||
|
||||
storeCoins,
|
||||
loadCoins,
|
||||
clearCoins,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user