- 修复「修改接入网关失败」的问题
- 完成「[功能请求]审计的历史会话建议添加“已阅”的功能」close #194 - 增加一键删除登录日志和历史会话的功能
This commit is contained in:
@ -141,15 +141,27 @@ class AccessGateway extends Component {
|
||||
await this.showModal('更新接入网关', result.data);
|
||||
}
|
||||
|
||||
async reconnect(id) {
|
||||
message.info({content: '正在重连中...', key: id, duration: 5});
|
||||
let result = await request.post(`/access-gateways/${id}/reconnect`);
|
||||
if (result.code !== 1) {
|
||||
message.error({content: result.message, key: id, duration: 10});
|
||||
return;
|
||||
async reconnect(id, index) {
|
||||
let items = this.state.items;
|
||||
try {
|
||||
items[index]['reconnectLoading'] = true;
|
||||
this.setState({
|
||||
items: items
|
||||
});
|
||||
message.info({content: '正在重连中...', key: id, duration: 5});
|
||||
let result = await request.post(`/access-gateways/${id}/reconnect`);
|
||||
if (result.code !== 1) {
|
||||
message.error({content: result.message, key: id, duration: 10});
|
||||
return;
|
||||
}
|
||||
message.success({content: '重连完成。', key: id, duration: 3});
|
||||
this.loadTableData(this.state.queryParams);
|
||||
} finally {
|
||||
items[index]['reconnectLoading'] = false;
|
||||
this.setState({
|
||||
items: items
|
||||
});
|
||||
}
|
||||
message.success({content: '重连完成。', key: id, duration: 3});
|
||||
this.loadTableData(this.state.queryParams);
|
||||
}
|
||||
|
||||
showModal(title, obj) {
|
||||
@ -173,37 +185,39 @@ class AccessGateway extends Component {
|
||||
modalConfirmLoading: true
|
||||
});
|
||||
|
||||
if (formData.id) {
|
||||
// 向后台提交数据
|
||||
const result = await request.put('/access-gateways/' + formData.id, formData);
|
||||
if (result.code === 1) {
|
||||
message.success('更新成功');
|
||||
try {
|
||||
if (formData.id) {
|
||||
// 向后台提交数据
|
||||
const result = await request.put('/access-gateways/' + formData.id, formData);
|
||||
if (result.code === 1) {
|
||||
message.success('更新成功');
|
||||
|
||||
this.setState({
|
||||
modalVisible: false
|
||||
});
|
||||
this.loadTableData(this.state.queryParams);
|
||||
this.setState({
|
||||
modalVisible: false
|
||||
});
|
||||
this.loadTableData(this.state.queryParams);
|
||||
} else {
|
||||
message.error('更新失败 :( ' + result.message, 10);
|
||||
}
|
||||
} else {
|
||||
message.error('更新失败 :( ' + result.message, 10);
|
||||
}
|
||||
} else {
|
||||
// 向后台提交数据
|
||||
const result = await request.post('/access-gateways', formData);
|
||||
if (result.code === 1) {
|
||||
message.success('新增成功');
|
||||
// 向后台提交数据
|
||||
const result = await request.post('/access-gateways', formData);
|
||||
if (result.code === 1) {
|
||||
message.success('新增成功');
|
||||
|
||||
this.setState({
|
||||
modalVisible: false
|
||||
});
|
||||
this.loadTableData(this.state.queryParams);
|
||||
} else {
|
||||
message.error('新增失败 :( ' + result.message, 10);
|
||||
this.setState({
|
||||
modalVisible: false
|
||||
});
|
||||
this.loadTableData(this.state.queryParams);
|
||||
} else {
|
||||
message.error('新增失败 :( ' + result.message, 10);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.setState({
|
||||
modalConfirmLoading: false
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
modalConfirmLoading: false
|
||||
});
|
||||
};
|
||||
|
||||
batchDelete = async () => {
|
||||
@ -340,11 +354,11 @@ class AccessGateway extends Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button type="link" size='small' loading={this.state.items[index]['execLoading']}
|
||||
<Button type="link" size='small'
|
||||
onClick={() => this.update(record['id'])}>编辑</Button>
|
||||
|
||||
<Button type="link" size='small' loading={this.state.items[index]['execLoading']}
|
||||
onClick={() => this.reconnect(record['id'])}>重连</Button>
|
||||
<Button type="link" size='small' loading={this.state.items[index]['reconnectLoading']}
|
||||
onClick={() => this.reconnect(record['id'], index)}>重连</Button>
|
||||
|
||||
<Button type="text" size='small' danger
|
||||
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
|
||||
|
@ -131,7 +131,7 @@ class Dashboard extends Component {
|
||||
<div style={{margin: 16, marginBottom: 0}}>
|
||||
<Row gutter={16}>
|
||||
<Col span={6}>
|
||||
<Card bordered={true} hoverable>
|
||||
<Card bordered={true} hoverable={true}>
|
||||
<Link to={'/user'}>
|
||||
<Statistic title="在线用户" value={this.state.counter['user']}
|
||||
prefix={<UserOutlined/>}/>
|
||||
@ -139,7 +139,7 @@ class Dashboard extends Component {
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card bordered={true} hoverable>
|
||||
<Card bordered={true} hoverable={true}>
|
||||
<Link to={'/asset'}>
|
||||
<Statistic title="资产数量" value={this.state.counter['asset']}
|
||||
prefix={<DesktopOutlined/>}/>
|
||||
@ -147,7 +147,7 @@ class Dashboard extends Component {
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card bordered={true} hoverable>
|
||||
<Card bordered={true} hoverable={true}>
|
||||
<Link to={'/credential'} hoverable>
|
||||
<Statistic title="授权凭证" value={this.state.counter['credential']}
|
||||
prefix={<IdcardOutlined/>}/>
|
||||
@ -156,7 +156,7 @@ class Dashboard extends Component {
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={6}>
|
||||
<Card bordered={true} hoverable>
|
||||
<Card bordered={true} hoverable={true}>
|
||||
<Link to={'/online-session'}>
|
||||
<Statistic title="在线会话" value={this.state.counter['onlineSession']}
|
||||
prefix={<LinkOutlined/>}/>
|
||||
|
@ -20,7 +20,7 @@ import qs from "qs";
|
||||
import request from "../../common/request";
|
||||
import {formatDate, isEmpty} from "../../utils/utils";
|
||||
import {message} from "antd/es";
|
||||
import {DeleteOutlined, ExclamationCircleOutlined, SyncOutlined, UndoOutlined} from "@ant-design/icons";
|
||||
import {ClearOutlined, DeleteOutlined, ExclamationCircleOutlined, SyncOutlined, UndoOutlined} from "@ant-design/icons";
|
||||
|
||||
|
||||
const confirm = Modal.confirm;
|
||||
@ -152,6 +152,30 @@ class LoginLog extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
clearLoginLogs = async () => {
|
||||
this.setState({
|
||||
clearBtnLoading: true
|
||||
})
|
||||
try {
|
||||
let result = await request.post('/login-logs/clear');
|
||||
if (result.code === 1) {
|
||||
message.success('操作成功,即将跳转至登录页面。', 3);
|
||||
this.setState({
|
||||
selectedRowKeys: []
|
||||
})
|
||||
setTimeout(function () {
|
||||
window.location.reload();
|
||||
}, 3000);
|
||||
} else {
|
||||
message.error(result.message, 10);
|
||||
}
|
||||
} finally {
|
||||
this.setState({
|
||||
clearBtnLoading: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const columns = [{
|
||||
@ -353,6 +377,26 @@ class LoginLog extends Component {
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="清空">
|
||||
<Button type="primary" danger icon={<ClearOutlined/>}
|
||||
loading={this.state.clearBtnLoading}
|
||||
onClick={() => {
|
||||
const title = <Text style={{color: 'red'}}
|
||||
strong>您确定要清空全部的登录日志吗?</Text>;
|
||||
confirm({
|
||||
icon: <ExclamationCircleOutlined/>,
|
||||
title: title,
|
||||
content: '删除用户未注销的登录日志将会强制用户下线,当前登录的用户也会退出登录。',
|
||||
okType: 'danger',
|
||||
onOk: this.clearLoginLogs,
|
||||
onCancel() {
|
||||
|
||||
},
|
||||
});
|
||||
}}>
|
||||
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
|
3
web/src/components/session/OfflineSession.css
Normal file
3
web/src/components/session/OfflineSession.css
Normal file
@ -0,0 +1,3 @@
|
||||
.unreviewed{
|
||||
background: #e6f7ff;
|
||||
}
|
@ -20,9 +20,16 @@ import request from "../../common/request";
|
||||
import {differTime} from "../../utils/utils";
|
||||
import Playback from "./Playback";
|
||||
import {message} from "antd/es";
|
||||
import {DeleteOutlined, ExclamationCircleOutlined, SyncOutlined, UndoOutlined} from "@ant-design/icons";
|
||||
import {
|
||||
CheckOutlined,
|
||||
ClearOutlined,
|
||||
DeleteOutlined,
|
||||
ExclamationCircleOutlined, EyeInvisibleOutlined, EyeOutlined,
|
||||
SyncOutlined,
|
||||
UndoOutlined
|
||||
} from "@ant-design/icons";
|
||||
import {MODE_COLORS, PROTOCOL_COLORS} from "../../common/constants";
|
||||
|
||||
import './OfflineSession.css'
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const confirm = Modal.confirm;
|
||||
@ -42,7 +49,8 @@ class OfflineSession extends Component {
|
||||
pageSize: 10,
|
||||
protocol: '',
|
||||
userId: undefined,
|
||||
assetId: undefined
|
||||
assetId: undefined,
|
||||
reviewed: undefined
|
||||
},
|
||||
loading: false,
|
||||
playbackVisible: false,
|
||||
@ -147,6 +155,16 @@ class OfflineSession extends Component {
|
||||
this.loadTableData(query);
|
||||
}
|
||||
|
||||
handleChangeByRead = reviewed => {
|
||||
let query = {
|
||||
...this.state.queryParams,
|
||||
'pageIndex': 1,
|
||||
'pageSize': this.state.queryParams.pageSize,
|
||||
'reviewed': reviewed,
|
||||
}
|
||||
this.loadTableData(query);
|
||||
}
|
||||
|
||||
handleSearchByNickname = async nickname => {
|
||||
const result = await request.get(`/users/paging?pageIndex=1&pageSize=1000&nickname=${nickname}`);
|
||||
if (result.code !== 1) {
|
||||
@ -213,6 +231,110 @@ class OfflineSession extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleAllReviewed = async () => {
|
||||
this.setState({
|
||||
reviewedAllBtnLoading: true
|
||||
})
|
||||
try {
|
||||
let result = await request.post(`/sessions/reviewed`);
|
||||
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({
|
||||
reviewedAllBtnLoading: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleReviewed = async () => {
|
||||
this.setState({
|
||||
reviewedBtnLoading: true
|
||||
})
|
||||
try {
|
||||
let result = await request.post(`/sessions/${this.state.selectedRowKeys.join(',')}/reviewed`);
|
||||
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({
|
||||
reviewedBtnLoading: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleUnreviewed = async () => {
|
||||
this.setState({
|
||||
unreviewedBtnLoading: true
|
||||
})
|
||||
try {
|
||||
let result = await request.post(`/sessions/${this.state.selectedRowKeys.join(',')}/unreviewed`);
|
||||
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({
|
||||
unreviewedBtnLoading: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
del = async (id) => {
|
||||
const result = await request.delete(`/sessions/${id}`);
|
||||
if (result.code === 1) {
|
||||
notification['success']({
|
||||
message: '提示',
|
||||
description: '删除成功',
|
||||
});
|
||||
this.loadTableData();
|
||||
} else {
|
||||
notification['error']({
|
||||
message: '提示',
|
||||
description: result.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
clearSession = async () => {
|
||||
this.setState({
|
||||
clearBtnLoading: true
|
||||
})
|
||||
try {
|
||||
let result = await request.post('/sessions/clear');
|
||||
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({
|
||||
clearBtnLoading: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const columns = [{
|
||||
@ -221,7 +343,7 @@ class OfflineSession extends Component {
|
||||
key: 'id',
|
||||
render: (id, record, index) => {
|
||||
return index + 1;
|
||||
}
|
||||
},
|
||||
}, {
|
||||
title: '来源IP',
|
||||
dataIndex: 'clientIp',
|
||||
@ -312,34 +434,17 @@ class OfflineSession extends Component {
|
||||
}
|
||||
});
|
||||
}}>禁用IP</Button>
|
||||
<Button type="link" size='small' onClick={() => {
|
||||
<Button type="link" size='small' danger onClick={() => {
|
||||
confirm({
|
||||
title: '您确定要删除此会话吗?',
|
||||
content: '',
|
||||
okText: '确定',
|
||||
okType: 'danger',
|
||||
cancelText: '取消',
|
||||
onOk() {
|
||||
del(record.id)
|
||||
onOk: () => {
|
||||
this.del(record.id)
|
||||
}
|
||||
});
|
||||
|
||||
const del = async (id) => {
|
||||
const result = await request.delete(`/sessions/${id}`);
|
||||
if (result.code === 1) {
|
||||
notification['success']({
|
||||
message: '提示',
|
||||
description: '删除成功',
|
||||
});
|
||||
this.loadTableData();
|
||||
} else {
|
||||
notification['error']({
|
||||
message: '提示',
|
||||
description: result.message,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}}>删除</Button>
|
||||
</div>
|
||||
)
|
||||
@ -366,10 +471,10 @@ class OfflineSession extends Component {
|
||||
<Content className="site-layout-background page-content">
|
||||
<div style={{marginBottom: 20}}>
|
||||
<Row justify="space-around" align="middle" gutter={24}>
|
||||
<Col span={8} key={1}>
|
||||
<Col span={4} key={1}>
|
||||
<Title level={3}>离线会话列表</Title>
|
||||
</Col>
|
||||
<Col span={16} key={2} style={{textAlign: 'right'}}>
|
||||
<Col span={20} key={2} style={{textAlign: 'right'}}>
|
||||
<Space>
|
||||
|
||||
<Search
|
||||
@ -380,7 +485,7 @@ class OfflineSession extends Component {
|
||||
/>
|
||||
|
||||
<Select
|
||||
style={{width: 150}}
|
||||
style={{width: 140}}
|
||||
showSearch
|
||||
value={this.state.queryParams.userId}
|
||||
placeholder='用户昵称'
|
||||
@ -393,7 +498,7 @@ class OfflineSession extends Component {
|
||||
</Select>
|
||||
|
||||
<Select
|
||||
style={{width: 150}}
|
||||
style={{width: 140}}
|
||||
showSearch
|
||||
value={this.state.queryParams.assetId}
|
||||
placeholder='资产名称'
|
||||
@ -404,6 +509,14 @@ class OfflineSession extends Component {
|
||||
{assetOptions}
|
||||
</Select>
|
||||
|
||||
<Select onChange={this.handleChangeByRead}
|
||||
value={this.state.queryParams.reviewed ? this.state.queryParams.reviewed : ''}
|
||||
style={{width: 100}}>
|
||||
<Select.Option value="">全部会话</Select.Option>
|
||||
<Select.Option value="true">只看已读</Select.Option>
|
||||
<Select.Option value="false">只看未读</Select.Option>
|
||||
</Select>
|
||||
|
||||
<Select onChange={this.handleChangeByProtocol}
|
||||
value={this.state.queryParams.protocol ? this.state.queryParams.protocol : ''}
|
||||
style={{width: 100}}>
|
||||
@ -422,9 +535,7 @@ class OfflineSession extends Component {
|
||||
this.loadTableData({
|
||||
pageIndex: 1,
|
||||
pageSize: 10,
|
||||
protocol: '',
|
||||
userId: undefined,
|
||||
assetId: undefined
|
||||
protocol: ''
|
||||
})
|
||||
}}>
|
||||
|
||||
@ -441,6 +552,29 @@ class OfflineSession extends Component {
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="全部标为已阅">
|
||||
<Button icon={<CheckOutlined />}
|
||||
loading={this.state.reviewedAllBtnLoading}
|
||||
onClick={this.handleAllReviewed}>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="标为已阅">
|
||||
<Button disabled={!hasSelected} icon={<EyeOutlined />}
|
||||
loading={this.state.reviewedBtnLoading}
|
||||
onClick={this.handleReviewed}>
|
||||
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="标为未阅">
|
||||
<Button disabled={!hasSelected} icon={<EyeInvisibleOutlined />}
|
||||
loading={this.state.unreviewedBtnLoading}
|
||||
onClick={this.handleUnreviewed}>
|
||||
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="批量删除">
|
||||
<Button type="primary" danger disabled={!hasSelected} icon={<DeleteOutlined/>}
|
||||
loading={this.state.delBtnLoading}
|
||||
@ -457,6 +591,25 @@ class OfflineSession extends Component {
|
||||
},
|
||||
onCancel() {
|
||||
|
||||
},
|
||||
});
|
||||
}}>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title="清空">
|
||||
<Button type="primary" danger icon={<ClearOutlined/>}
|
||||
loading={this.state.clearBtnLoading}
|
||||
onClick={() => {
|
||||
const content = <Text style={{color: 'red'}}
|
||||
strong>您确定要清空全部的离线会话吗?</Text>;
|
||||
confirm({
|
||||
icon: <ExclamationCircleOutlined/>,
|
||||
content: content,
|
||||
okType: 'danger',
|
||||
onOk: this.clearSession,
|
||||
onCancel() {
|
||||
|
||||
},
|
||||
});
|
||||
}}>
|
||||
@ -482,6 +635,9 @@ class OfflineSession extends Component {
|
||||
showTotal: total => `总计 ${total} 条`
|
||||
}}
|
||||
loading={this.state.loading}
|
||||
rowClassName={(record, index) => {
|
||||
return record['reviewed'] ? '' : 'unreviewed';
|
||||
}}
|
||||
/>
|
||||
|
||||
{
|
||||
|
@ -249,9 +249,9 @@ class Playback extends Component {
|
||||
this.startSpeedUp();
|
||||
}
|
||||
}}>
|
||||
<Select.Option key="1">1x</Select.Option>
|
||||
<Select.Option key="2">2x</Select.Option>
|
||||
<Select.Option key="5">5x</Select.Option>
|
||||
<Select.Option key="1" value={1}>1x</Select.Option>
|
||||
<Select.Option key="2" value={2}>2x</Select.Option>
|
||||
<Select.Option key="5" value={5}>5x</Select.Option>
|
||||
</Select>
|
||||
</Col>
|
||||
<Col flex='none'>
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React, {Component} from 'react';
|
||||
import {Button, Form, Input, Layout, Select, Switch, Tabs, Tooltip, Typography} from "antd";
|
||||
import {Alert, Button, Form, Input, Layout, Select, Space, Switch, Tabs, Tooltip, Typography} from "antd";
|
||||
import request from "../../common/request";
|
||||
import {message} from "antd/es";
|
||||
import {ExclamationCircleOutlined} from "@ant-design/icons";
|
||||
import {download, getToken} from "../../utils/utils";
|
||||
import {server} from "../../common/env";
|
||||
|
||||
const {Content} = Layout;
|
||||
const {Option} = Select;
|
||||
@ -91,6 +93,10 @@ class Setting extends Component {
|
||||
if (this.mailSettingFormRef.current) {
|
||||
this.mailSettingFormRef.current.setFieldsValue(properties)
|
||||
}
|
||||
|
||||
if (this.otherSettingFormRef.current) {
|
||||
this.otherSettingFormRef.current.setFieldsValue(properties)
|
||||
}
|
||||
} else {
|
||||
message.error(result['message']);
|
||||
}
|
||||
@ -443,7 +449,6 @@ class Setting extends Component {
|
||||
}
|
||||
|
||||
|
||||
|
||||
<Form.Item {...formTailLayout}>
|
||||
<Button type="primary" htmlType="submit">
|
||||
更新
|
||||
@ -525,7 +530,7 @@ class Setting extends Component {
|
||||
|
||||
<TabPane tab="其他配置" key="other">
|
||||
<Title level={3}>其他配置</Title>
|
||||
<Form ref={this.guacdSettingFormRef} name="other" onFinish={this.changeProperties}
|
||||
<Form ref={this.otherSettingFormRef} name="other" onFinish={this.changeProperties}
|
||||
layout="vertical">
|
||||
|
||||
<Form.Item
|
||||
@ -565,6 +570,30 @@ class Setting extends Component {
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</TabPane>
|
||||
|
||||
<TabPane tab="备份与恢复" key="backup">
|
||||
<Title level={3}>备份与恢复</Title>
|
||||
|
||||
<Space direction="vertical">
|
||||
<Alert
|
||||
message="恢复数据时,如存在登录账号相同的用户时,会保留原系统中的数据,此外由于登录密码加密之后不可逆,恢复的账户密码将随机产生。"
|
||||
type="info"
|
||||
/>
|
||||
|
||||
<Space>
|
||||
<Button type="primary" onClick={() => {
|
||||
download(`${server}/backup/export?X-Auth-Token=${getToken()}&t=${new Date().getTime()}`);
|
||||
}}>
|
||||
导出备份
|
||||
</Button>
|
||||
|
||||
<Button type="dashed">
|
||||
恢复备份
|
||||
</Button>
|
||||
</Space>
|
||||
</Space>
|
||||
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Content>
|
||||
</>
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
Modal,
|
||||
Row,
|
||||
Space,
|
||||
Switch,
|
||||
Table,
|
||||
Tag,
|
||||
Tooltip,
|
||||
@ -26,14 +27,14 @@ import {message} from "antd/es";
|
||||
import {
|
||||
DeleteOutlined,
|
||||
DownOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
ExclamationCircleOutlined, FrownOutlined,
|
||||
InsuranceOutlined,
|
||||
LockOutlined,
|
||||
PlusOutlined,
|
||||
SyncOutlined,
|
||||
UndoOutlined
|
||||
} from '@ant-design/icons';
|
||||
import {hasPermission} from "../../service/permission";
|
||||
import {getCurrentUser} from "../../service/permission";
|
||||
import dayjs from "dayjs";
|
||||
import UserShareSelectedAsset from "./UserShareSelectedAsset";
|
||||
|
||||
@ -73,16 +74,6 @@ class User extends Component {
|
||||
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
|
||||
@ -129,11 +120,10 @@ class User extends Component {
|
||||
queryParams: queryParams
|
||||
});
|
||||
|
||||
this.loadTableData(queryParams).then(r => {
|
||||
})
|
||||
this.loadTableData(queryParams);
|
||||
};
|
||||
|
||||
showDeleteConfirm(id, content) {
|
||||
showDeleteConfirm(id, content, index) {
|
||||
let self = this;
|
||||
confirm({
|
||||
title: '您确定要删除此用户吗?',
|
||||
@ -142,7 +132,7 @@ class User extends Component {
|
||||
okType: 'danger',
|
||||
cancelText: '取消',
|
||||
onOk() {
|
||||
self.delete(id);
|
||||
self.delete(id, index);
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -167,7 +157,6 @@ class User extends Component {
|
||||
this.setState({
|
||||
modalConfirmLoading: true
|
||||
});
|
||||
|
||||
if (formData.id) {
|
||||
// 向后台提交数据
|
||||
const result = await request.put('/users/' + formData.id, formData);
|
||||
@ -293,6 +282,72 @@ class User extends Component {
|
||||
this.loadTableData(query);
|
||||
}
|
||||
|
||||
async delete(id, index) {
|
||||
let items = this.state.items;
|
||||
try {
|
||||
items[index]['delLoading'] = true;
|
||||
this.setState({
|
||||
items: items
|
||||
});
|
||||
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);
|
||||
}
|
||||
} finally {
|
||||
items[index]['delLoading'] = false;
|
||||
this.setState({
|
||||
items: items
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
changeUserStatus = async (id, checked, index) => {
|
||||
let items = this.state.items;
|
||||
try {
|
||||
items[index]['statusLoading'] = true;
|
||||
this.setState({
|
||||
items: items
|
||||
});
|
||||
let result = await request.patch(`/users/${id}/status?status=${checked ? 'enabled' : 'disabled'}`);
|
||||
if (result['code'] !== 1) {
|
||||
message.error(result['message']);
|
||||
return
|
||||
}
|
||||
this.loadTableData(this.state.queryParams);
|
||||
} finally {
|
||||
items[index]['statusLoading'] = false;
|
||||
this.setState({
|
||||
items: items
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
resetTOTP = async (id, index) => {
|
||||
let items = this.state.items;
|
||||
try {
|
||||
items[index]['resetTOTPLoading'] = true;
|
||||
this.setState({
|
||||
items: items
|
||||
});
|
||||
let result = await request.post(`/users/${id}/reset-totp`);
|
||||
if (result['code'] === 1) {
|
||||
message.success('操作成功', 3);
|
||||
this.loadTableData();
|
||||
} else {
|
||||
message.error(result['message'], 10);
|
||||
}
|
||||
} finally {
|
||||
items[index]['resetTOTPLoading'] = false;
|
||||
this.setState({
|
||||
items: items
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const columns = [{
|
||||
@ -348,15 +403,28 @@ class User extends Component {
|
||||
dataIndex: 'mail',
|
||||
key: 'mail',
|
||||
}, {
|
||||
title: '二次认证',
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
render: (status, record, index) => {
|
||||
return <Switch checkedChildren="启用" unCheckedChildren="停用"
|
||||
disabled={getCurrentUser()['id'] === record['id']}
|
||||
loading={record['statusLoading']}
|
||||
checked={status !== 'disabled'}
|
||||
onChange={checked => {
|
||||
this.changeUserStatus(record['id'], checked, index);
|
||||
}}/>
|
||||
}
|
||||
}, {
|
||||
title: '双因素认证',
|
||||
dataIndex: 'totpSecret',
|
||||
key: 'totpSecret',
|
||||
render: (text, record) => {
|
||||
|
||||
if (text === '1') {
|
||||
return <Tag icon={<InsuranceOutlined/>} color="success">开启</Tag>;
|
||||
return <Tag icon={<InsuranceOutlined/>} color="success">已开启</Tag>;
|
||||
} else {
|
||||
return <Tag icon={<ExclamationCircleOutlined/>} color="warning">关闭</Tag>;
|
||||
return <Tag icon={<FrownOutlined />} color="warning">未开启</Tag>;
|
||||
}
|
||||
}
|
||||
}, {
|
||||
@ -398,7 +466,7 @@ class User extends Component {
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
render: (text, record) => {
|
||||
render: (text, record, index) => {
|
||||
|
||||
const menu = (
|
||||
<Menu>
|
||||
@ -414,6 +482,7 @@ class User extends Component {
|
||||
|
||||
<Menu.Item key="2">
|
||||
<Button type="text" size='small'
|
||||
loading={record['resetTOTPLoading']}
|
||||
onClick={() => {
|
||||
confirm({
|
||||
title: '您确定要重置此用户的双因素认证吗?',
|
||||
@ -421,13 +490,7 @@ class User extends Component {
|
||||
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);
|
||||
}
|
||||
this.resetTOTP(record['id'], index);
|
||||
}
|
||||
});
|
||||
}}>重置双因素认证</Button>
|
||||
@ -446,8 +509,9 @@ class User extends Component {
|
||||
<Menu.Divider/>
|
||||
<Menu.Item key="5">
|
||||
<Button type="text" size='small' danger
|
||||
disabled={!hasPermission(record['owner'])}
|
||||
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
|
||||
disabled={getCurrentUser()['id'] === record['id']}
|
||||
loading={record['delLoading']}
|
||||
onClick={() => this.showDeleteConfirm(record.id, record.name, index)}>删除</Button>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
@ -544,53 +608,6 @@ class User extends Component {
|
||||
</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}
|
||||
|
@ -61,7 +61,6 @@ const UserModal = ({title, visible, handleOk, handleCancel, confirmLoading, mode
|
||||
<Input type="password" autoComplete="new-password" placeholder="输入登录密码"/>
|
||||
</Form.Item>) : null
|
||||
}
|
||||
|
||||
</Form>
|
||||
</Modal>
|
||||
)
|
||||
|
Reference in New Issue
Block a user