Browse Source
* features add userinfo page * Delete unnecessary objects * Modify the title style * Meun using antd * userinfo/base/:page -> userinfo/:page * itemview -> list * userinfo add upload * meun -> menu * use getRoutes & rename filespull/1058/head
committed by
ddcat1115
20 changed files with 2728 additions and 50 deletions
File diff suppressed because it is too large
@ -0,0 +1,19 @@ |
|||
const fs = require('fs'); |
|||
|
|||
function getJson(infoType) { |
|||
const json = fs.readFileSync(`${__dirname}/${infoType}.json`, 'utf8'); |
|||
return JSON.parse(json); |
|||
} |
|||
|
|||
export function getProvince(req, res) { |
|||
res.json(getJson('province')); |
|||
} |
|||
|
|||
export function getCity(req, res) { |
|||
res.json(getJson('city')[req.params.province]); |
|||
} |
|||
|
|||
export default { |
|||
getProvince, |
|||
getCity, |
|||
}; |
|||
@ -0,0 +1,138 @@ |
|||
[ |
|||
{ |
|||
"name": "北京市", |
|||
"id": "110000" |
|||
}, |
|||
{ |
|||
"name": "天津市", |
|||
"id": "120000" |
|||
}, |
|||
{ |
|||
"name": "河北省", |
|||
"id": "130000" |
|||
}, |
|||
{ |
|||
"name": "山西省", |
|||
"id": "140000" |
|||
}, |
|||
{ |
|||
"name": "内蒙古自治区", |
|||
"id": "150000" |
|||
}, |
|||
{ |
|||
"name": "辽宁省", |
|||
"id": "210000" |
|||
}, |
|||
{ |
|||
"name": "吉林省", |
|||
"id": "220000" |
|||
}, |
|||
{ |
|||
"name": "黑龙江省", |
|||
"id": "230000" |
|||
}, |
|||
{ |
|||
"name": "上海市", |
|||
"id": "310000" |
|||
}, |
|||
{ |
|||
"name": "江苏省", |
|||
"id": "320000" |
|||
}, |
|||
{ |
|||
"name": "浙江省", |
|||
"id": "330000" |
|||
}, |
|||
{ |
|||
"name": "安徽省", |
|||
"id": "340000" |
|||
}, |
|||
{ |
|||
"name": "福建省", |
|||
"id": "350000" |
|||
}, |
|||
{ |
|||
"name": "江西省", |
|||
"id": "360000" |
|||
}, |
|||
{ |
|||
"name": "山东省", |
|||
"id": "370000" |
|||
}, |
|||
{ |
|||
"name": "河南省", |
|||
"id": "410000" |
|||
}, |
|||
{ |
|||
"name": "湖北省", |
|||
"id": "420000" |
|||
}, |
|||
{ |
|||
"name": "湖南省", |
|||
"id": "430000" |
|||
}, |
|||
{ |
|||
"name": "广东省", |
|||
"id": "440000" |
|||
}, |
|||
{ |
|||
"name": "广西壮族自治区", |
|||
"id": "450000" |
|||
}, |
|||
{ |
|||
"name": "海南省", |
|||
"id": "460000" |
|||
}, |
|||
{ |
|||
"name": "重庆市", |
|||
"id": "500000" |
|||
}, |
|||
{ |
|||
"name": "四川省", |
|||
"id": "510000" |
|||
}, |
|||
{ |
|||
"name": "贵州省", |
|||
"id": "520000" |
|||
}, |
|||
{ |
|||
"name": "云南省", |
|||
"id": "530000" |
|||
}, |
|||
{ |
|||
"name": "西藏自治区", |
|||
"id": "540000" |
|||
}, |
|||
{ |
|||
"name": "陕西省", |
|||
"id": "610000" |
|||
}, |
|||
{ |
|||
"name": "甘肃省", |
|||
"id": "620000" |
|||
}, |
|||
{ |
|||
"name": "青海省", |
|||
"id": "630000" |
|||
}, |
|||
{ |
|||
"name": "宁夏回族自治区", |
|||
"id": "640000" |
|||
}, |
|||
{ |
|||
"name": "新疆维吾尔自治区", |
|||
"id": "650000" |
|||
}, |
|||
{ |
|||
"name": "台湾省", |
|||
"id": "710000" |
|||
}, |
|||
{ |
|||
"name": "香港特别行政区", |
|||
"id": "810000" |
|||
}, |
|||
{ |
|||
"name": "澳门特别行政区", |
|||
"id": "820000" |
|||
} |
|||
] |
|||
@ -0,0 +1,65 @@ |
|||
import { queryProvince, queryCity } from '../services/geographic'; |
|||
|
|||
export default { |
|||
namespace: 'geographic', |
|||
|
|||
state: { |
|||
province: [], |
|||
city: [], |
|||
isLoading: false, |
|||
}, |
|||
|
|||
effects: { |
|||
*fetchProvince(_, { call, put }) { |
|||
yield put({ |
|||
type: 'changeLoading', |
|||
payload: true, |
|||
}); |
|||
const response = yield call(queryProvince); |
|||
yield put({ |
|||
type: 'setProvince', |
|||
payload: response, |
|||
}); |
|||
yield put({ |
|||
type: 'changeLoading', |
|||
payload: false, |
|||
}); |
|||
}, |
|||
*fetchCity({ payload }, { call, put }) { |
|||
yield put({ |
|||
type: 'changeLoading', |
|||
payload: true, |
|||
}); |
|||
const response = yield call(queryCity, payload); |
|||
yield put({ |
|||
type: 'setCity', |
|||
payload: response, |
|||
}); |
|||
yield put({ |
|||
type: 'changeLoading', |
|||
payload: false, |
|||
}); |
|||
}, |
|||
}, |
|||
|
|||
reducers: { |
|||
setProvince(state, action) { |
|||
return { |
|||
...state, |
|||
province: action.payload, |
|||
}; |
|||
}, |
|||
setCity(state, action) { |
|||
return { |
|||
...state, |
|||
city: action.payload, |
|||
}; |
|||
}, |
|||
changeLoading(state, action) { |
|||
return { |
|||
...state, |
|||
isLoading: action.payload, |
|||
}; |
|||
}, |
|||
}, |
|||
}; |
|||
@ -0,0 +1,46 @@ |
|||
import React, { Component, Fragment } from 'react'; |
|||
import { Icon, List } from 'antd'; |
|||
|
|||
export default class AccountView extends Component { |
|||
getData = () => { |
|||
return [ |
|||
{ |
|||
title: '绑定淘宝', |
|||
description: '当前未绑定淘宝账号', |
|||
actions: [<a>绑定</a>], |
|||
avatar: <Icon type="taobao" className="taobao" />, |
|||
}, |
|||
{ |
|||
title: '绑定支付宝', |
|||
description: '当前未绑定支付宝账号', |
|||
actions: [<a>绑定</a>], |
|||
avatar: <Icon type="alipay" className="alipay" />, |
|||
}, |
|||
{ |
|||
title: '绑定钉钉', |
|||
description: '当前未绑定钉钉账号', |
|||
actions: [<a>绑定</a>], |
|||
avatar: <Icon type="dingding" className="dingding" />, |
|||
}, |
|||
]; |
|||
}; |
|||
render() { |
|||
return ( |
|||
<Fragment> |
|||
<List |
|||
itemLayout="horizontal" |
|||
dataSource={this.getData()} |
|||
renderItem={item => ( |
|||
<List.Item actions={item.actions}> |
|||
<List.Item.Meta |
|||
avatar={item.avatar} |
|||
title={item.title} |
|||
description={item.description} |
|||
/> |
|||
</List.Item> |
|||
)} |
|||
/> |
|||
</Fragment> |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,147 @@ |
|||
import React, { Component, Fragment } from 'react'; |
|||
import { Form, Input, Upload, Select, Button } from 'antd'; |
|||
import styles from './BaseView.less'; |
|||
import GeographicView from './GeographicView'; |
|||
import PhoneView from './PhoneView'; |
|||
|
|||
const FormItem = Form.Item; |
|||
const { Option } = Select; |
|||
|
|||
// 头像组件 方便以后独立,增加裁剪之类的功能
|
|||
const AvatarView = ({ avatar }) => ( |
|||
<Fragment> |
|||
<div className={styles.avatar_title}>头像</div> |
|||
<div className={styles.avatar}> |
|||
<img src={avatar} alt="avatar" /> |
|||
</div> |
|||
<Upload fileList={[]}> |
|||
<div className={styles.button_view}> |
|||
<Button icon="upload">更换头像</Button> |
|||
</div> |
|||
</Upload> |
|||
</Fragment> |
|||
); |
|||
|
|||
const validatorGeographic = (rule, value, callback) => { |
|||
const { province, city } = value; |
|||
if (!province.key) { |
|||
callback('Please input your province!'); |
|||
} |
|||
if (!city.key) { |
|||
callback('Please input your city!'); |
|||
} |
|||
callback(); |
|||
}; |
|||
|
|||
const validatorPhone = (rule, value, callback) => { |
|||
const values = value.split('-'); |
|||
if (!values[0]) { |
|||
callback('Please input your area code!'); |
|||
} |
|||
if (!values[1]) { |
|||
callback('Please input your phone number!'); |
|||
} |
|||
callback(); |
|||
}; |
|||
|
|||
@Form.create() |
|||
export default class BaseView extends Component { |
|||
componentDidMount() { |
|||
this.setBaseInfo(); |
|||
} |
|||
setBaseInfo = () => { |
|||
const { currentUser } = this.props; |
|||
Object.keys(this.props.form.getFieldsValue()).forEach((key) => { |
|||
const obj = {}; |
|||
obj[key] = currentUser[key] || null; |
|||
this.props.form.setFieldsValue(obj); |
|||
}); |
|||
}; |
|||
getAvatarURL() { |
|||
if (this.props.currentUser.avatar) { |
|||
return this.props.currentUser.avatar; |
|||
} |
|||
const url = |
|||
'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png'; |
|||
return url; |
|||
} |
|||
render() { |
|||
const { getFieldDecorator } = this.props.form; |
|||
return ( |
|||
<div className={styles.baseView}> |
|||
<div className={styles.left}> |
|||
<Form layout="vertical" onSubmit={this.handleSubmit} hideRequiredMark> |
|||
<FormItem label="邮箱"> |
|||
{getFieldDecorator('email', { |
|||
rules: [ |
|||
{ required: true, message: 'Please input your email!' }, |
|||
], |
|||
})(<Input />)} |
|||
</FormItem> |
|||
<FormItem label="昵称"> |
|||
{getFieldDecorator('name', { |
|||
rules: [ |
|||
{ required: true, message: 'Please input your nick name!' }, |
|||
], |
|||
})(<Input />)} |
|||
</FormItem> |
|||
<FormItem label="个人简介"> |
|||
{getFieldDecorator('profile', { |
|||
rules: [ |
|||
{ required: true, message: 'Please input personal profile!' }, |
|||
], |
|||
})(<Input.TextArea rows={4} />)} |
|||
</FormItem> |
|||
<FormItem label="国家/地区"> |
|||
{getFieldDecorator('country', { |
|||
rules: [ |
|||
{ required: true, message: 'Please input your country!' }, |
|||
], |
|||
})( |
|||
<Select style={{ width: 220 }}> |
|||
<Option value="China">中国</Option> |
|||
<Option value="USA">美国</Option> |
|||
<Option value="France">法国</Option> |
|||
<Option value="Russian">俄罗斯</Option> |
|||
<Option value="UK">英国</Option> |
|||
</Select>, |
|||
)} |
|||
</FormItem> |
|||
<FormItem label="所在省市"> |
|||
{getFieldDecorator('geographic', { |
|||
rules: [ |
|||
{ |
|||
required: true, |
|||
message: 'Please input your geographic info!', |
|||
}, |
|||
{ |
|||
validator: validatorGeographic, |
|||
}, |
|||
], |
|||
})(<GeographicView />)} |
|||
</FormItem> |
|||
<FormItem label="街道地址"> |
|||
{getFieldDecorator('address', { |
|||
rules: [ |
|||
{ required: true, message: 'Please input your address!' }, |
|||
], |
|||
})(<Input />)} |
|||
</FormItem> |
|||
<FormItem label="联系电话"> |
|||
{getFieldDecorator('phone', { |
|||
rules: [ |
|||
{ required: true, message: 'Please input your phone!' }, |
|||
{ validator: validatorPhone }, |
|||
], |
|||
})(<PhoneView />)} |
|||
</FormItem> |
|||
<Button type="primary">更新信息</Button> |
|||
</Form> |
|||
</div> |
|||
<div className={styles.right}> |
|||
<AvatarView avatar={this.getAvatarURL()} /> |
|||
</div> |
|||
</div> |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
@import '~antd/lib/style/themes/default.less'; |
|||
|
|||
.baseView { |
|||
display: flex; |
|||
.left { |
|||
width: 448px; |
|||
} |
|||
.right { |
|||
flex: 1; |
|||
padding-left: 104px; |
|||
.avatar_title { |
|||
height: 22px; |
|||
width: 28px; |
|||
font-size: 14px; |
|||
color: rgba(0, 0, 0, 0.85); |
|||
line-height: 22px; |
|||
margin-bottom: 8px; |
|||
} |
|||
.avatar { |
|||
width: 144px; |
|||
height: 144px; |
|||
margin-bottom: 12px; |
|||
overflow: hidden; |
|||
img { |
|||
width: 100%; |
|||
} |
|||
} |
|||
.button_view { |
|||
width: 144px; |
|||
text-align: center; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
import React, { PureComponent } from 'react'; |
|||
import { Select, Spin } from 'antd'; |
|||
import { connect } from 'dva'; |
|||
|
|||
const { Option } = Select; |
|||
|
|||
const nullSlectItem = { |
|||
label: '', |
|||
key: '', |
|||
}; |
|||
|
|||
@connect(({ geographic }) => { |
|||
const { province, isLoading, city } = geographic; |
|||
return { |
|||
province, |
|||
city, |
|||
isLoading, |
|||
}; |
|||
}) |
|||
export default class ProvinceSelect extends PureComponent { |
|||
componentDidMount = () => { |
|||
this.props.dispatch({ |
|||
type: 'geographic/fetchProvince', |
|||
}); |
|||
}; |
|||
getProvinceOption() { |
|||
return this.getOption(this.props.province); |
|||
} |
|||
getCityOption = () => { |
|||
return this.getOption(this.props.city); |
|||
}; |
|||
getOption = (list) => { |
|||
if (!list || list.length < 1) { |
|||
return ( |
|||
<Option key={0} value={0}> |
|||
没有找到选项 |
|||
</Option> |
|||
); |
|||
} |
|||
return list.map((item) => { |
|||
return ( |
|||
<Option key={item.id} value={item.id}> |
|||
{item.name} |
|||
</Option> |
|||
); |
|||
}); |
|||
}; |
|||
selectProvinceItem = (item) => { |
|||
this.props.dispatch({ |
|||
type: 'geographic/fetchCity', |
|||
payload: item.key, |
|||
}); |
|||
this.props.onChange({ |
|||
province: item, |
|||
city: nullSlectItem, |
|||
}); |
|||
}; |
|||
selectCityItem = (item) => { |
|||
this.props.onChange({ |
|||
province: this.props.value.province, |
|||
city: item, |
|||
}); |
|||
}; |
|||
conversionObject() { |
|||
const { value } = this.props; |
|||
if (!value) { |
|||
return { |
|||
province: nullSlectItem, |
|||
city: nullSlectItem, |
|||
}; |
|||
} |
|||
const { province, city } = value; |
|||
return { |
|||
province: province || nullSlectItem, |
|||
city: city || nullSlectItem, |
|||
}; |
|||
} |
|||
render() { |
|||
const { province, city } = this.conversionObject(); |
|||
return ( |
|||
<Spin spinning={this.props.isLoading}> |
|||
<Select |
|||
value={province} |
|||
labelInValue |
|||
showSearch |
|||
onSelect={this.selectProvinceItem} |
|||
style={{ width: 220, marginRight: 8 }} |
|||
> |
|||
{this.getProvinceOption()} |
|||
</Select> |
|||
<Select |
|||
value={city} |
|||
labelInValue |
|||
showSearch |
|||
onSelect={this.selectCityItem} |
|||
style={{ width: 220 }} |
|||
> |
|||
{this.getCityOption()} |
|||
</Select> |
|||
</Spin> |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
import React, { PureComponent } from 'react'; |
|||
import { connect } from 'dva'; |
|||
import { Route, routerRedux, Switch, Redirect } from 'dva/router'; |
|||
import { Menu } from 'antd'; |
|||
import styles from './Info.less'; |
|||
import { getRoutes } from '../../utils/utils'; |
|||
|
|||
const { Item } = Menu; |
|||
|
|||
const menuMap = { |
|||
base: '基本设置', |
|||
safe: '安全设置', |
|||
account: '账号绑定', |
|||
message: '新消息通知', |
|||
}; |
|||
|
|||
@connect(({ user }) => ({ |
|||
currentUser: user.currentUser, |
|||
})) |
|||
export default class Info extends PureComponent { |
|||
constructor(props) { |
|||
super(props); |
|||
const { match, location } = props; |
|||
let key = location.pathname.replace(`${match.path}/`, ''); |
|||
key = menuMap[key] ? key : 'base'; |
|||
this.state = { |
|||
selectKey: key, |
|||
}; |
|||
} |
|||
getmenu = () => { |
|||
return Object.keys(menuMap).map(item => <Item key={item}>{menuMap[item]}</Item>); |
|||
}; |
|||
getRightTitle = () => { |
|||
return menuMap[this.state.selectKey]; |
|||
}; |
|||
selectKey = ({ key }) => { |
|||
this.props.dispatch(routerRedux.push(`/userinfo/${key}`)); |
|||
this.setState({ |
|||
selectKey: key, |
|||
}); |
|||
}; |
|||
render() { |
|||
const { match, routerData, currentUser } = this.props; |
|||
if (!currentUser.userid) { |
|||
return ''; |
|||
} |
|||
return ( |
|||
<div className={styles.main}> |
|||
<div className={styles.leftmenu}> |
|||
<Menu |
|||
mode="inline" |
|||
selectedKeys={[this.state.selectKey]} |
|||
onClick={this.selectKey} |
|||
> |
|||
{this.getmenu()} |
|||
</Menu> |
|||
</div> |
|||
<div className={styles.right}> |
|||
<div className={styles.title}>{this.getRightTitle()}</div> |
|||
<Switch> |
|||
{ |
|||
getRoutes(match.path, routerData).map(item => ( |
|||
<Route |
|||
key={item.key} |
|||
path={item.path} |
|||
render={props => <item.component {...props} currentUser={currentUser} />} |
|||
exact={item.exact} |
|||
/> |
|||
)) |
|||
} |
|||
<Redirect exact from="/userinfo" to="/userinfo/base" /> |
|||
<Redirect to="/exception/404" /> |
|||
</Switch> |
|||
</div> |
|||
</div> |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
@import '~antd/lib/style/themes/default.less'; |
|||
|
|||
.main { |
|||
width: 100%; |
|||
height: 100%; |
|||
background-color: #fff; |
|||
display: flex; |
|||
padding-top: 15px; |
|||
padding-bottom: 15px; |
|||
.leftmenu { |
|||
width: 224px; |
|||
border-right: 1px solid #e8e8e8; |
|||
:global { |
|||
.ant-menu-inline { |
|||
border: none; |
|||
} |
|||
} |
|||
} |
|||
.right { |
|||
flex: 1; |
|||
padding-left: 40px; |
|||
padding-right: 40px; |
|||
padding-top: 9px; |
|||
padding-bottom: 9px; |
|||
.title { |
|||
font-size: 20px; |
|||
color: rgba(0, 0, 0, 0.85); |
|||
line-height: 28px; |
|||
width: 100px; |
|||
height: 28px; |
|||
font-weight: bold; |
|||
margin-bottom: 24px; |
|||
} |
|||
} |
|||
} |
|||
:global { |
|||
.ant-list-item-meta { |
|||
// 账号绑定图标 |
|||
.taobao { |
|||
color: #ff4000; |
|||
display: block; |
|||
font-size: 48px; |
|||
line-height: 48px; |
|||
border-radius: 4px; |
|||
} |
|||
.dingding { |
|||
background-color: #2eabff; |
|||
color: #fff; |
|||
font-size: 32px; |
|||
line-height: 32px; |
|||
padding: 6px; |
|||
margin: 2px; |
|||
border-radius: 4px; |
|||
} |
|||
.alipay { |
|||
color: #2eabff; |
|||
font-size: 48px; |
|||
line-height: 48px; |
|||
border-radius: 4px; |
|||
} |
|||
} |
|||
|
|||
// 密码强度 |
|||
font.strong { |
|||
color: #52c41a; |
|||
} |
|||
font.medium { |
|||
color: yellow; |
|||
} |
|||
font.weak { |
|||
color: red; |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
import React, { Component, Fragment } from 'react'; |
|||
import { Switch, List } from 'antd'; |
|||
|
|||
const Action = <Switch defaultChecked />; |
|||
|
|||
export default class MessageView extends Component { |
|||
getData = () => { |
|||
return [ |
|||
{ |
|||
title: '账户密码', |
|||
description: '其他用户的消息将以站内信的形式通知', |
|||
actions: [Action], |
|||
}, |
|||
{ |
|||
title: '消息通知', |
|||
description: '已绑定手机:138****8293', |
|||
actions: [Action], |
|||
}, |
|||
{ |
|||
title: '系统消息', |
|||
description: '系统消息将以站内信的形式通知', |
|||
actions: [Action], |
|||
}, |
|||
{ |
|||
title: '待办通知', |
|||
description: '待办事项将以站内信的形式通知', |
|||
actions: [Action], |
|||
}, |
|||
]; |
|||
}; |
|||
render() { |
|||
return ( |
|||
<Fragment> |
|||
<List |
|||
itemLayout="horizontal" |
|||
dataSource={this.getData()} |
|||
renderItem={item => ( |
|||
<List.Item actions={item.actions}> |
|||
<List.Item.Meta |
|||
title={item.title} |
|||
description={item.description} |
|||
/> |
|||
</List.Item> |
|||
)} |
|||
/> |
|||
</Fragment> |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
import React, { Fragment, PureComponent } from 'react'; |
|||
import { Input } from 'antd'; |
|||
|
|||
class PhoneView extends PureComponent { |
|||
render() { |
|||
const { value, onChange } = this.props; |
|||
let values = ['', '']; |
|||
if (value) { |
|||
values = value.split('-'); |
|||
} |
|||
return ( |
|||
<Fragment> |
|||
<Input |
|||
value={values[0]} |
|||
onChange={(e) => { |
|||
onChange(`${e.target.value}-${values[1]}`); |
|||
}} |
|||
style={{ width: 128, marginRight: 8 }} |
|||
/> |
|||
<Input |
|||
onChange={(e) => { |
|||
onChange(`${values[0]}-${e.target.value}`); |
|||
}} |
|||
value={values[1]} |
|||
style={{ width: 312 }} |
|||
/> |
|||
</Fragment> |
|||
); |
|||
} |
|||
} |
|||
|
|||
export default PhoneView; |
|||
@ -0,0 +1,60 @@ |
|||
import React, { Component, Fragment } from 'react'; |
|||
import { List } from 'antd'; |
|||
|
|||
const passwordStrength = { |
|||
strong: <font className="strong">强</font>, |
|||
medium: <font className="medium">中文</font>, |
|||
weak: <font className="weak">弱</font>, |
|||
}; |
|||
|
|||
export default class SafeView extends Component { |
|||
getData = () => { |
|||
return [ |
|||
{ |
|||
title: '账户密码', |
|||
description: ( |
|||
<Fragment> 当前密码强度:{passwordStrength.strong}</Fragment> |
|||
), |
|||
actions: [<a>修改</a>], |
|||
}, |
|||
{ |
|||
title: '密保手机', |
|||
description: '已绑定手机:138****8293', |
|||
actions: [<a>修改</a>], |
|||
}, |
|||
{ |
|||
title: '密保问题', |
|||
description: '未设置密保问题,密保问题可有效保护账户安全', |
|||
actions: [<a>设置</a>], |
|||
}, |
|||
{ |
|||
title: '备用邮箱', |
|||
description: '已绑定邮箱:ant***sign.com', |
|||
actions: [<a>修改</a>], |
|||
}, |
|||
{ |
|||
title: 'MFA 设备', |
|||
description: '未绑定 MFA 设备,绑定后,可以进行二次确认', |
|||
actions: [<a>绑定</a>], |
|||
}, |
|||
]; |
|||
}; |
|||
render() { |
|||
return ( |
|||
<Fragment> |
|||
<List |
|||
itemLayout="horizontal" |
|||
dataSource={this.getData()} |
|||
renderItem={item => ( |
|||
<List.Item actions={item.actions}> |
|||
<List.Item.Meta |
|||
title={item.title} |
|||
description={item.description} |
|||
/> |
|||
</List.Item> |
|||
)} |
|||
/> |
|||
</Fragment> |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
import request from '../utils/request'; |
|||
|
|||
export async function queryProvince() { |
|||
return request('/api/geographic/province'); |
|||
} |
|||
|
|||
export async function queryCity(province) { |
|||
return request(`/api/geographic/city/${province}`); |
|||
} |
|||
Loading…
Reference in new issue