Reinit project
This commit is contained in:
40
src/pages/home/_layout.tsx
Normal file
40
src/pages/home/_layout.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Routes:
|
||||
* - ./src/pages/routes
|
||||
*/
|
||||
import React from 'react';
|
||||
import { connect, DispatchProp } from 'dva';
|
||||
import { RouterTypes, Redirect } from 'umi';
|
||||
import Tab from '@/components/Tab';
|
||||
|
||||
import styles from './style.less';
|
||||
import { HomeModelState } from './model';
|
||||
|
||||
const HomePage: React.FC<RouterTypes & DispatchProp & HomeModelState> = ({
|
||||
dispatch,
|
||||
tabItems,
|
||||
selectedTab,
|
||||
location: { pathname },
|
||||
children,
|
||||
}) => {
|
||||
if (pathname === '/home') return <Redirect to="/home/home" />
|
||||
|
||||
return (
|
||||
<div className={styles.home}>
|
||||
<section className={styles.page}>
|
||||
{children}
|
||||
</section>
|
||||
<footer className={styles.tab}>
|
||||
<Tab
|
||||
items={tabItems}
|
||||
selectedKey={selectedTab}
|
||||
onChange={payload => dispatch({ type: 'home/changeTab', payload })}
|
||||
/>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
({ home }: any) => ({ ...home }),
|
||||
)(HomePage);
|
||||
103
src/pages/home/estate/index.tsx
Normal file
103
src/pages/home/estate/index.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Radio } from 'antd';
|
||||
import FittedImage from 'react-fitted-image';
|
||||
import { connect, DispatchProp } from 'dva';
|
||||
import { formatMessage } from 'umi-plugin-locale';
|
||||
import List from '@/components/List';
|
||||
import Label from '@/components/Label';
|
||||
import { HomeEstateModelState } from './model';
|
||||
import { EstateType, Estate } from '@/types/common';
|
||||
import { dispatch } from '@/utils/utils';
|
||||
|
||||
import styles from './style.less';
|
||||
|
||||
const RadioGroup = Radio.Group;
|
||||
const RadioButton = Radio.Button;
|
||||
|
||||
const renderItem = (data: Estate) => {
|
||||
const holdTab = data.type === EstateType.Hold;
|
||||
return (
|
||||
<div
|
||||
className={styles.item}
|
||||
onClick={() => dispatch({ type: 'homeEstate/gotoDetail', payload: data })}
|
||||
>
|
||||
<div className="avatar">
|
||||
<FittedImage src={data.image || ''} fit="cover" />
|
||||
</div>
|
||||
<div className="content">
|
||||
<span className="name">{data.name}</span>
|
||||
<span className="desc">
|
||||
{
|
||||
holdTab ?
|
||||
formatMessage({ id: 'home.estate.timeDue' }, { time: data.lockTime }) :
|
||||
formatMessage({ id: 'home.estate.timeSold' }, { time: data.outTime })
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<div className="postfix">
|
||||
<span className={`cost-msg ${holdTab ? 'primary' : ''}`}>
|
||||
{formatMessage({ id: 'home.estate.cost' })} <span>{data.inPrice}</span> LOG
|
||||
</span>
|
||||
{!holdTab && <span className="sold-msg">
|
||||
{formatMessage({ id: 'home.estate.profit' })} {data.profitPrice} LOG
|
||||
</span>}
|
||||
{holdTab && <span className="hold-msg">
|
||||
{formatMessage({ id: 'home.estate.profit2' })}{data.profitShow}
|
||||
</span>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const EstatePage: React.FC<HomeEstateModelState & DispatchProp> = ({
|
||||
dispatch,
|
||||
loading,
|
||||
refreshing,
|
||||
tab,
|
||||
data,
|
||||
num,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
if (!data.length) {
|
||||
dispatch({ type: 'homeEstate/refresh' });
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.header}>
|
||||
<RadioGroup
|
||||
buttonStyle="solid"
|
||||
value={tab}
|
||||
onChange={e => dispatch({ type: 'homeEstate/changeTab', payload: e.target.value })}
|
||||
>
|
||||
<RadioButton value={EstateType.Hold}>{formatMessage({ id: 'home.estate.hold' })}</RadioButton>
|
||||
<RadioButton value={EstateType.Sold}>{formatMessage({ id: 'home.estate.sold' })}</RadioButton>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
<Label
|
||||
name={formatMessage({
|
||||
id: tab === EstateType.Hold ? 'home.estate.holdTitle' : 'home.estate.soldTitle',
|
||||
}, { num })}
|
||||
className={styles.label}
|
||||
/>
|
||||
|
||||
<List
|
||||
className={styles.list}
|
||||
data={data}
|
||||
renderItem={renderItem}
|
||||
refreshing={refreshing}
|
||||
onRefresh={() => dispatch({ type: 'homeEstate/refresh' })}
|
||||
loading={loading}
|
||||
onLoadMore={() => dispatch({ type: 'homeEstate/loadMore' })}
|
||||
itemKey={data => data.id}
|
||||
itemSize={80}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(
|
||||
({ homeEstate } : any) => ({ ...homeEstate }),
|
||||
)(EstatePage);
|
||||
110
src/pages/home/estate/model.ts
Normal file
110
src/pages/home/estate/model.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { Model, routerRedux } from 'dva';
|
||||
import { message } from 'antd';
|
||||
import { EstateType, Estates } from '@/types/common';
|
||||
import { getEstateList, EstateList } from './service';
|
||||
|
||||
export interface HomeEstateModelState {
|
||||
refreshing: boolean;
|
||||
loading: boolean;
|
||||
loadingFinished: boolean;
|
||||
data: Estates;
|
||||
num: number;
|
||||
pageIndex: number;
|
||||
|
||||
tab: EstateType;
|
||||
}
|
||||
|
||||
const EstateModel: Model = {
|
||||
namespace: 'homeEstate',
|
||||
|
||||
state: {
|
||||
refreshing: false,
|
||||
loading: false,
|
||||
enableLoad: true,
|
||||
loadingFinished: false,
|
||||
data: [],
|
||||
num: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
tab: EstateType.Hold,
|
||||
} as HomeEstateModelState,
|
||||
|
||||
effects: {
|
||||
*refresh(_, { put, call, select }) {
|
||||
const {
|
||||
pageSize,
|
||||
tab,
|
||||
} = yield select((state: any) => state.homeEstate);
|
||||
yield put({ type: 'onUpdateState', payload: { refreshing: true, loading: false } });
|
||||
try {
|
||||
const { total, list }: EstateList = yield call(getEstateList, 1, pageSize, tab);
|
||||
const { tab: newTab } = yield select((state: any) => state.homeEstate);
|
||||
if (tab === newTab) {
|
||||
yield put({
|
||||
type: 'onUpdateState',
|
||||
payload: {
|
||||
refreshing: false,
|
||||
pageIndex: 2,
|
||||
data: list,
|
||||
num: total,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
message.error(e.message);
|
||||
yield put({ type: 'onUpdateState', payload: { refreshing: false, pageIndex: 1 } });
|
||||
}
|
||||
},
|
||||
|
||||
*loadMore(_, { put, call, select }) {
|
||||
const {
|
||||
pageIndex,
|
||||
pageSize,
|
||||
tab,
|
||||
data,
|
||||
} = yield select((state: any) => state.homeEstate);
|
||||
yield put({ type: 'onUpdateState', payload: { loading: true, refreshing: false } });
|
||||
try {
|
||||
const { total, list }: EstateList = yield call(getEstateList, pageIndex, pageSize, tab);
|
||||
const { tab: newTab } = yield select((state: any) => state.homeEstate);
|
||||
if (tab === newTab) {
|
||||
yield put({
|
||||
type: 'onUpdateState',
|
||||
payload: {
|
||||
loading: false,
|
||||
pageIndex: pageIndex + 1,
|
||||
data: [...data, ...list],
|
||||
num: total,
|
||||
enableLoad: list.length === pageSize,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
message.error(e.message);
|
||||
yield put({ type: 'onUpdateState', payload: { loading: false } });
|
||||
}
|
||||
},
|
||||
|
||||
*changeTab({ payload: tab }, { put }) {
|
||||
yield put({ type: 'onUpdateState', payload: { tab, pageIndex: 1, data: [], num: 0 } });
|
||||
yield put({ type: 'refresh' });
|
||||
},
|
||||
|
||||
*gotoDetail({ payload }, { put }) {
|
||||
if (!payload) return;
|
||||
yield put(routerRedux.push('/estate'));
|
||||
yield put({ type: 'ext/onUpdateState', payload: { estateExt: payload } });
|
||||
},
|
||||
},
|
||||
|
||||
reducers: {
|
||||
onUpdateState(state, { payload } : any) {
|
||||
return {
|
||||
...state,
|
||||
...payload,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default EstateModel;
|
||||
22
src/pages/home/estate/service.ts
Normal file
22
src/pages/home/estate/service.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import http from '@/utils/http';
|
||||
import { Estates, EstateType } from '@/types/common';
|
||||
import { calcEstate } from '@/utils/transform';
|
||||
|
||||
export interface EstateList {
|
||||
total: number;
|
||||
list: Estates;
|
||||
}
|
||||
|
||||
export async function getEstateList(pageIndex: number, pageSize: number, type: EstateType) {
|
||||
const {
|
||||
ok,
|
||||
data,
|
||||
msg,
|
||||
} = await http.get<EstateList>('/estateorder/list', { type, pageIndex, pageSize });
|
||||
if (ok && data) {
|
||||
data.list.forEach(it => it.type = type);
|
||||
data.list.forEach(calcEstate);
|
||||
return data;
|
||||
}
|
||||
throw Error(msg);
|
||||
}
|
||||
96
src/pages/home/estate/style.less
Normal file
96
src/pages/home/estate/style.less
Normal file
@@ -0,0 +1,96 @@
|
||||
@import '../../../global.less';
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.header {
|
||||
height: @header-height;
|
||||
line-height: @header-height;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
background-color: @bg-color;
|
||||
}
|
||||
|
||||
.list {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.item {
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
padding: 0 10px;
|
||||
transition: background-color .2s;
|
||||
|
||||
&:active {
|
||||
background-color: @bg-color;
|
||||
}
|
||||
|
||||
:global {
|
||||
.avatar {
|
||||
flex-shrink: 0;
|
||||
align-self: center;
|
||||
@size: 56px;
|
||||
width: @size;
|
||||
height: @size;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 0 10px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
.desc {
|
||||
font-weight: lighter;
|
||||
font-size: 0.8rem;
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.postfix {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-end;
|
||||
font-size: 0.8rem;
|
||||
font-weight: lighter;
|
||||
|
||||
.cost-msg {
|
||||
margin-bottom: 6px;
|
||||
|
||||
&.primary {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
> span {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.hold-msg {
|
||||
background-color: @bg-primary-color;
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
.sold-msg {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/pages/home/fund/index.tsx
Normal file
7
src/pages/home/fund/index.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
const FundPage: React.FC = () => {
|
||||
return <div>Fund</div>
|
||||
}
|
||||
|
||||
export default FundPage;
|
||||
36
src/pages/home/home/index.tsx
Normal file
36
src/pages/home/home/index.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import { formatMessage } from 'umi-plugin-locale';
|
||||
import Label from '@/components/Label';
|
||||
|
||||
import styles from './style.less';
|
||||
|
||||
const AdCard: React.FC = () => (
|
||||
<section className={styles.adcard}>
|
||||
<code>// TODO</code>
|
||||
</section>
|
||||
);
|
||||
|
||||
const HomePage: React.FC = () => {
|
||||
const header = (
|
||||
<div className={styles.header}>
|
||||
<span className="title">{formatMessage({ id: 'home.home.statistic' })}</span>
|
||||
<span className="label">
|
||||
1000
|
||||
<span className="unit">LOG</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{header}
|
||||
<div className={styles.body}>
|
||||
<AdCard />
|
||||
|
||||
<Label name={formatMessage({ id: 'home.home.header' })} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default HomePage;
|
||||
17
src/pages/home/home/model.ts
Normal file
17
src/pages/home/home/model.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Model } from 'dva';
|
||||
|
||||
export interface HomeHomeModelState {
|
||||
total: string;
|
||||
}
|
||||
|
||||
const model: Model = {
|
||||
namespace: 'homeHome',
|
||||
|
||||
state: {},
|
||||
|
||||
effects: {},
|
||||
|
||||
reducers: {},
|
||||
};
|
||||
|
||||
export default model;
|
||||
57
src/pages/home/home/style.less
Normal file
57
src/pages/home/home/style.less
Normal file
@@ -0,0 +1,57 @@
|
||||
@import '../../../global.less';
|
||||
|
||||
@headerHeight: 36%;
|
||||
@offset: 10%;
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: @headerHeight;
|
||||
width: 100%;
|
||||
background: linear-gradient(to right, @primary-color, @secondary-color);
|
||||
color: white;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
:global {
|
||||
.title {
|
||||
font-size: 0.8rem;
|
||||
margin: 14% 0 0;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 2.2rem;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: 100%;
|
||||
padding: @headerHeight + @offset 14px 0;
|
||||
}
|
||||
|
||||
.adcard {
|
||||
box-shadow: 1px 1px 10px lighten(@secondary-color, 18%);
|
||||
border-radius: 4px;
|
||||
height: 96px;
|
||||
background-color: #fff;
|
||||
}
|
||||
17
src/pages/home/market/index.tsx
Normal file
17
src/pages/home/market/index.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import FittedImage from 'react-fitted-image';
|
||||
import banner from '@/assets/m_banner.jpg';
|
||||
|
||||
import styles from './style.less';
|
||||
|
||||
const MarketPage: React.FC = () => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.banner}>
|
||||
<FittedImage src={banner} fit="cover" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MarketPage;
|
||||
110
src/pages/home/market/model.ts
Normal file
110
src/pages/home/market/model.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { Model, routerRedux } from 'dva';
|
||||
import { message } from 'antd';
|
||||
import { EstateType, Estates } from '@/types/common';
|
||||
import { getEstateList, EstateList } from './service';
|
||||
|
||||
export interface HomeMarketModelState {
|
||||
refreshing: boolean;
|
||||
loading: boolean;
|
||||
loadingFinished: boolean;
|
||||
data: Estates;
|
||||
num: number;
|
||||
pageIndex: number;
|
||||
|
||||
tab: EstateType;
|
||||
}
|
||||
|
||||
const MarketModel: Model = {
|
||||
namespace: 'homeMarket',
|
||||
|
||||
state: {
|
||||
refreshing: false,
|
||||
loading: false,
|
||||
enableLoad: true,
|
||||
loadingFinished: false,
|
||||
data: [],
|
||||
num: 0,
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
tab: EstateType.Hold,
|
||||
} as HomeMarketModelState,
|
||||
|
||||
effects: {
|
||||
*refresh(_, { put, call, select }) {
|
||||
const {
|
||||
pageSize,
|
||||
tab,
|
||||
} = yield select((state: any) => state.homeEstate);
|
||||
yield put({ type: 'onUpdateState', payload: { refreshing: true, loading: false } });
|
||||
try {
|
||||
const { total, list }: EstateList = yield call(getEstateList, 1, pageSize, tab);
|
||||
const { tab: newTab } = yield select((state: any) => state.homeEstate);
|
||||
if (tab === newTab) {
|
||||
yield put({
|
||||
type: 'onUpdateState',
|
||||
payload: {
|
||||
refreshing: false,
|
||||
pageIndex: 2,
|
||||
data: list,
|
||||
num: total,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
message.error(e.message);
|
||||
yield put({ type: 'onUpdateState', payload: { refreshing: false, pageIndex: 1 } });
|
||||
}
|
||||
},
|
||||
|
||||
*loadMore(_, { put, call, select }) {
|
||||
const {
|
||||
pageIndex,
|
||||
pageSize,
|
||||
tab,
|
||||
data,
|
||||
} = yield select((state: any) => state.homeEstate);
|
||||
yield put({ type: 'onUpdateState', payload: { loading: true, refreshing: false } });
|
||||
try {
|
||||
const { total, list }: EstateList = yield call(getEstateList, pageIndex, pageSize, tab);
|
||||
const { tab: newTab } = yield select((state: any) => state.homeEstate);
|
||||
if (tab === newTab) {
|
||||
yield put({
|
||||
type: 'onUpdateState',
|
||||
payload: {
|
||||
loading: false,
|
||||
pageIndex: pageIndex + 1,
|
||||
data: [...data, ...list],
|
||||
num: total,
|
||||
enableLoad: list.length === pageSize,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
message.error(e.message);
|
||||
yield put({ type: 'onUpdateState', payload: { loading: false } });
|
||||
}
|
||||
},
|
||||
|
||||
*changeTab({ payload: tab }, { put }) {
|
||||
yield put({ type: 'onUpdateState', payload: { tab, pageIndex: 1, data: [], num: 0 } });
|
||||
yield put({ type: 'refresh' });
|
||||
},
|
||||
|
||||
*gotoDetail({ payload }, { put }) {
|
||||
if (!payload) return;
|
||||
yield put(routerRedux.push('/estate'));
|
||||
yield put({ type: 'ext/onUpdateState', payload: { estateExt: payload } });
|
||||
},
|
||||
},
|
||||
|
||||
reducers: {
|
||||
onUpdateState(state, { payload } : any) {
|
||||
return {
|
||||
...state,
|
||||
...payload,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default MarketModel;
|
||||
22
src/pages/home/market/service.ts
Normal file
22
src/pages/home/market/service.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import http from '@/utils/http';
|
||||
import { Estates, EstateType } from '@/types/common';
|
||||
import { calcEstate } from '@/utils/transform';
|
||||
|
||||
export interface EstateList {
|
||||
total: number;
|
||||
list: Estates;
|
||||
}
|
||||
|
||||
export async function getEstateList(pageIndex: number, pageSize: number, type: EstateType) {
|
||||
const {
|
||||
ok,
|
||||
data,
|
||||
msg,
|
||||
} = await http.get<EstateList>('/estateorder/list', { type, pageIndex, pageSize });
|
||||
if (ok && data) {
|
||||
data.list.forEach(it => it.type = type);
|
||||
data.list.forEach(calcEstate);
|
||||
return data;
|
||||
}
|
||||
throw Error(msg);
|
||||
}
|
||||
8
src/pages/home/market/style.less
Normal file
8
src/pages/home/market/style.less
Normal file
@@ -0,0 +1,8 @@
|
||||
.container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.banner {
|
||||
width: 100%;
|
||||
height: 240px;
|
||||
}
|
||||
107
src/pages/home/model.ts
Normal file
107
src/pages/home/model.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { Model, routerRedux } from "dva";
|
||||
import _ from 'lodash';
|
||||
import { TabItemModels } from '@/components/Tab';
|
||||
import homeIcon from '@/assets/home.svg';
|
||||
import homeIconS from '@/assets/home_s.svg';
|
||||
import estateIcon from '@/assets/estate.svg';
|
||||
import estateIconS from '@/assets/estate_s.svg';
|
||||
import marketIcon from '@/assets/market.svg';
|
||||
import marketIconS from '@/assets/market_s.svg';
|
||||
import fundIcon from '@/assets/fund.svg';
|
||||
import fundIconS from '@/assets/fund_s.svg';
|
||||
import profileIcon from '@/assets/profile.svg';
|
||||
import profileIconS from '@/assets/profile_s.svg';
|
||||
|
||||
export interface HomeModelState {
|
||||
tabItems: TabItemModels;
|
||||
selectedTab: string;
|
||||
}
|
||||
|
||||
const tabItems: TabItemModels = [
|
||||
{
|
||||
key: '/home/home',
|
||||
name: 'home.tabHome',
|
||||
icon: homeIcon,
|
||||
iconSelected: homeIconS,
|
||||
},
|
||||
{
|
||||
key: '/home/estate',
|
||||
name: 'home.tabEstate',
|
||||
icon: estateIcon,
|
||||
iconSelected: estateIconS,
|
||||
},
|
||||
{
|
||||
key: '/home/market',
|
||||
name: 'home.tabMarket',
|
||||
icon: marketIcon,
|
||||
iconSelected: marketIconS,
|
||||
},
|
||||
{
|
||||
key: '/home/fund',
|
||||
name: 'home.tabFund',
|
||||
icon: fundIcon,
|
||||
iconSelected: fundIconS,
|
||||
},
|
||||
{
|
||||
key: '/home/profile',
|
||||
name: 'home.tabProfile',
|
||||
icon: profileIcon,
|
||||
iconSelected: profileIconS,
|
||||
},
|
||||
];
|
||||
|
||||
const initState: HomeModelState = {
|
||||
tabItems,
|
||||
selectedTab: tabItems[0].key,
|
||||
};
|
||||
|
||||
const model: Model = {
|
||||
namespace: 'home',
|
||||
|
||||
state: initState,
|
||||
|
||||
effects: {
|
||||
*changeTab({ payload: selectedTab }, { put, select, all }) {
|
||||
const {
|
||||
router: { location: { pathname } },
|
||||
home: { selectedTab: oldSelectedTab },
|
||||
} = yield select();
|
||||
|
||||
const tasks = [];
|
||||
if (oldSelectedTab !== selectedTab) {
|
||||
tasks.push(put({ type: 'onUpdateState', payload: { selectedTab } }));
|
||||
}
|
||||
if (pathname !== selectedTab) {
|
||||
tasks.push(put(routerRedux.replace(selectedTab)));
|
||||
}
|
||||
|
||||
if (tasks.length) {
|
||||
yield all(tasks);
|
||||
}
|
||||
},
|
||||
|
||||
*clear(_, { put }) {
|
||||
yield put({ type: 'onUpdateState', payload: initState })
|
||||
},
|
||||
},
|
||||
|
||||
reducers: {
|
||||
onUpdateState(state, { payload }: any) {
|
||||
return {
|
||||
...state,
|
||||
...payload,
|
||||
};
|
||||
},
|
||||
},
|
||||
|
||||
subscriptions: {
|
||||
onRouteChange({ dispatch, history }) {
|
||||
const { pathname } = history.location;
|
||||
if (_.some(tabItems, it => it.key === pathname)) {
|
||||
dispatch({ type: 'changeTab', payload: pathname });
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default model;
|
||||
73
src/pages/home/profile/index.tsx
Normal file
73
src/pages/home/profile/index.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import React from 'react';
|
||||
import FittedImage from 'react-fitted-image';
|
||||
import { connect, DispatchProp, routerRedux } from 'dva';
|
||||
import { UserModelState } from '@/models/user';
|
||||
import SettingItem from '@/components/SettingItem';
|
||||
import logoAccount from '@/assets/p_account.png';
|
||||
import logoSystem from '@/assets/p_system.png';
|
||||
import logoIdentity from '@/assets/p_identity.png';
|
||||
import logoMsg from '@/assets/p_msg.png';
|
||||
import logoAbout from '@/assets/p_about.png';
|
||||
|
||||
import styles from './style.less';
|
||||
|
||||
const ProfilePage: React.FC<UserModelState & DispatchProp> = ({
|
||||
dispatch,
|
||||
user,
|
||||
}) => {
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
const bannerElement = (
|
||||
<div className={styles.banner}>
|
||||
<div className={styles.info}>
|
||||
<div className="avatar">
|
||||
{user.avatar ?
|
||||
<FittedImage src={user.avatar} fit="cover" /> :
|
||||
<span>{user.nickname ? user.nickname[0] : user.phone[0]}</span>
|
||||
}
|
||||
</div>
|
||||
<span className="name">{user.nickname || user.phone}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{bannerElement}
|
||||
<div className={styles.content}>
|
||||
<SettingItem
|
||||
className="group"
|
||||
logo={logoAccount}
|
||||
title="profile.account"
|
||||
onClick={() => dispatch(routerRedux.push('/account'))}
|
||||
/>
|
||||
<SettingItem
|
||||
className="group"
|
||||
logo={logoSystem}
|
||||
title="profile.system"
|
||||
onClick={() => dispatch(routerRedux.push('/system'))}
|
||||
/>
|
||||
<SettingItem
|
||||
className="group"
|
||||
logo={logoIdentity}
|
||||
title="profile.identity"
|
||||
/>
|
||||
<SettingItem
|
||||
className="group"
|
||||
logo={logoMsg}
|
||||
title="profile.msg"
|
||||
/>
|
||||
<SettingItem
|
||||
className="group"
|
||||
logo={logoAbout}
|
||||
title="profile.about"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
({ user }: any) => ({ ...user }),
|
||||
)(ProfilePage);
|
||||
60
src/pages/home/profile/style.less
Normal file
60
src/pages/home/profile/style.less
Normal file
@@ -0,0 +1,60 @@
|
||||
@import '../../../global.less';
|
||||
|
||||
.container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.banner {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.info {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
transform: translateY(-55%);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
:global {
|
||||
.avatar {
|
||||
@size: 68px;
|
||||
width: @size;
|
||||
height: @size;
|
||||
overflow: hidden;
|
||||
color: @primary-color;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: @bg-color;
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
border-radius: 50%;
|
||||
border: 2px solid @primary-color;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-top: 8px;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
|
||||
:global {
|
||||
.group {
|
||||
margin: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/pages/home/style.less
Normal file
19
src/pages/home/style.less
Normal file
@@ -0,0 +1,19 @@
|
||||
@import '../../global.less';
|
||||
@tab-height: 64px;
|
||||
|
||||
.home {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.page {
|
||||
height: 100%;
|
||||
padding-bottom: @tab-height;
|
||||
}
|
||||
|
||||
.tab {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: @tab-height;
|
||||
}
|
||||
Reference in New Issue
Block a user