next-terminal/web/src/components/user/User.js
2021-04-08 19:34:42 +08:00

684 lines
25 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, {Component} from 'react';
import {
Badge,
Button,
Col,
Divider,
Dropdown,
Form,
Input,
Layout,
Menu,
Modal,
Row,
Space,
Table,
Tag,
Tooltip,
Typography,
} from "antd";
import qs from "qs";
import UserModal from "./UserModal";
import request from "../../common/request";
import {message} from "antd/es";
import {
DeleteOutlined,
DownOutlined,
ExclamationCircleOutlined,
InsuranceOutlined,
LockOutlined,
PlusOutlined,
SyncOutlined,
UndoOutlined
} from '@ant-design/icons';
import UserShareAsset from "./UserShareAsset";
import {hasPermission} from "../../service/permission";
import dayjs from "dayjs";
const confirm = Modal.confirm;
const {Search} = Input;
const {Title, Text} = Typography;
const {Content} = Layout;
class User extends Component {
inputRefOfNickname = React.createRef();
inputRefOfUsername = React.createRef();
inputRefOfMail = React.createRef();
changePasswordFormRef = React.createRef()
state = {
items: [],
total: 0,
queryParams: {
pageIndex: 1,
pageSize: 10
},
loading: false,
modalVisible: false,
modalTitle: '',
modalConfirmLoading: false,
model: null,
selectedRowKeys: [],
delBtnLoading: false,
assetVisible: false,
changePasswordVisible: false,
changePasswordConfirmLoading: false,
selectedRow: {}
};
componentDidMount() {
this.loadTableData();
}
async delete(id) {
let result = await request.delete('/users/' + id);
if (result.code === 1) {
message.success('操作成功', 3);
await this.loadTableData(this.state.queryParams);
} else {
message.error('删除失败 :( ' + result.message, 10);
}
}
async loadTableData(queryParams) {
this.setState({
loading: true
});
queryParams = queryParams || this.state.queryParams;
let paramsStr = qs.stringify(queryParams);
let data = {
items: [],
total: 0
};
try {
let result = await request.get('/users/paging?' + paramsStr);
if (result.code === 1) {
data = result.data;
} else {
message.error(result.message, 10);
}
} catch (e) {
} finally {
const items = data.items.map(item => {
return {'key': item['id'], ...item}
})
this.setState({
items: items,
total: data.total,
queryParams: queryParams,
loading: false
});
}
}
handleChangPage = (pageIndex, pageSize) => {
let queryParams = this.state.queryParams;
queryParams.pageIndex = pageIndex;
queryParams.pageSize = pageSize;
this.setState({
queryParams: queryParams
});
this.loadTableData(queryParams).then(r => {
})
};
showDeleteConfirm(id, content) {
let self = this;
confirm({
title: '您确定要删除此用户吗?',
content: content,
okText: '确定',
okType: 'danger',
cancelText: '取消',
onOk() {
self.delete(id);
}
});
};
showModal(title, user = {}) {
this.setState({
model: user,
modalVisible: true,
modalTitle: title
});
};
handleCancelModal = e => {
this.setState({
modalVisible: false,
modalTitle: ''
});
};
handleOk = async (formData) => {
// 弹窗 form 传来的数据
this.setState({
modalConfirmLoading: true
});
if (formData.id) {
// 向后台提交数据
const result = await request.put('/users/' + formData.id, formData);
if (result.code === 1) {
message.success('操作成功', 3);
this.setState({
modalVisible: false
});
await this.loadTableData(this.state.queryParams);
} else {
message.error('操作失败 :( ' + result.message, 10);
}
} else {
// 向后台提交数据
const result = await request.post('/users', formData);
if (result.code === 1) {
message.success('操作成功', 3);
this.setState({
modalVisible: false
});
await this.loadTableData(this.state.queryParams);
} else {
message.error('操作失败 :( ' + result.message, 10);
}
}
this.setState({
modalConfirmLoading: false
});
};
handleSearchByUsername = username => {
let query = {
...this.state.queryParams,
'pageIndex': 1,
'pageSize': this.state.queryParams.pageSize,
'username': username,
}
this.loadTableData(query);
};
handleSearchByNickname = nickname => {
let query = {
...this.state.queryParams,
'pageIndex': 1,
'pageSize': this.state.queryParams.pageSize,
'nickname': nickname,
}
this.loadTableData(query);
};
handleSearchByMail = mail => {
let query = {
...this.state.queryParams,
'pageIndex': 1,
'pageSize': this.state.queryParams.pageSize,
'mail': mail,
}
this.loadTableData(query);
};
batchDelete = async () => {
this.setState({
delBtnLoading: true
})
try {
let result = await request.delete('/users/' + this.state.selectedRowKeys.join(','));
if (result['code'] === 1) {
message.success('操作成功', 3);
this.setState({
selectedRowKeys: []
})
await this.loadTableData(this.state.queryParams);
} else {
message.error(result['message'], 10);
}
} finally {
this.setState({
delBtnLoading: false
})
}
}
handleAssetCancel = () => {
this.loadTableData()
this.setState({
assetVisible: false
})
}
handleChangePassword = async (values) => {
this.setState({
changePasswordConfirmLoading: true
})
let result = await request.post(`/users/${this.state.selectedRow['id']}/change-password?password=${values['password']}`);
if (result['code'] === 1) {
message.success('操作成功', 3);
} else {
message.error(result['message'], 10);
}
this.setState({
changePasswordConfirmLoading: false,
changePasswordVisible: false
})
}
handleTableChange = (pagination, filters, sorter) => {
let query = {
...this.state.queryParams,
'order': sorter.order,
'field': sorter.field
}
this.loadTableData(query);
}
render() {
const columns = [{
title: '序号',
dataIndex: 'id',
key: 'id',
render: (id, record, index) => {
return index + 1;
}
}, {
title: '登录账号',
dataIndex: 'username',
key: 'username',
sorter: true,
}, {
title: '用户昵称',
dataIndex: 'nickname',
key: 'nickname',
sorter: true,
}, {
title: '用户类型',
dataIndex: 'type',
key: 'type',
render: (text, record) => {
if (text === 'user') {
return (
<Tag>普通用户</Tag>
);
} else if (text === 'admin') {
return (
<Tag color="blue">管理用户</Tag>
);
} else {
return text;
}
}
}, {
title: '邮箱',
dataIndex: 'mail',
key: 'mail',
}, {
title: '二次认证',
dataIndex: 'totpSecret',
key: 'totpSecret',
render: (text, record) => {
if (text === '1') {
return <Tag icon={<InsuranceOutlined/>} color="success">开启</Tag>;
} else {
return <Tag icon={<ExclamationCircleOutlined/>} color="warning">关闭</Tag>;
}
}
}, {
title: '在线状态',
dataIndex: 'online',
key: 'online',
render: text => {
if (text) {
return (<Badge status="success" text="在线"/>);
} else {
return (<Badge status="default" text="离线"/>);
}
}
}, {
title: '授权资产',
dataIndex: 'sharerAssetCount',
key: 'sharerAssetCount',
render: (text, record, index) => {
return <Button type='link' onClick={async () => {
this.setState({
assetVisible: true,
sharer: record['id']
})
}}>{text}</Button>
}
}, {
title: '创建日期',
dataIndex: 'created',
key: 'created',
render: (text, record) => {
return (
<Tooltip title={text}>
{dayjs(text).fromNow()}
</Tooltip>
)
},
sorter: true,
},
{
title: '操作',
key: 'action',
render: (text, record) => {
const menu = (
<Menu>
<Menu.Item key="1">
<Button type="text" size='small'
onClick={() => {
this.setState({
changePasswordVisible: true,
selectedRow: record
})
}}>修改密码</Button>
</Menu.Item>
<Menu.Item key="2">
<Button type="text" size='small'
onClick={() => {
confirm({
title: '您确定要重置此用户的双因素认证吗?',
content: record['name'],
okText: '确定',
cancelText: '取消',
onOk: async () => {
let result = await request.post(`/users/${record['id']}/reset-totp`);
if (result['code'] === 1) {
message.success('操作成功', 3);
this.loadTableData();
} else {
message.error(result['message'], 10);
}
}
});
}}>重置双因素认证</Button>
</Menu.Item>
<Menu.Divider/>
<Menu.Item key="5">
<Button type="text" size='small' danger
disabled={!hasPermission(record['owner'])}
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
</Menu.Item>
</Menu>
);
return (
<div>
<Button type="link" size='small'
onClick={async () => {
let result = await request.get(`/users/${record['id']}`);
if (result['code'] !== 1) {
message.error(result['message']);
return;
}
this.showModal('更新用户', result['data']);
}}>编辑</Button>
<Dropdown overlay={menu}>
<Button type="link" size='small'>
更多 <DownOutlined/>
</Button>
</Dropdown>
</div>
)
},
}
];
const selectedRowKeys = this.state.selectedRowKeys;
const rowSelection = {
selectedRowKeys: this.state.selectedRowKeys,
onChange: (selectedRowKeys, selectedRows) => {
this.setState({selectedRowKeys});
},
};
const hasSelected = selectedRowKeys.length > 0;
return (
<>
<Content className="site-layout-background page-content">
<div style={{marginBottom: 20}}>
<Row justify="space-around" align="middle" gutter={24}>
<Col span={8} key={1}>
<Title level={3}>用户列表</Title>
</Col>
<Col span={16} key={2} style={{textAlign: 'right'}}>
<Space>
<Search
ref={this.inputRefOfNickname}
placeholder="用户昵称"
allowClear
onSearch={this.handleSearchByNickname}
/>
<Search
ref={this.inputRefOfUsername}
placeholder="登录账号"
allowClear
onSearch={this.handleSearchByUsername}
/>
<Search
ref={this.inputRefOfMail}
placeholder="邮箱"
allowClear
onSearch={this.handleSearchByMail}
/>
<Tooltip title='重置查询'>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfUsername.current.setValue('');
this.inputRefOfNickname.current.setValue('');
this.inputRefOfMail.current.setValue('');
this.loadTableData({pageIndex: 1, pageSize: 10})
}}>
</Button>
</Tooltip>
<Divider type="vertical"/>
<Tooltip title="新增">
<Button type="dashed" icon={<PlusOutlined/>}
onClick={() => this.showModal('新增用户', {})}>
</Button>
</Tooltip>
<Tooltip title="刷新列表">
<Button icon={<SyncOutlined/>} onClick={() => {
this.loadTableData(this.state.queryParams)
}}>
</Button>
</Tooltip>
{/*<Tooltip title="批量启用">*/}
{/* <Button type="dashed" danger disabled={!hasSelected}*/}
{/* icon={<IssuesCloseOutlined/>}*/}
{/* loading={this.state.delBtnLoading}*/}
{/* onClick={() => {*/}
{/* constant content = <div>*/}
{/* 您确定要启用选中的<Text style={{color: '#1890FF'}}*/}
{/* strong>{this.state.selectedRowKeys.length}</Text>条记录吗?*/}
{/* </div>;*/}
{/* confirm({*/}
{/* icon: <ExclamationCircleOutlined/>,*/}
{/* content: content,*/}
{/* onOk: () => {*/}
{/* },*/}
{/* onCancel() {*/}
{/* },*/}
{/* });*/}
{/* }}>*/}
{/* </Button>*/}
{/*</Tooltip>*/}
{/*<Tooltip title="批量禁用">*/}
{/* <Button type="default" danger disabled={!hasSelected} icon={<StopOutlined/>}*/}
{/* loading={this.state.delBtnLoading}*/}
{/* onClick={() => {*/}
{/* constant content = <div>*/}
{/* 您确定要禁用选中的<Text style={{color: '#1890FF'}}*/}
{/* strong>{this.state.selectedRowKeys.length}</Text>条记录吗?*/}
{/* </div>;*/}
{/* confirm({*/}
{/* icon: <ExclamationCircleOutlined/>,*/}
{/* content: content,*/}
{/* onOk: () => {*/}
{/* },*/}
{/* onCancel() {*/}
{/* },*/}
{/* });*/}
{/* }}>*/}
{/* </Button>*/}
{/*</Tooltip>*/}
<Tooltip title="批量删除">
<Button type="primary" danger disabled={!hasSelected} icon={<DeleteOutlined/>}
loading={this.state.delBtnLoading}
onClick={() => {
const content = <div>
您确定要删除选中的<Text style={{color: '#1890FF'}}
strong>{this.state.selectedRowKeys.length}</Text>
</div>;
confirm({
icon: <ExclamationCircleOutlined/>,
content: content,
onOk: () => {
this.batchDelete()
},
onCancel() {
},
});
}}>
</Button>
</Tooltip>
</Space>
</Col>
</Row>
</div>
<Table rowSelection={rowSelection}
dataSource={this.state.items}
columns={columns}
position={'both'}
pagination={{
showSizeChanger: true,
current: this.state.queryParams.pageIndex,
pageSize: this.state.queryParams.pageSize,
onChange: this.handleChangPage,
onShowSizeChange: this.handleChangPage,
total: this.state.total,
showTotal: total => `总计 ${total}`
}}
loading={this.state.loading}
onChange={this.handleTableChange}
/>
{/* 为了屏蔽ant modal 关闭后数据仍然遗留的问题*/}
{
this.state.modalVisible ?
<UserModal
visible={this.state.modalVisible}
title={this.state.modalTitle}
handleOk={this.handleOk}
handleCancel={this.handleCancelModal}
confirmLoading={this.state.modalConfirmLoading}
model={this.state.model}
>
</UserModal> : undefined
}
<Modal
width={window.innerWidth * 0.8}
title='已授权资产'
visible={this.state.assetVisible}
maskClosable={false}
destroyOnClose={true}
onOk={() => {
}}
onCancel={this.handleAssetCancel}
okText='确定'
cancelText='取消'
footer={null}
>
<UserShareAsset
sharer={this.state.sharer}
/>
</Modal>
{
this.state.changePasswordVisible ?
<Modal title="修改密码" visible={this.state.changePasswordVisible}
confirmLoading={this.state.changePasswordConfirmLoading}
maskClosable={false}
onOk={() => {
this.changePasswordFormRef.current
.validateFields()
.then(values => {
this.changePasswordFormRef.current.resetFields();
this.handleChangePassword(values);
})
.catch(info => {
});
}}
onCancel={() => {
this.setState({
changePasswordVisible: false
})
}}>
<Form ref={this.changePasswordFormRef}>
<Form.Item name='password' rules={[{required: true, message: '请输入新密码'}]}>
<Input prefix={<LockOutlined/>} placeholder="请输入新密码"/>
</Form.Item>
</Form>
</Modal> : undefined
}
</Content>
</>
);
}
}
export default User;