1024 lines
40 KiB
JavaScript
1024 lines
40 KiB
JavaScript
import React, {Component} from 'react';
|
||
|
||
import {
|
||
Alert,
|
||
Badge,
|
||
Button,
|
||
Col,
|
||
Divider,
|
||
Dropdown,
|
||
Form,
|
||
Input,
|
||
Layout,
|
||
Menu,
|
||
Modal,
|
||
notification,
|
||
PageHeader,
|
||
Row,
|
||
Select,
|
||
Space,
|
||
Table,
|
||
Tag,
|
||
Tooltip,
|
||
Transfer,
|
||
Typography
|
||
} from "antd";
|
||
import qs from "qs";
|
||
import AssetModal from "./AssetModal";
|
||
import request from "../../common/request";
|
||
import {message} from "antd/es";
|
||
import {getHeaders, isEmpty, itemRender} from "../../utils/utils";
|
||
import dayjs from 'dayjs';
|
||
import {
|
||
DeleteOutlined,
|
||
DownOutlined,
|
||
ExclamationCircleOutlined,
|
||
ImportOutlined,
|
||
PlusOutlined,
|
||
SyncOutlined,
|
||
UndoOutlined,
|
||
UploadOutlined
|
||
} from '@ant-design/icons';
|
||
import {PROTOCOL_COLORS} from "../../common/constants";
|
||
|
||
import {hasPermission, isAdmin} from "../../service/permission";
|
||
import Upload from "antd/es/upload";
|
||
import axios from "axios";
|
||
import {server} from "../../common/env";
|
||
|
||
|
||
const confirm = Modal.confirm;
|
||
const {Search} = Input;
|
||
const {Content} = Layout;
|
||
const {Title, Text} = Typography;
|
||
|
||
class Asset extends Component {
|
||
|
||
inputRefOfName = React.createRef();
|
||
inputRefOfIp = React.createRef();
|
||
changeOwnerFormRef = React.createRef();
|
||
|
||
state = {
|
||
items: [],
|
||
total: 0,
|
||
queryParams: {
|
||
pageIndex: 1,
|
||
pageSize: 10,
|
||
protocol: '',
|
||
tags: ''
|
||
},
|
||
loading: false,
|
||
modalVisible: false,
|
||
modalTitle: '',
|
||
modalConfirmLoading: false,
|
||
credentials: [],
|
||
tags: [],
|
||
selectedTags: [],
|
||
model: {},
|
||
selectedRowKeys: [],
|
||
delBtnLoading: false,
|
||
changeOwnerModalVisible: false,
|
||
changeSharerModalVisible: false,
|
||
changeOwnerConfirmLoading: false,
|
||
changeSharerConfirmLoading: false,
|
||
users: [],
|
||
selected: {},
|
||
selectedSharers: [],
|
||
importModalVisible: false,
|
||
fileList: [],
|
||
uploading: false,
|
||
};
|
||
|
||
async componentDidMount() {
|
||
|
||
this.loadTableData();
|
||
|
||
let result = await request.get('/tags');
|
||
if (result['code'] === 1) {
|
||
this.setState({
|
||
tags: result['data']
|
||
})
|
||
}
|
||
}
|
||
|
||
async delete(id) {
|
||
const result = await request.delete('/assets/' + id);
|
||
if (result['code'] === 1) {
|
||
message.success('删除成功');
|
||
await this.loadTableData(this.state.queryParams);
|
||
} else {
|
||
message.error('删除失败 :( ' + result.message, 10);
|
||
}
|
||
|
||
}
|
||
|
||
async loadTableData(queryParams) {
|
||
this.setState({
|
||
loading: true
|
||
});
|
||
|
||
queryParams = queryParams || this.state.queryParams;
|
||
|
||
// queryParams
|
||
let paramsStr = qs.stringify(queryParams);
|
||
|
||
let data = {
|
||
items: [],
|
||
total: 0
|
||
};
|
||
|
||
try {
|
||
let result = await request.get('/assets/paging?' + paramsStr);
|
||
if (result['code'] === 1) {
|
||
data = result['data'];
|
||
} else {
|
||
message.error(result['message']);
|
||
}
|
||
} 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 = async (pageIndex, pageSize) => {
|
||
let queryParams = this.state.queryParams;
|
||
queryParams.pageIndex = pageIndex;
|
||
queryParams.pageSize = pageSize;
|
||
|
||
this.setState({
|
||
queryParams: queryParams
|
||
});
|
||
|
||
await this.loadTableData(queryParams)
|
||
};
|
||
|
||
handleSearchByName = name => {
|
||
let query = {
|
||
...this.state.queryParams,
|
||
'pageIndex': 1,
|
||
'pageSize': this.state.queryParams.pageSize,
|
||
'name': name,
|
||
}
|
||
|
||
this.loadTableData(query);
|
||
};
|
||
|
||
handleSearchByIp = ip => {
|
||
let query = {
|
||
...this.state.queryParams,
|
||
'pageIndex': 1,
|
||
'pageSize': this.state.queryParams.pageSize,
|
||
'ip': ip,
|
||
}
|
||
|
||
this.loadTableData(query);
|
||
};
|
||
|
||
handleTagsChange = tags => {
|
||
this.setState({
|
||
selectedTags: tags
|
||
})
|
||
let query = {
|
||
...this.state.queryParams,
|
||
'pageIndex': 1,
|
||
'pageSize': this.state.queryParams.pageSize,
|
||
'tags': tags.join(','),
|
||
}
|
||
|
||
this.loadTableData(query);
|
||
}
|
||
|
||
handleSearchByProtocol = protocol => {
|
||
let query = {
|
||
...this.state.queryParams,
|
||
'pageIndex': 1,
|
||
'pageSize': this.state.queryParams.pageSize,
|
||
'protocol': protocol,
|
||
}
|
||
this.loadTableData(query);
|
||
}
|
||
|
||
showDeleteConfirm(id, content) {
|
||
let self = this;
|
||
confirm({
|
||
title: '您确定要删除此资产吗?',
|
||
content: content,
|
||
okText: '确定',
|
||
okType: 'danger',
|
||
cancelText: '取消',
|
||
onOk() {
|
||
self.delete(id);
|
||
}
|
||
});
|
||
};
|
||
|
||
async update(id) {
|
||
let result = await request.get(`/assets/${id}`);
|
||
if (result.code !== 1) {
|
||
message.error(result.message, 10);
|
||
return;
|
||
}
|
||
await this.showModal('更新资产', result.data);
|
||
}
|
||
|
||
async copy(id) {
|
||
let result = await request.get(`/assets/${id}`);
|
||
if (result.code !== 1) {
|
||
message.error(result.message, 10);
|
||
return;
|
||
}
|
||
result.data['id'] = undefined;
|
||
await this.showModal('复制资产', result.data);
|
||
}
|
||
|
||
async showModal(title, asset = {}) {
|
||
// 并行请求
|
||
let getCredentials = request.get('/credentials');
|
||
let getTags = request.get('/tags');
|
||
|
||
let credentials = [];
|
||
let tags = [];
|
||
|
||
let r1 = await getCredentials;
|
||
let r2 = await getTags;
|
||
|
||
if (r1['code'] === 1) {
|
||
credentials = r1['data'];
|
||
}
|
||
|
||
if (r2['code'] === 1) {
|
||
tags = r2['data'];
|
||
}
|
||
|
||
if (asset['tags'] && typeof (asset['tags']) === 'string') {
|
||
if (asset['tags'] === '' || asset['tags'] === '-') {
|
||
asset['tags'] = [];
|
||
} else {
|
||
asset['tags'] = asset['tags'].split(',');
|
||
}
|
||
} else {
|
||
asset['tags'] = [];
|
||
}
|
||
|
||
asset['use-ssl'] = asset['use-ssl'] === 'true';
|
||
|
||
asset['ignore-cert'] = asset['ignore-cert'] === 'true';
|
||
|
||
console.log(asset)
|
||
|
||
this.setState({
|
||
modalTitle: title,
|
||
modalVisible: true,
|
||
credentials: credentials,
|
||
tags: tags,
|
||
model: asset
|
||
});
|
||
};
|
||
|
||
handleCancelModal = e => {
|
||
this.setState({
|
||
modalTitle: '',
|
||
modalVisible: false
|
||
});
|
||
};
|
||
|
||
handleOk = async (formData) => {
|
||
// 弹窗 form 传来的数据
|
||
this.setState({
|
||
modalConfirmLoading: true
|
||
});
|
||
|
||
console.log(formData)
|
||
if (formData['tags']) {
|
||
formData.tags = formData['tags'].join(',');
|
||
}
|
||
|
||
if (formData.id) {
|
||
// 向后台提交数据
|
||
const result = await request.put('/assets/' + 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('/assets', 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
|
||
});
|
||
};
|
||
|
||
access = async (record) => {
|
||
const id = record['id'];
|
||
const protocol = record['protocol'];
|
||
const name = record['name'];
|
||
|
||
message.loading({content: '正在检测资产是否在线...', key: id});
|
||
let result = await request.post(`/assets/${id}/tcping`);
|
||
if (result.code === 1) {
|
||
if (result.data === true) {
|
||
message.success({content: '检测完成,您访问的资产在线,即将打开窗口进行访问。', key: id, duration: 3});
|
||
if (protocol === 'ssh') {
|
||
result = await request.get(`/assets/${id}/attributes`);
|
||
if (result.code === 1 && result['data']['ssh-mode'] === 'naive') {
|
||
window.open(`#/term?assetId=${id}&assetName=${name}`);
|
||
} else {
|
||
window.open(`#/access?assetId=${id}&assetName=${name}&protocol=${protocol}`);
|
||
}
|
||
} else {
|
||
window.open(`#/access?assetId=${id}&assetName=${name}&protocol=${protocol}`);
|
||
}
|
||
} else {
|
||
message.warn('您访问的资产未在线,请确认网络状态。', 10);
|
||
}
|
||
} else {
|
||
message.error('操作失败 :( ' + result.message, 10);
|
||
}
|
||
|
||
}
|
||
|
||
batchDelete = async () => {
|
||
this.setState({
|
||
delBtnLoading: true
|
||
})
|
||
try {
|
||
let result = await request.delete('/assets/' + 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
|
||
})
|
||
}
|
||
}
|
||
|
||
handleSearchByNickname = async nickname => {
|
||
const result = await request.get(`/users/paging?pageIndex=1&pageSize=100&nickname=${nickname}`);
|
||
if (result.code !== 1) {
|
||
message.error(result.message, 10);
|
||
return;
|
||
}
|
||
|
||
const items = result['data']['items'].map(item => {
|
||
return {'key': item['id'], ...item}
|
||
})
|
||
|
||
this.setState({
|
||
users: items
|
||
})
|
||
}
|
||
|
||
handleSharersChange = async targetKeys => {
|
||
this.setState({
|
||
selectedSharers: targetKeys
|
||
})
|
||
}
|
||
|
||
handleShowSharer = async (record) => {
|
||
let r1 = this.handleSearchByNickname('');
|
||
let r2 = request.get(`/resource-sharers/sharers?resourceId=${record['id']}`);
|
||
|
||
await r1;
|
||
let result = await r2;
|
||
|
||
let selectedSharers = [];
|
||
if (result['code'] !== 1) {
|
||
message.error(result['message']);
|
||
} else {
|
||
selectedSharers = result['data'];
|
||
}
|
||
|
||
let users = this.state.users;
|
||
users = users.map(item => {
|
||
let disabled = false;
|
||
if (record['owner'] === item['id']) {
|
||
disabled = true;
|
||
}
|
||
return {...item, 'disabled': disabled}
|
||
});
|
||
|
||
this.setState({
|
||
selectedSharers: selectedSharers,
|
||
selected: record,
|
||
changeSharerModalVisible: true,
|
||
users: users
|
||
})
|
||
}
|
||
|
||
handleCancelUpdateAttr = () => {
|
||
this.setState({
|
||
attrVisible: false,
|
||
selected: {},
|
||
attributes: {}
|
||
});
|
||
}
|
||
|
||
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: 'name',
|
||
key: 'name',
|
||
render: (name, record) => {
|
||
let short = name;
|
||
if (short && short.length > 20) {
|
||
short = short.substring(0, 20) + " ...";
|
||
}
|
||
return (
|
||
<Tooltip placement="topLeft" title={name}>
|
||
{short}
|
||
</Tooltip>
|
||
);
|
||
},
|
||
sorter: true,
|
||
}, {
|
||
title: '连接协议',
|
||
dataIndex: 'protocol',
|
||
key: 'protocol',
|
||
render: (text, record) => {
|
||
const title = `${record['ip'] + ':' + record['port']}`
|
||
return (
|
||
<Tooltip title={title}>
|
||
<Tag color={PROTOCOL_COLORS[text]}>{text}</Tag>
|
||
</Tooltip>
|
||
)
|
||
}
|
||
}, {
|
||
title: '标签',
|
||
dataIndex: 'tags',
|
||
key: 'tags',
|
||
render: tags => {
|
||
if (!isEmpty(tags)) {
|
||
let tagDocuments = []
|
||
let tagArr = tags.split(',');
|
||
for (let i = 0; i < tagArr.length; i++) {
|
||
if (tags[i] === '-') {
|
||
continue;
|
||
}
|
||
tagDocuments.push(<Tag key={tagArr[i]}>{tagArr[i]}</Tag>)
|
||
}
|
||
return tagDocuments;
|
||
}
|
||
}
|
||
}, {
|
||
title: '状态',
|
||
dataIndex: 'active',
|
||
key: 'active',
|
||
render: text => {
|
||
|
||
if (text) {
|
||
return (
|
||
<Tooltip title='运行中'>
|
||
<Badge status="processing"/>
|
||
</Tooltip>
|
||
)
|
||
} else {
|
||
return (
|
||
<Tooltip title='不可用'>
|
||
<Badge status="error"/>
|
||
</Tooltip>
|
||
)
|
||
}
|
||
}
|
||
}, {
|
||
title: '所有者',
|
||
dataIndex: 'ownerName',
|
||
key: 'ownerName'
|
||
}, {
|
||
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'
|
||
disabled={!hasPermission(record['owner'])}
|
||
onClick={() => this.update(record.id)}>编辑</Button>
|
||
</Menu.Item>
|
||
|
||
<Menu.Item key="2">
|
||
<Button type="text" size='small'
|
||
disabled={!hasPermission(record['owner'])}
|
||
onClick={() => this.copy(record.id)}>复制</Button>
|
||
</Menu.Item>
|
||
|
||
{isAdmin() ?
|
||
<Menu.Item key="4">
|
||
<Button type="text" size='small'
|
||
disabled={!hasPermission(record['owner'])}
|
||
onClick={() => {
|
||
this.handleSearchByNickname('')
|
||
.then(() => {
|
||
this.setState({
|
||
changeOwnerModalVisible: true,
|
||
selected: record,
|
||
})
|
||
this.changeOwnerFormRef
|
||
.current
|
||
.setFieldsValue({
|
||
owner: record['owner']
|
||
})
|
||
});
|
||
|
||
}}>更换所有者</Button>
|
||
</Menu.Item> : undefined
|
||
}
|
||
|
||
|
||
<Menu.Item key="5">
|
||
<Button type="text" size='small'
|
||
disabled={!hasPermission(record['owner'])}
|
||
onClick={async () => {
|
||
await this.handleShowSharer(record);
|
||
}}>更新授权人</Button>
|
||
</Menu.Item>
|
||
|
||
<Menu.Divider/>
|
||
<Menu.Item key="6">
|
||
<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={() => this.access(record)}>接入</Button>
|
||
|
||
<Dropdown overlay={menu}>
|
||
<Button type="link" size='small'>
|
||
更多 <DownOutlined/>
|
||
</Button>
|
||
</Dropdown>
|
||
</div>
|
||
)
|
||
},
|
||
}
|
||
];
|
||
|
||
if (isAdmin()) {
|
||
columns.splice(6, 0, {
|
||
title: '授权人数',
|
||
dataIndex: 'sharerCount',
|
||
key: 'sharerCount',
|
||
render: (text, record, index) => {
|
||
return <Button type='link' onClick={async () => {
|
||
await this.handleShowSharer(record, true);
|
||
}}>{text}</Button>
|
||
}
|
||
});
|
||
}
|
||
|
||
const selectedRowKeys = this.state.selectedRowKeys;
|
||
const rowSelection = {
|
||
selectedRowKeys: this.state.selectedRowKeys,
|
||
onChange: (selectedRowKeys, selectedRows) => {
|
||
this.setState({selectedRowKeys});
|
||
},
|
||
};
|
||
const hasSelected = selectedRowKeys.length > 0;
|
||
|
||
return (
|
||
<>
|
||
<Content key='page-content' className="site-layout-background page-content">
|
||
<div style={{marginBottom: 20}}>
|
||
<Row justify="space-around" align="middle" gutter={24}>
|
||
<Col span={4} key={1}>
|
||
<Title level={3}>资产列表</Title>
|
||
</Col>
|
||
<Col span={20} key={2} style={{textAlign: 'right'}}>
|
||
<Space>
|
||
|
||
<Search
|
||
ref={this.inputRefOfName}
|
||
placeholder="资产名称"
|
||
allowClear
|
||
onSearch={this.handleSearchByName}
|
||
style={{width: 200}}
|
||
/>
|
||
|
||
<Search
|
||
ref={this.inputRefOfIp}
|
||
placeholder="资产IP"
|
||
allowClear
|
||
onSearch={this.handleSearchByIp}
|
||
style={{width: 200}}
|
||
/>
|
||
|
||
<Select mode="multiple"
|
||
allowClear
|
||
value={this.state.selectedTags}
|
||
placeholder="资产标签" onChange={this.handleTagsChange}
|
||
style={{minWidth: 150}}>
|
||
{this.state.tags.map(tag => {
|
||
if (tag === '-') {
|
||
return undefined;
|
||
}
|
||
return (<Select.Option key={tag}>{tag}</Select.Option>)
|
||
})}
|
||
</Select>
|
||
|
||
<Select onChange={this.handleSearchByProtocol}
|
||
value={this.state.queryParams.protocol ? this.state.queryParams.protocol : ''}
|
||
style={{width: 100}}>
|
||
<Select.Option value="">全部协议</Select.Option>
|
||
<Select.Option value="rdp">rdp</Select.Option>
|
||
<Select.Option value="ssh">ssh</Select.Option>
|
||
<Select.Option value="vnc">vnc</Select.Option>
|
||
<Select.Option value="telnet">telnet</Select.Option>
|
||
</Select>
|
||
|
||
<Tooltip title='重置查询'>
|
||
|
||
<Button icon={<UndoOutlined/>} onClick={() => {
|
||
this.inputRefOfName.current.setValue('');
|
||
this.inputRefOfIp.current.setValue('');
|
||
this.setState({
|
||
selectedTags: []
|
||
})
|
||
this.loadTableData({pageIndex: 1, pageSize: 10, protocol: '', tags: ''})
|
||
}}>
|
||
|
||
</Button>
|
||
</Tooltip>
|
||
|
||
<Divider type="vertical"/>
|
||
|
||
{isAdmin() ?
|
||
<Tooltip title="批量导入">
|
||
<Button type="dashed" icon={<ImportOutlined/>}
|
||
onClick={() => {
|
||
this.setState({
|
||
importModalVisible: true
|
||
})
|
||
}}>
|
||
|
||
</Button>
|
||
</Tooltip> : undefined
|
||
}
|
||
|
||
|
||
<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="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 key='assets-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}
|
||
/>
|
||
|
||
{
|
||
this.state.modalVisible ?
|
||
<AssetModal
|
||
visible={this.state.modalVisible}
|
||
title={this.state.modalTitle}
|
||
handleOk={this.handleOk}
|
||
handleCancel={this.handleCancelModal}
|
||
confirmLoading={this.state.modalConfirmLoading}
|
||
credentials={this.state.credentials}
|
||
tags={this.state.tags}
|
||
model={this.state.model}
|
||
/>
|
||
: null
|
||
}
|
||
|
||
{
|
||
this.state.importModalVisible ?
|
||
<Modal title="资产导入" visible={true}
|
||
onOk={() => {
|
||
const formData = new FormData();
|
||
formData.append("file", this.state.fileList[0]);
|
||
|
||
let headers = getHeaders();
|
||
headers['Content-Type'] = 'multipart/form-data';
|
||
|
||
axios
|
||
.post(server + "/assets/import", formData, {
|
||
headers: headers
|
||
})
|
||
.then((resp) => {
|
||
console.log("上传成功", resp);
|
||
this.setState({
|
||
importModalVisible: false
|
||
})
|
||
let result = resp.data;
|
||
if (result['code'] === 1) {
|
||
let data = result['data'];
|
||
let successCount = data['successCount'];
|
||
let errorCount = data['errorCount'];
|
||
if (errorCount === 0) {
|
||
notification['success']({
|
||
message: '导入资产成功',
|
||
description: '共导入成功' + successCount + '条资产。',
|
||
});
|
||
} else {
|
||
notification['info']({
|
||
message: '导入资产完成',
|
||
description: `共导入成功${successCount}条资产,失败${errorCount}条资产。`,
|
||
});
|
||
}
|
||
} else {
|
||
notification['error']({
|
||
message: '导入资产失败',
|
||
description: result['message'],
|
||
});
|
||
}
|
||
this.loadTableData();
|
||
});
|
||
}}
|
||
onCancel={() => {
|
||
this.setState({
|
||
importModalVisible: false
|
||
})
|
||
}}
|
||
okButtonProps={{
|
||
disabled: this.state.fileList.length === 0
|
||
}}
|
||
>
|
||
<Space>
|
||
<Upload
|
||
maxCount={1}
|
||
onRemove={file => {
|
||
this.setState(state => {
|
||
const index = state.fileList.indexOf(file);
|
||
const newFileList = state.fileList.slice();
|
||
newFileList.splice(index, 1);
|
||
return {
|
||
fileList: newFileList,
|
||
};
|
||
});
|
||
}}
|
||
beforeUpload={(file) => {
|
||
this.setState(state => ({
|
||
fileList: [file],
|
||
}));
|
||
return false;
|
||
}}
|
||
fileList={this.state.fileList}
|
||
>
|
||
<Button icon={<UploadOutlined/>}>选择csv文件</Button>
|
||
</Upload>
|
||
|
||
<Button type="primary" onClick={() => {
|
||
|
||
let csvString= 'name,ssh,127.0.0.1,22,username,password,privateKey,passphrase,description';
|
||
//前置的"\uFEFF"为“零宽不换行空格”,可处理中文乱码问题
|
||
const blob = new Blob(["\uFEFF" + csvString], {type: 'text/csv;charset=gb2312;'});
|
||
let a = document.createElement('a');
|
||
a.download = 'sample.csv';
|
||
a.href = URL.createObjectURL(blob);
|
||
a.click();
|
||
}}>
|
||
下载样本文件
|
||
</Button>
|
||
</Space>
|
||
|
||
</Modal>
|
||
: undefined
|
||
}
|
||
|
||
<Modal title={<Text>更换资源「<strong style={{color: '#1890ff'}}>{this.state.selected['name']}</strong>」的所有者
|
||
</Text>}
|
||
visible={this.state.changeOwnerModalVisible}
|
||
confirmLoading={this.state.changeOwnerConfirmLoading}
|
||
centered={true}
|
||
onOk={() => {
|
||
this.setState({
|
||
changeOwnerConfirmLoading: true
|
||
});
|
||
|
||
let changeOwnerModalVisible = false;
|
||
this.changeOwnerFormRef
|
||
.current
|
||
.validateFields()
|
||
.then(async values => {
|
||
let result = await request.post(`/assets/${this.state.selected['id']}/change-owner?owner=${values['owner']}`);
|
||
if (result['code'] === 1) {
|
||
message.success('操作成功');
|
||
this.loadTableData();
|
||
} else {
|
||
message.error(result['message'], 10);
|
||
changeOwnerModalVisible = true;
|
||
}
|
||
})
|
||
.catch(info => {
|
||
|
||
})
|
||
.finally(() => {
|
||
this.setState({
|
||
changeOwnerConfirmLoading: false,
|
||
changeOwnerModalVisible: changeOwnerModalVisible
|
||
})
|
||
});
|
||
}}
|
||
onCancel={() => {
|
||
this.setState({
|
||
changeOwnerModalVisible: false
|
||
})
|
||
}}
|
||
>
|
||
|
||
<Form ref={this.changeOwnerFormRef}>
|
||
|
||
<Form.Item name='owner' rules={[{required: true, message: '请选择所有者'}]}>
|
||
<Select
|
||
showSearch
|
||
placeholder='请选择所有者'
|
||
onSearch={this.handleSearchByNickname}
|
||
filterOption={false}
|
||
>
|
||
{this.state.users.map(d => <Select.Option key={d.id}
|
||
value={d.id}>{d.nickname}</Select.Option>)}
|
||
</Select>
|
||
</Form.Item>
|
||
<Alert message="更换资产所有者不会影响授权凭证的所有者" type="info" showIcon/>
|
||
|
||
</Form>
|
||
</Modal>
|
||
|
||
|
||
{
|
||
this.state.changeSharerModalVisible ?
|
||
<Modal title={<Text>更新资源「<strong
|
||
style={{color: '#1890ff'}}>{this.state.selected['name']}</strong>」的授权人
|
||
</Text>}
|
||
visible={this.state.changeSharerModalVisible}
|
||
confirmLoading={this.state.changeSharerConfirmLoading}
|
||
centered={true}
|
||
onOk={async () => {
|
||
this.setState({
|
||
changeSharerConfirmLoading: true
|
||
});
|
||
|
||
let changeSharerModalVisible = false;
|
||
|
||
let result = await request.post(`/resource-sharers/overwrite-sharers`, {
|
||
resourceId: this.state.selected['id'],
|
||
resourceType: 'asset',
|
||
userIds: this.state.selectedSharers
|
||
});
|
||
if (result['code'] === 1) {
|
||
message.success('操作成功');
|
||
this.loadTableData();
|
||
} else {
|
||
message.error(result['message'], 10);
|
||
changeSharerModalVisible = true;
|
||
}
|
||
|
||
this.setState({
|
||
changeSharerConfirmLoading: false,
|
||
changeSharerModalVisible: changeSharerModalVisible
|
||
})
|
||
}}
|
||
onCancel={() => {
|
||
this.setState({
|
||
changeSharerModalVisible: false
|
||
})
|
||
}}
|
||
okButtonProps={{disabled: !hasPermission(this.state.selected['owner'])}}
|
||
>
|
||
|
||
<Transfer
|
||
dataSource={this.state.users}
|
||
disabled={!hasPermission(this.state.selected['owner'])}
|
||
showSearch
|
||
titles={['未授权', '已授权']}
|
||
operations={['授权', '移除']}
|
||
listStyle={{
|
||
width: 250,
|
||
height: 300,
|
||
}}
|
||
targetKeys={this.state.selectedSharers}
|
||
onChange={this.handleSharersChange}
|
||
render={item => `${item.nickname}`}
|
||
/>
|
||
</Modal> : undefined
|
||
}
|
||
</Content>
|
||
</>
|
||
);
|
||
}
|
||
}
|
||
|
||
export default Asset;
|