Fund list
This commit is contained in:
@@ -30,7 +30,7 @@ export interface ListProps<T = any> {
|
|||||||
enableRefresh?: boolean;
|
enableRefresh?: boolean;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
enableLoading?: boolean;
|
enableLoading?: boolean;
|
||||||
data?: T[];
|
data?: T[] | null;
|
||||||
onRefresh?: ActionCallback;
|
onRefresh?: ActionCallback;
|
||||||
onLoadMore?: ActionCallback;
|
onLoadMore?: ActionCallback;
|
||||||
renderItem?: (item: T, index: number, array: T[]) => React.ReactNode;
|
renderItem?: (item: T, index: number, array: T[]) => React.ReactNode;
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ body {
|
|||||||
|
|
||||||
@purple-one: #c17ffe;
|
@purple-one: #c17ffe;
|
||||||
@purple-two: #7483f6;
|
@purple-two: #7483f6;
|
||||||
|
@blue-one: #21b4fc;
|
||||||
|
@blue-two: #7483f6;
|
||||||
|
|
||||||
@header-height: 54px;
|
@header-height: 54px;
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,15 @@ export default {
|
|||||||
'home.mine.status1': '正在挖矿',
|
'home.mine.status1': '正在挖矿',
|
||||||
'home.mine.status2': '已耗尽能源',
|
'home.mine.status2': '已耗尽能源',
|
||||||
|
|
||||||
|
'home.fund.total': 'Total',
|
||||||
|
'home.fund.totalVIC': '当前VIC余额:',
|
||||||
|
'home.fund.totalUSDT': '当前USDT余额:',
|
||||||
|
'home.fund.draw': 'Draw',
|
||||||
|
'home.fund.journal': 'Journals',
|
||||||
|
'home.fund.type1': '挖矿收益',
|
||||||
|
'home.fund.type2': '社区奖励',
|
||||||
|
'home.fund.type3': '提币',
|
||||||
|
|
||||||
'estate.tab1': '买卖记录',
|
'estate.tab1': '买卖记录',
|
||||||
'estate.tab2': '地块介绍',
|
'estate.tab2': '地块介绍',
|
||||||
'estate.title1': '买入信息',
|
'estate.title1': '买入信息',
|
||||||
|
|||||||
@@ -61,6 +61,15 @@ export default {
|
|||||||
'home.mine.status1': '正在挖矿',
|
'home.mine.status1': '正在挖矿',
|
||||||
'home.mine.status2': '已耗尽能源',
|
'home.mine.status2': '已耗尽能源',
|
||||||
|
|
||||||
|
'home.fund.total': '总资金',
|
||||||
|
'home.fund.totalVIC': '当前VIC余额:',
|
||||||
|
'home.fund.totalUSDT': '当前USDT余额:',
|
||||||
|
'home.fund.draw': '提币',
|
||||||
|
'home.fund.journal': '收支明细',
|
||||||
|
'home.fund.type1': '挖矿收益',
|
||||||
|
'home.fund.type2': '社区奖励',
|
||||||
|
'home.fund.type3': '提币',
|
||||||
|
|
||||||
'estate.tab1': '买卖记录',
|
'estate.tab1': '买卖记录',
|
||||||
'estate.tab2': '地块介绍',
|
'estate.tab2': '地块介绍',
|
||||||
'estate.title1': '买入信息',
|
'estate.title1': '买入信息',
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Model, routerRedux } from 'dva';
|
import { Model } from 'dva';
|
||||||
import { message } from 'antd';
|
|
||||||
import { Mines, MineBasic } from '@/types/common';
|
import { Mines, MineBasic } from '@/types/common';
|
||||||
import { getMineList, getMineBasic } from './service';
|
import { getMineList, getMineBasic } from './service';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,114 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
|
import { connect, DispatchProp, routerRedux } from 'dva';
|
||||||
|
import { Spin, Icon } from 'antd';
|
||||||
|
import { formatMessage } from 'umi-plugin-locale';
|
||||||
|
import { HomeFundModelState } from './model';
|
||||||
|
import { Journal, JournalOp } from '@/types/common';
|
||||||
|
import Label from '@/components/Label';
|
||||||
|
import List from '@/components/List';
|
||||||
|
import { dispatch } from '@/utils/utils';
|
||||||
|
|
||||||
const FundPage: React.FC = () => {
|
import styles from './style.less';
|
||||||
return <div>Fund</div>
|
|
||||||
|
const FundPage: React.FC<HomeFundModelState & DispatchProp> = ({
|
||||||
|
dispatch,
|
||||||
|
refreshingList,
|
||||||
|
loadingList,
|
||||||
|
loadingFinished,
|
||||||
|
errorList,
|
||||||
|
data,
|
||||||
|
refreshingBasic,
|
||||||
|
errorBasic,
|
||||||
|
info,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!data || errorList) {
|
||||||
|
dispatch({ type: 'homeFund/loadList', payload: true });
|
||||||
}
|
}
|
||||||
|
if (!info || errorBasic) {
|
||||||
|
dispatch({ type: 'homeFund/loadBasic' });
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
export default FundPage;
|
const onRefresh = () => {
|
||||||
|
if (refreshingBasic || refreshingList) return;
|
||||||
|
dispatch({ type: 'homeFund/loadList', payload: true });
|
||||||
|
dispatch({ type: 'homeFund/loadBasic' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderBanner = (
|
||||||
|
<div className={styles.banner}>
|
||||||
|
<div className="bg" />
|
||||||
|
<span className="title">{formatMessage({ id: 'home.fund.total' })}</span>
|
||||||
|
<span className="content">
|
||||||
|
<span className="large">{info ? info.balanceSum : 0}</span>
|
||||||
|
VIC
|
||||||
|
</span>
|
||||||
|
<div className={styles.card}>
|
||||||
|
<div className="left">
|
||||||
|
<span className="line">
|
||||||
|
<span className="title2">{formatMessage({ id: 'home.fund.totalVIC' })}</span>
|
||||||
|
<span className="value">+{info ? info.balanceVIC : 0} VIC</span>
|
||||||
|
</span>
|
||||||
|
<span className="line">
|
||||||
|
<span className="title2">{formatMessage({ id: 'home.fund.totalUSDT' })}</span>
|
||||||
|
<span className="value">+{info ? info.balanceUSDT : 0} USDT</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="right app-btn"
|
||||||
|
>
|
||||||
|
{formatMessage({ id: 'home.fund.draw' })}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderItem = (it: Journal) => (
|
||||||
|
<div className={styles.item} key={it.id}>
|
||||||
|
<div className="line">
|
||||||
|
<div>{formatMessage({ id: `home.fund.type${it.type}` })}</div>
|
||||||
|
<div className={`amount ${it.op === JournalOp.Income ? 'active' : ''}`}>
|
||||||
|
{it.op === JournalOp.Income ? '+' : '-'}{it.amount}
|
||||||
|
{it.coinType.toUpperCase()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="line">
|
||||||
|
<div className="detail">
|
||||||
|
{it.detail}
|
||||||
|
</div>
|
||||||
|
<div className="small">{it.timestamp}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
{renderBanner}
|
||||||
|
|
||||||
|
<Label
|
||||||
|
className={styles.controller}
|
||||||
|
name={formatMessage({ id: 'home.fund.journal' })}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<List
|
||||||
|
className={styles.list}
|
||||||
|
data={data || []}
|
||||||
|
renderItem={renderItem}
|
||||||
|
refreshing={refreshingList}
|
||||||
|
onRefresh={onRefresh}
|
||||||
|
loading={loadingList}
|
||||||
|
enableLoading={!loadingFinished}
|
||||||
|
onLoadMore={() => dispatch({ type: 'homeFund/loadList' })}
|
||||||
|
itemKey={data => data.id}
|
||||||
|
itemSize={80}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
({ homeFund } : any) => ({ ...homeFund }),
|
||||||
|
)(FundPage);
|
||||||
|
|||||||
114
src/pages/home/fund/model.ts
Normal file
114
src/pages/home/fund/model.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { Model, routerRedux } from 'dva';
|
||||||
|
import { message } from 'antd';
|
||||||
|
import { Journal, FundBasic } from '@/types/common';
|
||||||
|
import { getFundList, getFundBasic } from './service';
|
||||||
|
|
||||||
|
export interface HomeFundModelState {
|
||||||
|
refreshingList: boolean;
|
||||||
|
loadingList: boolean;
|
||||||
|
loadingFinished: boolean;
|
||||||
|
errorList: boolean;
|
||||||
|
data: Journal[] | null;
|
||||||
|
|
||||||
|
refreshingBasic: boolean;
|
||||||
|
errorBasic: boolean;
|
||||||
|
info: FundBasic | null;
|
||||||
|
|
||||||
|
pageIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FundModel: Model = {
|
||||||
|
namespace: 'homeFund',
|
||||||
|
|
||||||
|
state: {
|
||||||
|
refreshingList: false,
|
||||||
|
loadingList: false,
|
||||||
|
loadingFinished: false,
|
||||||
|
errorList: false,
|
||||||
|
data: null,
|
||||||
|
|
||||||
|
refreshingBasic: false,
|
||||||
|
errorBasic: false,
|
||||||
|
info: null,
|
||||||
|
|
||||||
|
pageIndex: 1,
|
||||||
|
} as HomeFundModelState,
|
||||||
|
|
||||||
|
effects: {
|
||||||
|
*loadList({ payload: refresh }, { put, call, select }) {
|
||||||
|
const {
|
||||||
|
refreshingList,
|
||||||
|
loadingList,
|
||||||
|
pageIndex,
|
||||||
|
data,
|
||||||
|
} = yield select((state: any) => state.homeFund);
|
||||||
|
if (refreshingList || loadingList) return;
|
||||||
|
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
refreshingList: refresh,
|
||||||
|
loadingList: !refresh,
|
||||||
|
errorList: false,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const pageSize = 10;
|
||||||
|
const page = refresh ? 1 : pageIndex + 1;
|
||||||
|
const list = yield call(getFundList, page, pageSize);
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
data: refresh ? list : [...data, ...list],
|
||||||
|
refreshingList: false,
|
||||||
|
loadingList: false,
|
||||||
|
pageIndex: page,
|
||||||
|
errorList: false,
|
||||||
|
loadingFinished: list.length !== pageSize,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
message.error(e.message);
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
refreshingList: false,
|
||||||
|
loadingList: false,
|
||||||
|
errorList: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
*loadBasic(_, { put, call, select }) {
|
||||||
|
const { refreshingBasic } = yield select((state: any) => state.homeFund);
|
||||||
|
if (refreshingBasic) return;
|
||||||
|
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
refreshingBasic: true,
|
||||||
|
errorBasic: false,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
const info = yield call(getFundBasic);
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
info,
|
||||||
|
refreshingBasic: false,
|
||||||
|
errorBasic: false,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
yield put({
|
||||||
|
type: 'onUpdateState',
|
||||||
|
refreshingBasic: false,
|
||||||
|
errorBasic: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
reducers: {
|
||||||
|
onUpdateState(state, payload) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...payload,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FundModel;
|
||||||
29
src/pages/home/fund/service.ts
Normal file
29
src/pages/home/fund/service.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import http from '@/utils/http';
|
||||||
|
import { Journal, FundBasic } 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[]> {
|
||||||
|
const {
|
||||||
|
ok,
|
||||||
|
data,
|
||||||
|
msg,
|
||||||
|
} = await http.get<Journal[]>('/fund/journals', {
|
||||||
|
pageIndex,
|
||||||
|
pageSize,
|
||||||
|
});
|
||||||
|
if (ok && data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
throw Error(msg);
|
||||||
|
}
|
||||||
154
src/pages/home/fund/style.less
Normal file
154
src/pages/home/fund/style.less
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
@import '../../../global.less';
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.banner {
|
||||||
|
flex-shrink: 0;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.bg {
|
||||||
|
z-index: -1;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 90%;
|
||||||
|
background: linear-gradient(to bottom, @blue-one, @blue-two);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title, .content {
|
||||||
|
color: white;
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
margin: 10px 0 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content .large {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
margin-right: 6px;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: stretch;
|
||||||
|
background-color: white;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 0 10px;
|
||||||
|
box-shadow: 1px 1px 8px rgba(0,0,0,.1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.left {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line {
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title2 {
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-btn {
|
||||||
|
user-select: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
background-color: @blue-one;
|
||||||
|
@btn-height: 28px;
|
||||||
|
height: @btn-height;
|
||||||
|
line-height: @btn-height;
|
||||||
|
padding: 0 24px;
|
||||||
|
border-radius: @btn-height / 2;
|
||||||
|
color: white;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
box-shadow: 1px 1px 8px rgba(0,0,0,.2);
|
||||||
|
transition: background-color .18s;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: lighten(@blue-one, 10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.controller {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
height: 98%;
|
||||||
|
margin: 0 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
border: 1px solid @bg-color;
|
||||||
|
padding: 8px 10px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.8;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.line {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amount {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
.amount.active {
|
||||||
|
color: @blue-one;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-weight: lighter;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,18 +30,18 @@ const tabItems: TabItemModels = [
|
|||||||
icon: estateIcon,
|
icon: estateIcon,
|
||||||
iconSelected: estateIconS,
|
iconSelected: estateIconS,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: '/home/market',
|
|
||||||
name: 'home.tabMarket',
|
|
||||||
icon: marketIcon,
|
|
||||||
iconSelected: marketIconS,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: '/home/fund',
|
key: '/home/fund',
|
||||||
name: 'home.tabFund',
|
name: 'home.tabFund',
|
||||||
icon: fundIcon,
|
icon: fundIcon,
|
||||||
iconSelected: fundIconS,
|
iconSelected: fundIconS,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: '/home/market',
|
||||||
|
name: 'home.tabMarket',
|
||||||
|
icon: marketIcon,
|
||||||
|
iconSelected: marketIconS,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: '/home/profile',
|
key: '/home/profile',
|
||||||
name: 'home.tabProfile',
|
name: 'home.tabProfile',
|
||||||
|
|||||||
@@ -155,6 +155,61 @@ export interface CoinAddress {
|
|||||||
|
|
||||||
export interface CoinUnion extends CoinSupport, CoinAddress { };
|
export interface CoinUnion extends CoinSupport, CoinAddress { };
|
||||||
|
|
||||||
|
export interface FundBasic {
|
||||||
|
/**
|
||||||
|
* 总余额(以VIC计算)
|
||||||
|
*/
|
||||||
|
balanceSum: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前VIC余额
|
||||||
|
*/
|
||||||
|
balanceVIC: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前USDT余额
|
||||||
|
*/
|
||||||
|
balanceUSDT: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum JournalType {
|
||||||
|
/**
|
||||||
|
* 挖矿收益
|
||||||
|
*/
|
||||||
|
Mining = 1,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 社区奖励
|
||||||
|
*/
|
||||||
|
Award = 2,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提币
|
||||||
|
*/
|
||||||
|
Draw = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum JournalOp {
|
||||||
|
Expenditure = 0,
|
||||||
|
Income = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Journal {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
type: JournalType;
|
||||||
|
|
||||||
|
detail: string;
|
||||||
|
|
||||||
|
amount: string;
|
||||||
|
|
||||||
|
coinType: string;
|
||||||
|
|
||||||
|
op: JournalOp;
|
||||||
|
|
||||||
|
timestamp: string;
|
||||||
|
}
|
||||||
|
|
||||||
export enum MarketEstateAvailable {
|
export enum MarketEstateAvailable {
|
||||||
No = 0,
|
No = 0,
|
||||||
Yes = 1,
|
Yes = 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user