fork from bc4552c5a8
This commit is contained in:
231
client/containers/User/List.js
Normal file
231
client/containers/User/List.js
Normal file
@@ -0,0 +1,231 @@
|
||||
import React, { PureComponent as Component } from 'react';
|
||||
import { formatTime } from '../../common.js';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { setBreadcrumb } from '../../reducer/modules/user';
|
||||
//import PropTypes from 'prop-types'
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Table, Popconfirm, message, Input } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
const Search = Input.Search;
|
||||
const limit = 20;
|
||||
@connect(
|
||||
state => {
|
||||
return {
|
||||
curUserRole: state.user.role
|
||||
};
|
||||
},
|
||||
{
|
||||
setBreadcrumb
|
||||
}
|
||||
)
|
||||
class List extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
data: [],
|
||||
total: null,
|
||||
current: 1,
|
||||
backups: [],
|
||||
isSearch: false
|
||||
};
|
||||
}
|
||||
static propTypes = {
|
||||
setBreadcrumb: PropTypes.func,
|
||||
curUserRole: PropTypes.string
|
||||
};
|
||||
changePage = current => {
|
||||
this.setState(
|
||||
{
|
||||
current: current
|
||||
},
|
||||
this.getUserList
|
||||
);
|
||||
};
|
||||
|
||||
getUserList() {
|
||||
axios.get('/api/user/list?page=' + this.state.current + '&limit=' + limit).then(res => {
|
||||
let result = res.data;
|
||||
|
||||
if (result.errcode === 0) {
|
||||
let list = result.data.list;
|
||||
let total = result.data.count;
|
||||
list.map((item, index) => {
|
||||
item.key = index;
|
||||
item.up_time = formatTime(item.up_time);
|
||||
});
|
||||
this.setState({
|
||||
data: list,
|
||||
total: total,
|
||||
backups: list
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getUserList();
|
||||
}
|
||||
|
||||
confirm = uid => {
|
||||
axios
|
||||
.post('/api/user/del', {
|
||||
id: uid
|
||||
})
|
||||
.then(
|
||||
res => {
|
||||
if (res.data.errcode === 0) {
|
||||
message.success('已删除此用户');
|
||||
let userlist = this.state.data;
|
||||
userlist = userlist.filter(item => {
|
||||
return item._id != uid;
|
||||
});
|
||||
this.setState({
|
||||
data: userlist
|
||||
});
|
||||
} else {
|
||||
message.error(res.data.errmsg);
|
||||
}
|
||||
},
|
||||
err => {
|
||||
message.error(err.message);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
async UNSAFE_componentWillMount() {
|
||||
this.props.setBreadcrumb([{ name: '用户管理' }]);
|
||||
}
|
||||
|
||||
handleSearch = value => {
|
||||
let params = { q: value };
|
||||
if (params.q !== '') {
|
||||
axios.get('/api/user/search', { params }).then(data => {
|
||||
let userList = [];
|
||||
|
||||
data = data.data.data;
|
||||
if (data) {
|
||||
data.forEach(v =>
|
||||
userList.push({
|
||||
...v,
|
||||
_id: v.uid
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
data: userList,
|
||||
isSearch: true
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
data: this.state.backups,
|
||||
isSearch: false
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const role = this.props.curUserRole;
|
||||
let data = [];
|
||||
if (role === 'admin') {
|
||||
data = this.state.data;
|
||||
}
|
||||
let columns = [
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'username',
|
||||
key: 'username',
|
||||
width: 180,
|
||||
render: (username, item) => {
|
||||
return <Link to={'/user/profile/' + item._id}>{item.username}</Link>;
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Email',
|
||||
dataIndex: 'email',
|
||||
key: 'email'
|
||||
},
|
||||
{
|
||||
title: '用户角色',
|
||||
dataIndex: 'role',
|
||||
key: 'role',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '更新日期',
|
||||
dataIndex: 'up_time',
|
||||
key: 'up_time',
|
||||
width: 160
|
||||
},
|
||||
{
|
||||
title: '功能',
|
||||
key: 'action',
|
||||
width: '90px',
|
||||
render: item => {
|
||||
return (
|
||||
<span>
|
||||
{/* <span className="ant-divider" /> */}
|
||||
<Popconfirm
|
||||
title="确认删除此用户?"
|
||||
onConfirm={() => {
|
||||
this.confirm(item._id);
|
||||
}}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<a style={{ display: 'block', textAlign: 'center' }} href="#">
|
||||
删除
|
||||
</a>
|
||||
</Popconfirm>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
columns = columns.filter(item => {
|
||||
if (item.key === 'action' && role !== 'admin') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const pageConfig = {
|
||||
total: this.state.total,
|
||||
pageSize: limit,
|
||||
current: this.state.current,
|
||||
onChange: this.changePage
|
||||
};
|
||||
|
||||
const defaultPageConfig = {
|
||||
total: this.state.data.length,
|
||||
pageSize: limit,
|
||||
current: 1
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="user-table">
|
||||
<div className="user-search-wrapper">
|
||||
<h2 style={{ marginBottom: '10px' }}>用户总数:{this.state.total}位</h2>
|
||||
<Search
|
||||
onChange={e => this.handleSearch(e.target.value)}
|
||||
onSearch={this.handleSearch}
|
||||
placeholder="请输入用户名"
|
||||
/>
|
||||
</div>
|
||||
<Table
|
||||
bordered={true}
|
||||
rowKey={record => record._id}
|
||||
columns={columns}
|
||||
pagination={this.state.isSearch ? defaultPageConfig : pageConfig}
|
||||
dataSource={data}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default List;
|
||||
556
client/containers/User/Profile.js
Normal file
556
client/containers/User/Profile.js
Normal file
@@ -0,0 +1,556 @@
|
||||
import React, { PureComponent as Component } from 'react';
|
||||
import { Row, Col, Input, Button, Select, message, Upload, Tooltip } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { formatTime } from '../../common.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import { setBreadcrumb, setImageUrl } from '../../reducer/modules/user';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
const EditButton = props => {
|
||||
const { isAdmin, isOwner, onClick, name, admin } = props;
|
||||
if (isOwner) {
|
||||
// 本人
|
||||
if (admin) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
icon="edit"
|
||||
onClick={() => {
|
||||
onClick(name, true);
|
||||
}}
|
||||
>
|
||||
修改
|
||||
</Button>
|
||||
);
|
||||
} else if (isAdmin) {
|
||||
// 管理员
|
||||
return (
|
||||
<Button
|
||||
icon="edit"
|
||||
onClick={() => {
|
||||
onClick(name, true);
|
||||
}}
|
||||
>
|
||||
修改
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
EditButton.propTypes = {
|
||||
isAdmin: PropTypes.bool,
|
||||
isOwner: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
name: PropTypes.string,
|
||||
admin: PropTypes.bool
|
||||
};
|
||||
|
||||
@connect(
|
||||
state => {
|
||||
return {
|
||||
curUid: state.user.uid,
|
||||
userType: state.user.type,
|
||||
curRole: state.user.role
|
||||
};
|
||||
},
|
||||
{
|
||||
setBreadcrumb
|
||||
}
|
||||
)
|
||||
class Profile extends Component {
|
||||
static propTypes = {
|
||||
match: PropTypes.object,
|
||||
curUid: PropTypes.number,
|
||||
userType: PropTypes.string,
|
||||
setBreadcrumb: PropTypes.func,
|
||||
curRole: PropTypes.string,
|
||||
upload: PropTypes.bool
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
usernameEdit: false,
|
||||
emailEdit: false,
|
||||
secureEdit: false,
|
||||
roleEdit: false,
|
||||
userinfo: {}
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._uid = this.props.match.params.uid;
|
||||
this.handleUserinfo(this.props);
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
if (!nextProps.match.params.uid) {
|
||||
return;
|
||||
}
|
||||
if (this._uid !== nextProps.match.params.uid) {
|
||||
this.handleUserinfo(nextProps);
|
||||
}
|
||||
}
|
||||
|
||||
handleUserinfo(props) {
|
||||
const uid = props.match.params.uid;
|
||||
this.getUserInfo(uid);
|
||||
}
|
||||
|
||||
handleEdit = (key, val) => {
|
||||
var s = {};
|
||||
s[key] = val;
|
||||
this.setState(s);
|
||||
};
|
||||
|
||||
getUserInfo = id => {
|
||||
var _this = this;
|
||||
const { curUid } = this.props;
|
||||
|
||||
axios.get('/api/user/find?id=' + id).then(res => {
|
||||
_this.setState({
|
||||
userinfo: res.data.data,
|
||||
_userinfo: res.data.data
|
||||
});
|
||||
if (curUid === +id) {
|
||||
this.props.setBreadcrumb([{ name: res.data.data.username }]);
|
||||
} else {
|
||||
this.props.setBreadcrumb([{ name: '管理: ' + res.data.data.username }]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
updateUserinfo = name => {
|
||||
var state = this.state;
|
||||
let value = this.state._userinfo[name];
|
||||
let params = { uid: state.userinfo.uid };
|
||||
params[name] = value;
|
||||
|
||||
axios.post('/api/user/update', params).then(
|
||||
res => {
|
||||
let data = res.data;
|
||||
if (data.errcode === 0) {
|
||||
let userinfo = this.state.userinfo;
|
||||
userinfo[name] = value;
|
||||
this.setState({
|
||||
userinfo: userinfo
|
||||
});
|
||||
|
||||
this.handleEdit(name + 'Edit', false);
|
||||
message.success('更新用户信息成功');
|
||||
} else {
|
||||
message.error(data.errmsg);
|
||||
}
|
||||
},
|
||||
err => {
|
||||
message.error(err.message);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
changeUserinfo = e => {
|
||||
let dom = e.target;
|
||||
let name = dom.getAttribute('name');
|
||||
let value = dom.value;
|
||||
|
||||
this.setState({
|
||||
_userinfo: {
|
||||
...this.state._userinfo,
|
||||
[name]: value
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
changeRole = val => {
|
||||
let userinfo = this.state.userinfo;
|
||||
userinfo.role = val;
|
||||
this.setState({
|
||||
_userinfo: userinfo
|
||||
});
|
||||
this.updateUserinfo('role');
|
||||
};
|
||||
|
||||
updatePassword = () => {
|
||||
let old_password = document.getElementById('old_password').value;
|
||||
let password = document.getElementById('password').value;
|
||||
let verify_pass = document.getElementById('verify_pass').value;
|
||||
if (password != verify_pass) {
|
||||
return message.error('两次输入的密码不一样');
|
||||
}
|
||||
let params = {
|
||||
uid: this.state.userinfo.uid,
|
||||
password: password,
|
||||
old_password: old_password
|
||||
};
|
||||
|
||||
axios.post('/api/user/change_password', params).then(
|
||||
res => {
|
||||
let data = res.data;
|
||||
if (data.errcode === 0) {
|
||||
this.handleEdit('secureEdit', false);
|
||||
message.success('修改密码成功');
|
||||
if (this.props.curUid === this.state.userinfo.uid) {
|
||||
location.reload();
|
||||
}
|
||||
} else {
|
||||
message.error(data.errmsg);
|
||||
}
|
||||
},
|
||||
err => {
|
||||
message.error(err.message);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
let ButtonGroup = Button.Group;
|
||||
let userNameEditHtml, emailEditHtml, secureEditHtml, roleEditHtml;
|
||||
const Option = Select.Option;
|
||||
let userinfo = this.state.userinfo;
|
||||
let _userinfo = this.state._userinfo;
|
||||
let roles = { admin: '管理员', member: '会员' };
|
||||
let userType = '';
|
||||
if (this.props.userType === 'third') {
|
||||
userType = false;
|
||||
} else if (this.props.userType === 'site') {
|
||||
userType = true;
|
||||
} else {
|
||||
userType = false;
|
||||
}
|
||||
|
||||
// 用户名信息修改
|
||||
if (this.state.usernameEdit === false) {
|
||||
userNameEditHtml = (
|
||||
<div>
|
||||
<span className="text">{userinfo.username}</span>
|
||||
{/*<span className="text-button" onClick={() => { this.handleEdit('usernameEdit', true) }}><Icon type="edit" />修改</span>*/}
|
||||
{/* {btn} */}
|
||||
{/* 站点登陆才能编辑 */}
|
||||
{userType && (
|
||||
<EditButton
|
||||
userType={userType}
|
||||
isOwner={userinfo.uid === this.props.curUid}
|
||||
isAdmin={this.props.curRole === 'admin'}
|
||||
onClick={this.handleEdit}
|
||||
name="usernameEdit"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
userNameEditHtml = (
|
||||
<div>
|
||||
<Input
|
||||
value={_userinfo.username}
|
||||
name="username"
|
||||
onChange={this.changeUserinfo}
|
||||
placeholder="用户名"
|
||||
/>
|
||||
<ButtonGroup className="edit-buttons">
|
||||
<Button
|
||||
className="edit-button"
|
||||
onClick={() => {
|
||||
this.handleEdit('usernameEdit', false);
|
||||
}}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
className="edit-button"
|
||||
onClick={() => {
|
||||
this.updateUserinfo('username');
|
||||
}}
|
||||
type="primary"
|
||||
>
|
||||
确定
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
// 邮箱信息修改
|
||||
if (this.state.emailEdit === false) {
|
||||
emailEditHtml = (
|
||||
<div>
|
||||
<span className="text">{userinfo.email}</span>
|
||||
{/*<span className="text-button" onClick={() => { this.handleEdit('emailEdit', true) }} ><Icon type="edit" />修改</span>*/}
|
||||
{/* {btn} */}
|
||||
{/* 站点登陆才能编辑 */}
|
||||
{userType && (
|
||||
<EditButton
|
||||
admin={userinfo.role === 'admin'}
|
||||
isOwner={userinfo.uid === this.props.curUid}
|
||||
isAdmin={this.props.curRole === 'admin'}
|
||||
onClick={this.handleEdit}
|
||||
name="emailEdit"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
emailEditHtml = (
|
||||
<div>
|
||||
<Input
|
||||
placeholder="Email"
|
||||
value={_userinfo.email}
|
||||
name="email"
|
||||
onChange={this.changeUserinfo}
|
||||
/>
|
||||
<ButtonGroup className="edit-buttons">
|
||||
<Button
|
||||
className="edit-button"
|
||||
onClick={() => {
|
||||
this.handleEdit('emailEdit', false);
|
||||
}}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
className="edit-button"
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
this.updateUserinfo('email');
|
||||
}}
|
||||
>
|
||||
确定
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.roleEdit === false) {
|
||||
roleEditHtml = (
|
||||
<div>
|
||||
<span className="text">{roles[userinfo.role]}</span>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
roleEditHtml = (
|
||||
<Select defaultValue={_userinfo.role} onChange={this.changeRole} style={{ width: 150 }}>
|
||||
<Option value="admin">管理员</Option>
|
||||
<Option value="member">会员</Option>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.secureEdit === false) {
|
||||
let btn = '';
|
||||
if (userType) {
|
||||
btn = (
|
||||
<Button
|
||||
icon="edit"
|
||||
onClick={() => {
|
||||
this.handleEdit('secureEdit', true);
|
||||
}}
|
||||
>
|
||||
修改
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
secureEditHtml = btn;
|
||||
} else {
|
||||
secureEditHtml = (
|
||||
<div>
|
||||
<Input
|
||||
style={{
|
||||
display: this.props.curRole === 'admin' && userinfo.role != 'admin' ? 'none' : ''
|
||||
}}
|
||||
placeholder="旧的密码"
|
||||
type="password"
|
||||
name="old_password"
|
||||
id="old_password"
|
||||
/>
|
||||
<Input placeholder="新的密码" type="password" name="password" id="password" />
|
||||
<Input placeholder="确认密码" type="password" name="verify_pass" id="verify_pass" />
|
||||
<ButtonGroup className="edit-buttons">
|
||||
<Button
|
||||
className="edit-button"
|
||||
onClick={() => {
|
||||
this.handleEdit('secureEdit', false);
|
||||
}}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button className="edit-button" onClick={this.updatePassword} type="primary">
|
||||
确定
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="user-profile">
|
||||
<div className="user-item-body">
|
||||
{userinfo.uid === this.props.curUid ? (
|
||||
<h3>个人设置</h3>
|
||||
) : (
|
||||
<h3>{userinfo.username} 资料设置</h3>
|
||||
)}
|
||||
|
||||
<Row className="avatarCon" type="flex" justify="start">
|
||||
<Col span={24}>
|
||||
{userinfo.uid === this.props.curUid ? (
|
||||
<AvatarUpload uid={userinfo.uid}>点击上传头像</AvatarUpload>
|
||||
) : (
|
||||
<div className="avatarImg">
|
||||
<img src={`/api/user/avatar?uid=${userinfo.uid}`} />
|
||||
</div>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="user-item" type="flex" justify="start">
|
||||
<div className="maoboli" />
|
||||
<Col span={4}>用户id</Col>
|
||||
<Col span={12}>{userinfo.uid}</Col>
|
||||
</Row>
|
||||
<Row className="user-item" type="flex" justify="start">
|
||||
<div className="maoboli" />
|
||||
<Col span={4}>用户名</Col>
|
||||
<Col span={12}>{userNameEditHtml}</Col>
|
||||
</Row>
|
||||
<Row className="user-item" type="flex" justify="start">
|
||||
<div className="maoboli" />
|
||||
<Col span={4}>Email</Col>
|
||||
<Col span={12}>{emailEditHtml}</Col>
|
||||
</Row>
|
||||
<Row
|
||||
className="user-item"
|
||||
style={{ display: this.props.curRole === 'admin' ? '' : 'none' }}
|
||||
type="flex"
|
||||
justify="start"
|
||||
>
|
||||
<div className="maoboli" />
|
||||
<Col span={4}>角色</Col>
|
||||
<Col span={12}>{roleEditHtml}</Col>
|
||||
</Row>
|
||||
<Row
|
||||
className="user-item"
|
||||
style={{ display: this.props.curRole === 'admin' ? '' : 'none' }}
|
||||
type="flex"
|
||||
justify="start"
|
||||
>
|
||||
<div className="maoboli" />
|
||||
<Col span={4}>登陆方式</Col>
|
||||
<Col span={12}>{userinfo.type === 'site' ? '站点登陆' : '第三方登陆'}</Col>
|
||||
</Row>
|
||||
<Row className="user-item" type="flex" justify="start">
|
||||
<div className="maoboli" />
|
||||
<Col span={4}>创建账号时间</Col>
|
||||
<Col span={12}>{formatTime(userinfo.add_time)}</Col>
|
||||
</Row>
|
||||
<Row className="user-item" type="flex" justify="start">
|
||||
<div className="maoboli" />
|
||||
<Col span={4}>更新账号时间</Col>
|
||||
<Col span={12}>{formatTime(userinfo.up_time)}</Col>
|
||||
</Row>
|
||||
|
||||
{userType ? (
|
||||
<Row className="user-item" type="flex" justify="start">
|
||||
<div className="maoboli" />
|
||||
<Col span={4}>密码</Col>
|
||||
<Col span={12}>{secureEditHtml}</Col>
|
||||
</Row>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@connect(
|
||||
state => {
|
||||
return {
|
||||
url: state.user.imageUrl
|
||||
};
|
||||
},
|
||||
{
|
||||
setImageUrl
|
||||
}
|
||||
)
|
||||
class AvatarUpload extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
static propTypes = {
|
||||
uid: PropTypes.number,
|
||||
setImageUrl: PropTypes.func,
|
||||
url: PropTypes.any
|
||||
};
|
||||
uploadAvatar(basecode) {
|
||||
axios
|
||||
.post('/api/user/upload_avatar', { basecode: basecode })
|
||||
.then(() => {
|
||||
// this.setState({ imageUrl: basecode });
|
||||
this.props.setImageUrl(basecode);
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
handleChange(info) {
|
||||
if (info.file.status === 'done') {
|
||||
// Get this url from response in real world.
|
||||
getBase64(info.file.originFileObj, basecode => {
|
||||
this.uploadAvatar(basecode);
|
||||
});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const { url } = this.props;
|
||||
let imageUrl = url ? url : `/api/user/avatar?uid=${this.props.uid}`;
|
||||
// let imageUrl = this.state.imageUrl ? this.state.imageUrl : `/api/user/avatar?uid=${this.props.uid}`;
|
||||
// console.log(this.props.uid);
|
||||
return (
|
||||
<div className="avatar-box">
|
||||
<Tooltip
|
||||
placement="right"
|
||||
title={<div>点击头像更换 (只支持jpg、png格式且大小不超过200kb的图片)</div>}
|
||||
>
|
||||
<div>
|
||||
<Upload
|
||||
className="avatar-uploader"
|
||||
name="basecode"
|
||||
showUploadList={false}
|
||||
action="/api/user/upload_avatar"
|
||||
beforeUpload={beforeUpload}
|
||||
onChange={this.handleChange.bind(this)}
|
||||
>
|
||||
{/*<Avatar size="large" src={imageUrl} />*/}
|
||||
<div style={{ width: 100, height: 100 }}>
|
||||
<img className="avatar" src={imageUrl} />
|
||||
</div>
|
||||
</Upload>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<span className="avatarChange" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function beforeUpload(file) {
|
||||
const isJPG = file.type === 'image/jpeg';
|
||||
const isPNG = file.type === 'image/png';
|
||||
if (!isJPG && !isPNG) {
|
||||
message.error('图片的格式只能为 jpg、png!');
|
||||
}
|
||||
const isLt2M = file.size / 1024 / 1024 < 0.2;
|
||||
if (!isLt2M) {
|
||||
message.error('图片必须小于 200kb!');
|
||||
}
|
||||
|
||||
return (isPNG || isJPG) && isLt2M;
|
||||
}
|
||||
|
||||
function getBase64(img, callback) {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener('load', () => callback(reader.result));
|
||||
reader.readAsDataURL(img);
|
||||
}
|
||||
|
||||
export default Profile;
|
||||
45
client/containers/User/User.js
Normal file
45
client/containers/User/User.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import './index.scss';
|
||||
import React, { PureComponent as Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Route } from 'react-router-dom';
|
||||
import List from './List.js';
|
||||
import PropTypes from 'prop-types';
|
||||
import Profile from './Profile.js';
|
||||
import { Row } from 'antd';
|
||||
@connect(
|
||||
state => {
|
||||
return {
|
||||
curUid: state.user.uid,
|
||||
userType: state.user.type,
|
||||
role: state.user.role
|
||||
};
|
||||
},
|
||||
{}
|
||||
)
|
||||
class User extends Component {
|
||||
static propTypes = {
|
||||
match: PropTypes.object,
|
||||
curUid: PropTypes.number,
|
||||
userType: PropTypes.string,
|
||||
role: PropTypes.string
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div className="g-doc">
|
||||
<Row className="user-box">
|
||||
<Route path={this.props.match.path + '/list'} component={List} />
|
||||
<Route path={this.props.match.path + '/profile/:uid'} component={Profile} />
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default User;
|
||||
241
client/containers/User/index.scss
Normal file
241
client/containers/User/index.scss
Normal file
@@ -0,0 +1,241 @@
|
||||
@import '../../styles/mixin.scss';
|
||||
|
||||
.g-doc {
|
||||
margin: 0 auto 0.24rem;
|
||||
}
|
||||
|
||||
/* .user-box.css */
|
||||
.user-box {
|
||||
@include row-width-limit;
|
||||
padding: 0;
|
||||
display: -webkit-box;
|
||||
-webkit-box-flex: 1;
|
||||
margin: 0 auto;
|
||||
margin-top: 24px;
|
||||
.user-list {
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2);
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
min-height: 5rem;
|
||||
.search {
|
||||
padding: 5px;
|
||||
background-color: #eee;
|
||||
}
|
||||
ul {
|
||||
border: none;
|
||||
}
|
||||
.ant-menu-item {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
.user-name {
|
||||
padding: 0.24rem;
|
||||
background-color: #34495e;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
span {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.router-content {
|
||||
min-height: calc(100% - 2.47rem);
|
||||
}
|
||||
.user-table {
|
||||
-webkit-box-flex: 1;
|
||||
padding: 24px;
|
||||
background: #fff;
|
||||
min-height: 5rem;
|
||||
.ant-table-wrapper table {
|
||||
// font-size: .14rem;
|
||||
|
||||
a {
|
||||
// font-size: 13px;
|
||||
}
|
||||
}
|
||||
.user-search-wrapper {
|
||||
position: relative;
|
||||
.ant-input-search {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 250px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-profile {
|
||||
-webkit-box-flex: 1;
|
||||
background: #fff;
|
||||
min-height: 5rem;
|
||||
position: relative;
|
||||
|
||||
.edit-buttons {
|
||||
.edit-button {
|
||||
text-align: center;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.bacToPer {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 8px;
|
||||
z-index: 3;
|
||||
}
|
||||
.user-item-body {
|
||||
width: 95%;
|
||||
margin: 0px auto;
|
||||
padding: 32px;
|
||||
position: relative;
|
||||
// box-shadow: 0 4px 6px rgba(50, 50, 93, 0.3), 0 1px 3px rgba(0, 0, 0, 0.01);
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.user-item-mask-top {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: black;
|
||||
z-index: 2;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.user-item-mask {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
.user-item {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
padding: 8px 0px;
|
||||
|
||||
.maoboli {
|
||||
// background-color: rgba(225,225,225,0.16);
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.user-item {
|
||||
min-height: 35px;
|
||||
line-height: 35px;
|
||||
margin: 5px;
|
||||
margin-left: 0px;
|
||||
font-size: 14px;
|
||||
#old_password,
|
||||
#password,
|
||||
#verify_pass {
|
||||
margin-top: 20px;
|
||||
}
|
||||
#old_password {
|
||||
margin-top: 0px;
|
||||
}
|
||||
.ant-col-12 {
|
||||
.ant-input {
|
||||
width: 60%;
|
||||
margin-right: 16px;
|
||||
}
|
||||
}
|
||||
.ant-col-4 {
|
||||
color: rgba(71, 86, 99, 0.8);
|
||||
font-weight: 500;
|
||||
padding: 0px 16px;
|
||||
text-indent: 0.7em;
|
||||
margin-right: 30px;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
width: 130px;
|
||||
}
|
||||
.text {
|
||||
padding-right: 16px;
|
||||
}
|
||||
.text-button {
|
||||
color: #657289;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-nav {
|
||||
border-bottom-left-radius: 0.04rem;
|
||||
border-bottom-right-radius: 0.04rem;
|
||||
}
|
||||
.avatar-uploader {
|
||||
border: none;
|
||||
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.31), 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.avatar-uploader,
|
||||
.avatar-uploader-trigger {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
overflow: hidden;
|
||||
border-radius: 50px;
|
||||
}
|
||||
.avatar {
|
||||
width: 100px;
|
||||
min-height: 100px;
|
||||
}
|
||||
.avatarImg {
|
||||
width: 100px;
|
||||
border-radius: 50px;
|
||||
overflow: hidden;
|
||||
height: 100px;
|
||||
background-color: white;
|
||||
box-shadow: 0 4px 6px rgba(50, 50, 93, 0.31), 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
margin-left: 60px;
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.avatar-uploader {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
height: 100px;
|
||||
}
|
||||
.ant-upload-list {
|
||||
display: none;
|
||||
}
|
||||
.avatar-box {
|
||||
width: 100px;
|
||||
position: relative;
|
||||
margin-left: 60px;
|
||||
}
|
||||
.avatarCon {
|
||||
// background: gray;
|
||||
padding: 16px 0px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.avatar-uploader-trigger {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
font-size: 28px;
|
||||
color: #999;
|
||||
}
|
||||
.avatarChange {
|
||||
display: block;
|
||||
width: 300px;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
margin-left: -100px;
|
||||
color: #ececec;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user