@ant-design/icons#ExclamationCircleOutlined JavaScript Examples
The following examples show how to use
@ant-design/icons#ExclamationCircleOutlined.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: status.jsx From virtuoso-design-system with MIT License | 6 votes |
storiesOf('antd/tag', module).add('status', () =>
<>
<Divider orientation="left">Without icon</Divider>
<div>
<Tag color="success">success</Tag>
<Tag color="processing">processing</Tag>
<Tag color="error">error</Tag>
<Tag color="warning">warning</Tag>
<Tag color="default">default</Tag>
</div>
<Divider orientation="left">With icon</Divider>
<div>
<Tag icon={<CheckCircleOutlined />} color="success">
success
</Tag>
<Tag icon={<SyncOutlined spin />} color="processing">
processing
</Tag>
<Tag icon={<CloseCircleOutlined />} color="error">
error
</Tag>
<Tag icon={<ExclamationCircleOutlined />} color="warning">
warning
</Tag>
<Tag icon={<ClockCircleOutlined />} color="default">
waiting
</Tag>
<Tag icon={<MinusCircleOutlined />} color="default">
stop
</Tag>
</div>
</>,
{ docs: { page: () => (<><h1 id="enus">en-US</h1>
<p>We preset five different colors, you can set color property such as <code>success</code>,<code>processing</code>,<code>error</code>,<code>default</code> and <code>warning</code> to indicate specific status.</p></>) } });
Example #2
Source File: index.jsx From mixbox with GNU General Public License v3.0 | 6 votes |
export default function batchDeleteConfirm(count) {
const content = (
<div>
您确定删除
<span style={{padding: '0 5px', color: 'red', fontSize: 18}}>
{count}
</span>
条记录吗?
</div>
);
return new Promise((resolve, reject) => {
Modal.confirm({
title: '温馨提示',
content,
icon: <ExclamationCircleOutlined/>,
okType: 'danger',
onOk: () => resolve(true),
onCancel: () => reject('user cancel confirm'),
});
});
}
Example #3
Source File: function.js From ant-simple-pro with MIT License | 6 votes |
confirm = (func, content = '确定要删除吗?', onCancel) => {
Modal.confirm({
title: '提示',
content,
okText: '确认',
centered: true,
icon: <ExclamationCircleOutlined />,
cancelText: '取消',
onOk: async () => {
func && func();
},
onCancel() {
onCancel && onCancel();
},
});
}
Example #4
Source File: index.js From quick_redis_blog with MIT License | 6 votes |
/**
*删除节点
*
* @param {*} keys
* @memberof ResourceTree
*/
deleteNode(keys) {
let titles = [];
for (let k of keys) {
let retObj = this.findNodeByNodeKey(this.props.hosts, k);
titles.push(retObj.title);
}
let { confirm } = Modal;
confirm({
title: intl.get("ResourceTree.delete.notice"),
icon: <ExclamationCircleOutlined />,
content: "" + titles,
onOk: () => {
this.props.delHostsData(keys);
resourceTreeSelectKeys = [];
},
onCancel() {},
});
}
Example #5
Source File: confirm-router.jsx From virtuoso-design-system with MIT License | 6 votes |
function showConfirm() {
for (let i = 0; i < 3; i += 1) {
setTimeout(() => {
confirm({
icon: <ExclamationCircleOutlined />,
content: <Button onClick={destroyAll}>Click to destroy all</Button>,
onOk() {
console.log('OK');
},
onCancel() {
console.log('Cancel');
},
});
}, i * 500);
}
}
Example #6
Source File: Term.js From next-terminal with GNU Affero General Public License v3.0 | 6 votes |
showMessage(msg) {
message.destroy();
Modal.confirm({
title: '提示',
icon: <ExclamationCircleOutlined/>,
content: msg,
centered: true,
okText: '重新连接',
cancelText: '关闭页面',
onOk() {
window.location.reload();
},
onCancel() {
window.close();
},
});
}
Example #7
Source File: Access.js From next-terminal with GNU Affero General Public License v3.0 | 6 votes |
showMessage(msg) {
message.destroy();
Modal.confirm({
title: '提示',
icon: <ExclamationCircleOutlined/>,
content: msg,
centered: true,
okText: '重新连接',
cancelText: '关闭页面',
onOk() {
window.location.reload();
},
onCancel() {
window.close();
},
});
}
Example #8
Source File: confirm.jsx From virtuoso-design-system with MIT License | 6 votes |
function showConfirm() {
confirm({
title: 'Do you Want to delete these items?',
icon: <ExclamationCircleOutlined />,
content: 'Some descriptions',
onOk() {
console.log('OK');
},
onCancel() {
console.log('Cancel');
},
});
}
Example #9
Source File: Event.js From website with MIT License | 6 votes |
messages = status =>
({
F: {
message: 'Acesso Bloqueado',
icon: <CloseCircleOutlined />
},
P: {
message: 'Acesso Limitado',
icon: <ExclamationCircleOutlined />
},
O: {
message: 'Acesso Liberado',
icon: <CheckOutlined />
}
}[status])
Example #10
Source File: challengesTagSort.js From ctf_platform with MIT License | 6 votes |
handleHint(id, challID, bought) {
if (bought === false) {
confirm({
title: 'Are you sure you want to purchase hint ' + parseInt(id + 1) + ' for "' + this.state.viewingChallengeDetails.name + '"?',
icon: <ExclamationCircleOutlined />,
onOk: (close) => { this.handleBuyHint(close.bind(this), id, challID) },
onCancel() { },
});
}
else {
fetch(window.ipAddress + "/v1/challenge/hint", {
method: 'post',
headers: { 'Content-Type': 'application/json', "Authorization": window.IRSCTFToken },
body: JSON.stringify({
"id": parseInt(id),
"chall": challID,
})
}).then((results) => {
return results.json(); //return data in JSON (since its JSON data)
}).then((data) => {
//console.log(data)
if (data.success === true) {
this.setState({ hintModal: true, hintContent: data.hint })
this.props.obtainScore()
}
}).catch((error) => {
console.log(error)
message.error({ content: "Oops. There was an issue connecting to the server" });
})
}
}
Example #11
Source File: confirm.jsx From virtuoso-design-system with MIT License | 6 votes |
function showPromiseConfirm() {
confirm({
title: 'Do you want to delete these items?',
icon: <ExclamationCircleOutlined />,
content: 'When clicked the OK button, this dialog will be closed after 1 second',
onOk() {
return new Promise((resolve, reject) => {
setTimeout(Math.random() > 0.5 ? resolve : reject, 1000);
}).catch(() => console.log('Oops errors!'));
},
onCancel() {},
});
}
Example #12
Source File: confirm.jsx From virtuoso-design-system with MIT License | 6 votes |
function showDeleteConfirm() {
confirm({
title: 'Are you sure delete this task?',
icon: <ExclamationCircleOutlined />,
content: 'Some descriptions',
okText: 'Yes',
okType: 'danger',
cancelText: 'No',
onOk() {
console.log('OK');
},
onCancel() {
console.log('Cancel');
},
});
}
Example #13
Source File: confirm.jsx From virtuoso-design-system with MIT License | 6 votes |
function showPropsConfirm() {
confirm({
title: 'Are you sure delete this task?',
icon: <ExclamationCircleOutlined />,
content: 'Some descriptions',
okText: 'Yes',
okType: 'danger',
okButtonProps: {
disabled: true,
},
cancelText: 'No',
onOk() {
console.log('OK');
},
onCancel() {
console.log('Cancel');
},
});
}
Example #14
Source File: locale.jsx From virtuoso-design-system with MIT License | 6 votes |
function confirm() {
Modal.confirm({
title: 'Confirm',
icon: <ExclamationCircleOutlined />,
content: 'Bla bla ...',
okText: '确认',
cancelText: '取消',
});
}
Example #15
Source File: LoginLog.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() {
const columns = [{
title: '序号',
dataIndex: 'id',
key: 'id',
render: (id, record, index) => {
return index + 1;
}
}, {
title: '登录账号',
dataIndex: 'username',
key: 'username'
}, {
title: '登录IP',
dataIndex: 'clientIp',
key: 'clientIp'
}, {
title: '登录状态',
dataIndex: 'state',
key: 'state',
render: text => {
if (text === '0') {
return <Tag color="error">失败</Tag>
} else {
return <Tag color="success">成功</Tag>
}
}
}, {
title: '失败原因',
dataIndex: 'reason',
key: 'reason'
}, {
title: '浏览器',
dataIndex: 'clientUserAgent',
key: 'clientUserAgent',
render: (text, record) => {
if (isEmpty(text)) {
return '未知';
}
return (
<Tooltip placement="topLeft" title={text}>
{text.split(' ')[0]}
</Tooltip>
)
}
}, {
title: '登录时间',
dataIndex: 'loginTime',
key: 'loginTime',
render: (text, record) => {
return formatDate(text, 'yyyy-MM-dd hh:mm:ss');
}
}, {
title: '注销时间',
dataIndex: 'logoutTime',
key: 'logoutTime',
render: (text, record) => {
if (isEmpty(text) || text === '0001-01-01 00:00:00') {
return '';
}
return text;
}
},
{
title: '操作',
key: 'action',
render: (text, record) => {
return (
<div>
<Button type="link" size='small' onClick={() => {
confirm({
title: '您确定要删除此条登录日志吗?',
content: '删除用户未注销的登录日志将会强制用户下线',
okText: '确定',
okType: 'danger',
cancelText: '取消',
onOk() {
del(record.id)
}
});
const del = async (id) => {
const result = await request.delete(`/login-logs/${id}`);
if (result.code === 1) {
notification['success']({
message: '提示',
description: '删除成功',
});
this.loadTableData();
} else {
notification['error']({
message: '提示',
description: result.message,
});
}
}
}}>删除</Button>
</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.inputRefOfClientIp}
placeholder="登录账号"
allowClear
onSearch={this.handleSearchByUsername}
/>
<Search
ref={this.inputRefOfClientIp}
placeholder="登录IP"
allowClear
onSearch={this.handleSearchByClientIp}
/>
<Select
style={{width: 100}}
placeholder='用户昵称'
onChange={this.handleChangeByState}
defaultValue={''}
>
<Select.Option value=''>全部状态</Select.Option>
<Select.Option value='1'>只看成功</Select.Option>
<Select.Option value='0'>只看失败</Select.Option>
</Select>
<Tooltip title='重置查询'>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfClientIp.current.setValue('');
this.loadTableData({
pageIndex: 1,
pageSize: 10,
protocol: '',
userId: undefined,
assetId: undefined
})
}}>
</Button>
</Tooltip>
<Divider type="vertical"/>
<Tooltip title="刷新列表">
<Button icon={<SyncOutlined/>} onClick={() => {
this.loadTableData(this.state.queryParams)
}}>
</Button>
</Tooltip>
<Tooltip title="批量删除">
<Button type="dashed" 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/>,
title: content,
content: '删除用户未注销的登录日志将会强制用户下线',
onOk: () => {
this.batchDelete()
},
onCancel() {
},
});
}}>
</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>
</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,
total: this.state.total,
showTotal: total => `总计 ${total} 条`
}}
loading={this.state.loading}
/>
</Content>
</>
);
}
Example #16
Source File: UserGroup.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() {
const columns = [{
title: '序号',
dataIndex: 'id',
key: 'id',
render: (id, record, index) => {
return index + 1;
}
}, {
title: '名称',
dataIndex: 'name',
sorter: true,
render: (name, record, index) => {
return (
<Button type="link" size='small'
loading={this.state.items[index].updateBtnLoading}
onClick={() => this.showModal('更新用户组', record['id'], index)}>{name}</Button>
);
}
}, {
title: '授权资产',
dataIndex: 'assetCount',
key: 'assetCount',
render: (text, record) => {
return <Button type='link' onClick={async () => {
this.setState({
assetVisible: true,
userGroupId: record['id']
})
}}>{text}</Button>
}
}, {
title: '创建日期',
dataIndex: 'created',
key: 'created',
render: (text) => {
return (
<Tooltip title={text}>
{dayjs(text).fromNow()}
</Tooltip>
)
},
sorter: true,
},
{
title: '操作',
key: 'action',
render: (text, record, index) => {
return (
<div>
<Button type="link" size='small'
loading={this.state.items[index].updateBtnLoading}
onClick={() => this.showModal('更新用户组', record['id'], index)}>编辑</Button>
<Button type="link" size='small'
onClick={() => {
this.setState({
assetVisible: true,
userGroupId: record['id']
})
}}>资产授权</Button>
<Button type="link" size='small' danger
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
</div>
)
},
}
];
const selectedRowKeys = this.state.selectedRowKeys;
const rowSelection = {
selectedRowKeys: this.state.selectedRowKeys,
onChange: (selectedRowKeys) => {
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.inputRefOfName}
placeholder="名称"
allowClear
onSearch={this.handleSearchByName}
/>
<Tooltip title='重置查询'>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.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="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}
rowKey='id'
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 ?
<UserGroupModal
visible={this.state.modalVisible}
title={this.state.modalTitle}
handleOk={this.handleOk}
handleCancel={this.handleCancelModal}
confirmLoading={this.state.modalConfirmLoading}
model={this.state.model}
users={this.state.users}
>
</UserGroupModal> : undefined
}
<Drawer
title="资产授权"
placement="right"
closable={true}
destroyOnClose={true}
onClose={() => {
this.loadTableData(this.state.queryParams);
this.setState({
assetVisible: false
})
}}
visible={this.state.assetVisible}
width={window.innerWidth * 0.8}
>
<UserShareSelectedAsset
userGroupId={this.state.userGroupId}
>
</UserShareSelectedAsset>
</Drawer>
</Content>
</>
);
}
Example #17
Source File: User.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() {
const columns = [{
title: '序号',
dataIndex: 'id',
key: 'id',
render: (id, record, index) => {
return index + 1;
}
}, {
title: '登录账号',
dataIndex: 'username',
key: 'username',
sorter: true,
render: (username, record) => {
return (
<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']);
}}>{username}</Button>
);
}
}, {
title: '用户昵称',
dataIndex: 'nickname',
key: 'nickname',
sorter: true,
}, {
title: '用户类型',
dataIndex: 'type',
key: 'type',
render: (text) => {
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: '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) => {
if (text === '1') {
return <Tag icon={<InsuranceOutlined/>} color="success">已开启</Tag>;
} else {
return <Tag icon={<FrownOutlined />} 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) => {
return <Button type='link' onClick={async () => {
this.setState({
assetVisible: true,
sharer: record['id']
})
}}>{text}</Button>
}
}, {
title: '创建日期',
dataIndex: 'created',
key: 'created',
render: (text) => {
return (
<Tooltip title={text}>
{dayjs(text).fromNow()}
</Tooltip>
)
},
sorter: true,
}, {
title: '来源',
dataIndex: 'source',
key: 'source',
render: (text) => {
if (text === 'ldap') {
return (
<Tag color="gold">域同步</Tag>
);
}
}
},
{
title: '操作',
key: 'action',
render: (text, record, index) => {
const menu = (
<Menu>
<Menu.Item key="1">
<Button type="text" size='small'
disabled={record['source'] === 'ldap'}
onClick={() => {
this.setState({
changePasswordVisible: true,
selectedRow: record
})
}}>修改密码</Button>
</Menu.Item>
<Menu.Item key="2">
<Button type="text" size='small'
loading={record['resetTOTPLoading']}
onClick={() => {
confirm({
title: '您确定要重置此用户的双因素认证吗?',
content: record['name'],
okText: '确定',
cancelText: '取消',
onOk: async () => {
this.resetTOTP(record['id'], index);
}
});
}}>重置双因素认证</Button>
</Menu.Item>
<Menu.Item key="3">
<Button type="text" size='small'
onClick={() => {
this.setState({
assetVisible: true,
sharer: record['id']
})
}}>资产授权</Button>
</Menu.Item>
<Menu.Divider/>
<Menu.Item key="5">
<Button type="text" size='small' danger
disabled={getCurrentUser()['id'] === record['id']}
loading={record['delLoading']}
onClick={() => this.showDeleteConfirm(record.id, record.name, index)}>删除</Button>
</Menu.Item>
</Menu>
);
return (
<div>
<Button type="link" size='small'
disabled={getCurrentUser()['id'] === record['id']}
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) => {
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="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}
rowKey='id'
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
}
<Drawer
title="资产授权"
placement="right"
closable={true}
destroyOnClose={true}
onClose={() => {
this.loadTableData(this.state.queryParams);
this.setState({
assetVisible: false
})
}}
visible={this.state.assetVisible}
width={window.innerWidth * 0.8}
>
<UserShareSelectedAsset
sharer={this.state.sharer}
userGroupId={undefined}
>
</UserShareSelectedAsset>
</Drawer>
{
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);
});
}}
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>
</>
);
}
Example #18
Source File: Strategy.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() {
const renderStatus = (text) => {
if (text === '1') {
return <Tag color={'green'}>开启</Tag>
} else {
return <Tag color={'red'}>关闭</Tag>
}
}
const columns = [{
title: '序号',
dataIndex: 'id',
key: 'id',
render: (id, record, index) => {
return index + 1;
}
}, {
title: '名称',
dataIndex: 'name',
key: 'name',
sorter: true,
}, {
title: '上传',
dataIndex: 'upload',
key: 'upload',
render: (text) => {
return renderStatus(text);
}
}, {
title: '下载',
dataIndex: 'download',
key: 'download',
render: (text) => {
return renderStatus(text);
}
}, {
title: '编辑',
dataIndex: 'edit',
key: 'edit',
render: (text) => {
return renderStatus(text);
}
}, {
title: '删除',
dataIndex: 'delete',
key: 'delete',
render: (text) => {
return renderStatus(text);
}
}, {
title: '重命名',
dataIndex: 'rename',
key: 'rename',
render: (text) => {
return renderStatus(text);
}
}, {
title: '复制',
dataIndex: 'copy',
key: 'copy',
render: (text) => {
return renderStatus(text);
}
}, {
title: '粘贴',
dataIndex: 'paste',
key: 'paste',
render: (text) => {
return renderStatus(text);
}
}, {
title: '创建时间',
dataIndex: 'created',
key: 'created',
}, {
title: '操作',
key: 'action',
render: (text, record, index) => {
return (
<div>
<Button type="link" size='small' loading={this.state.items[index]['execLoading']}
onClick={() => this.showModal('更新授权策略', record)}>编辑</Button>
<Button type="text" size='small' danger
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
</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={12} key={1}>
<Title level={3}>授权策略</Title>
</Col>
<Col span={12} key={2} style={{textAlign: 'right'}}>
<Space>
<Search
ref={this.inputRefOfName}
placeholder="名称"
allowClear
onSearch={this.handleSearchByName}
/>
<Tooltip title='重置查询'>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.current.setValue('');
this.loadTableData({pageIndex: 1, pageSize: 10, name: '', content: ''})
}}>
</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="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}
/>
{
this.state.modalVisible ?
<StrategyModal
visible={this.state.modalVisible}
title={this.state.modalTitle}
handleOk={this.handleOk}
handleCancel={this.handleCancelModal}
confirmLoading={this.state.modalConfirmLoading}
model={this.state.model}
>
</StrategyModal> : undefined
}
</Content>
</>
);
}
Example #19
Source File: Info.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() {
let contentClassName = isAdmin() ? 'page-content' : 'page-content-user';
return (
<>
<Content className={["site-layout-background", contentClassName]}>
<Row>
<Col span={12}>
<Title level={3}>修改密码</Title>
<Form ref={this.passwordFormRef} name="password" onFinish={this.changePassword}>
<input type='password' hidden={true} autoComplete='new-password'/>
<Form.Item
{...formItemLayout}
name="oldPassword"
label="原始密码"
rules={[
{
required: true,
message: '原始密码',
},
]}
>
<Input type='password' placeholder="请输入原始密码" style={{width: 240}}/>
</Form.Item>
<Form.Item
{...formItemLayout}
name="newPassword"
label="新的密码"
rules={[
{
required: true,
message: '请输入新的密码',
},
]}
>
<Input type='password' placeholder="新的密码"
onChange={(value) => this.onNewPasswordChange(value)} style={{width: 240}}/>
</Form.Item>
<Form.Item
{...formItemLayout}
name="newPassword2"
label="确认密码"
rules={[
{
required: true,
message: '请和上面输入新的密码保持一致',
},
]}
validateStatus={this.state.validateStatus}
help={this.state.errorMsg || ' '}
>
<Input type='password' placeholder="请和上面输入新的密码保持一致"
onChange={(value) => this.onNewPassword2Change(value)} style={{width: 240}}/>
</Form.Item>
<Form.Item {...formTailLayout}>
<Button disabled={this.state.errorMsg || !this.state.validateStatus} type="primary" htmlType="submit">
提交
</Button>
</Form.Item>
</Form>
<Divider/>
</Col>
<Col span={12}>
<Title level={3}>授权信息</Title>
<Descriptions column={1}>
<Descriptions.Item label="授权令牌">
<Text strong copyable>{this.state.accessToken.token}</Text>
</Descriptions.Item>
<Descriptions.Item label="生成时间">
<Text strong>{this.state.accessToken.created}</Text>
</Descriptions.Item>
</Descriptions>
<Space>
<Button type="primary" onClick={this.genAccessToken}>
重新生成
</Button>
</Space>
</Col>
</Row>
<Title level={3}>双因素认证</Title>
<Form hidden={this.state.qr}>
<Form.Item {...formItemLayout}>
{
this.state.user.enableTotp ?
<Result
status="success"
title="您已成功开启双因素认证!"
subTitle="多因素认证-MFA二次认证-登录身份鉴别,访问控制更安全。"
extra={[
<Button type="primary" key="console" danger onClick={() => {
confirm({
title: '您确认要解除双因素认证吗?',
icon: <ExclamationCircleOutlined/>,
content: '解除之后可能存在系统账号被暴力破解的风险。',
okText: '确认',
okType: 'danger',
cancelText: '取消',
onOk: async () => {
let result = await request.post('/account/reset-totp');
if (result.code === 1) {
message.success('双因素认证解除成功');
await this.loadInfo();
} else {
message.error(result.message);
}
},
onCancel() {
console.log('Cancel');
},
})
}}>
解除绑定
</Button>,
<Button key="re-bind" onClick={this.resetTOTP}>重新绑定</Button>,
]}
/> :
<Result
status="warning"
title="您还未开启双因素认证!"
subTitle="系统账号存在被暴力破解的风险。"
extra={
<Button type="primary" key="bind" onClick={this.resetTOTP}>
去开启
</Button>
}
/>
}
</Form.Item>
</Form>
<Form hidden={!this.state.qr} onFinish={this.confirmTOTP}>
<Form.Item {...formItemLayout} label="二维码">
<Space size={12}>
<Card
hoverable
style={{width: 280}}
cover={<Image
style={{padding: 20}}
width={280}
src={"data:image/png;base64, " + this.state.qr}
/>
}
>
<Meta title="双因素认证二维码"
description="有效期30秒,在扫描后请尽快输入。推荐使用Google Authenticator, Authy 或者 Microsoft Authenticator。"/>
</Card>
<Button
type="primary"
icon={<ReloadOutlined/>}
onClick={this.resetTOTP}
>
重新加载
</Button>
</Space>
</Form.Item>
<Form.Item
{...formItemLayout}
name="totp"
label="TOTP"
rules={[
{
required: true,
message: '请输入双因素认证APP中显示的授权码',
},
]}
>
<Input placeholder="请输入双因素认证APP中显示的授权码"/>
</Form.Item>
<Form.Item {...formTailLayout}>
<Button type="primary" htmlType="submit">
确认
</Button>
</Form.Item>
</Form>
</Content>
</>
);
}
Example #20
Source File: OnlineSession.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() { const columns = [{ title: '序号', dataIndex: 'id', key: 'id', render: (id, record, index) => { return index + 1; } }, { title: '来源IP', dataIndex: 'clientIp', key: 'clientIp' }, { title: '接入方式', dataIndex: 'mode', key: 'mode', render: (text) => { return ( <Tag color={MODE_COLORS[text]}>{text}</Tag> ) } }, { title: '用户昵称', dataIndex: 'creatorName', key: 'creatorName' }, { title: '资产名称', dataIndex: 'assetName', key: 'assetName' }, { title: '连接协议', dataIndex: 'protocol', key: 'protocol', render: (text, record) => { const title = `${record.username}@${record.ip}:${record.port}`; return ( <Tooltip title={title}> <Tag color={PROTOCOL_COLORS[text]}>{text}</Tag> </Tooltip> ) } }, { title: '接入时间', dataIndex: 'connectedTime', key: 'connectedTime', render: (text, record) => { return ( <Tooltip title={text}> {dayjs(text).fromNow()} </Tooltip> ) } }, { title: '接入时长', dataIndex: 'connectedTime', key: 'connectedTime', render: (text, record) => { return differTime(new Date(record['connectedTime']), new Date()); } }, { title: '操作', key: 'action', render: (text, record) => { return ( <div> <Button type="link" size='small' onClick={() => { this.showMonitor(record) }}>监控</Button> <Button type="link" size='small' onClick={async () => { confirm({ title: '您确定要断开此会话吗?', content: '', okText: '确定', okType: 'danger', cancelText: '取消', onOk() { dis(record.id) } }); const dis = async (id) => { const result = await request.post(`/sessions/${id}/disconnect`); if (result.code === 1) { notification['success']({ message: '提示', description: '断开成功', }); this.loadTableData(); } else { notification['success']({ message: '提示', description: result.message, }); } } }}>断开</Button> </div> ) }, } ]; const selectedRowKeys = this.state.selectedRowKeys; const rowSelection = { selectedRowKeys: selectedRowKeys, onChange: (selectedRowKeys, selectedRows) => { this.setState({selectedRowKeys}); }, }; const hasSelected = selectedRowKeys.length > 0; const userOptions = this.state.users.map(d => <Select.Option key={d.id} value={d.id}>{d.nickname}</Select.Option>); const assetOptions = this.state.assets.map(d => <Select.Option key={d.id} value={d.id}>{d.name}</Select.Option>); 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.inputRefOfClientIp} placeholder="来源IP" allowClear onSearch={this.handleSearchByClientIp} /> <Select style={{width: 150}} showSearch value={this.state.queryParams.userId} placeholder='用户昵称' onSearch={this.handleSearchByNickname} onChange={this.handleChangeByUserId} filterOption={false} allowClear > {userOptions} </Select> <Select style={{width: 150}} showSearch value={this.state.queryParams.assetId} placeholder='资产名称' onSearch={this.handleSearchByAssetName} onChange={this.handleChangeByAssetId} filterOption={false} > {assetOptions} </Select> <Select onChange={this.handleChangeByProtocol} 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.Option value="kubernetes">kubernetes</Select.Option> </Select> <Tooltip title='重置查询'> <Button icon={<UndoOutlined/>} onClick={() => { this.inputRefOfClientIp.current.setValue(''); this.loadTableData({ pageIndex: 1, pageSize: 10, protocol: '', userId: undefined, assetId: undefined }) }}> </Button> </Tooltip> <Divider type="vertical"/> <Tooltip title="刷新列表"> <Button icon={<SyncOutlined/>} onClick={() => { this.loadTableData(this.state.queryParams) }}> </Button> </Tooltip> <Tooltip title="批量断开"> <Button type="primary" danger disabled={!hasSelected} icon={<DisconnectOutlined/>} 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.batchDis() }, 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, total: this.state.total, showTotal: total => `总计 ${total} 条` }} loading={this.state.loading} /> { this.state.accessVisible ? <Modal className='modal-no-padding' title={this.state.sessionTitle} maskClosable={false} visible={this.state.accessVisible} footer={null} width={window.innerWidth * 0.8} height={window.innerWidth * 0.8 / this.state.sessionWidth * this.state.sessionHeight} onCancel={() => { message.destroy(); this.setState({accessVisible: false}) }} > { this.state.sessionMode === 'guacd' ? <AccessMonitor sessionId={this.state.sessionId} width={this.state.sessionWidth} height={this.state.sessionHeight} protocol={this.state.sessionProtocol} rate={window.innerWidth * 0.8 / this.state.sessionWidth}> </AccessMonitor> : <TermMonitor sessionId={this.state.sessionId} width={this.state.sessionWidth} height={this.state.sessionHeight}> </TermMonitor> } </Modal> : undefined } </Content> </> ); }
Example #21
Source File: OfflineSession.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() { const columns = [{ title: '序号', dataIndex: 'id', key: 'id', render: (id, record, index) => { return index + 1; }, }, { title: '来源IP', dataIndex: 'clientIp', key: 'clientIp' }, { title: '接入方式', dataIndex: 'mode', key: 'mode', render: (text) => { return ( <Tag color={MODE_COLORS[text]}>{text}</Tag> ) } }, { title: '用户昵称', dataIndex: 'creatorName', key: 'creatorName' }, { title: '资产名称', dataIndex: 'assetName', key: 'assetName' }, { title: '连接协议', dataIndex: 'protocol', key: 'protocol', render: (text, record) => { const title = `${record.username}@${record.ip}:${record.port}`; return ( <Tooltip title={title}> <Tag color={PROTOCOL_COLORS[text]}>{text}</Tag> </Tooltip> ) } }, { title: '接入时间', dataIndex: 'connectedTime', key: 'connectedTime', render: (text, record) => { return ( <Tooltip title={text}> {dayjs(text).fromNow()} </Tooltip> ) } }, { title: '接入时长', dataIndex: 'connectedTime', key: 'connectedTime', render: (text, record) => { return differTime(new Date(record['connectedTime']), new Date(record['disconnectedTime'])); } }, { title: '操作', key: 'action', render: (text, record) => { let disabled = true; if (record['recording'] && record['recording'] === '1') { disabled = false } return ( <div> <Button type="link" size='small' disabled={disabled} onClick={() => this.showPlayback(record)}>回放</Button> <Button type="link" size='small' onClick={() => { confirm({ title: '您确定要禁止该IP访问本系统吗?', content: '', okText: '确定', okType: 'danger', cancelText: '取消', onOk: async () => { // 向后台提交数据 let formData = { ip: record['clientIp'], rule: 'reject', priority: 99, } const result = await request.post('/securities', formData); if (result.code === 1) { message.success('禁用成功'); } else { message.error(result.message, 10); } } }); }}>禁用IP</Button> <Button type="link" size='small' danger onClick={() => { confirm({ title: '您确定要删除此会话吗?', content: '', okText: '确定', okType: 'danger', cancelText: '取消', onOk: () => { this.del(record.id) } }); }}>删除</Button> </div> ) }, } ]; const selectedRowKeys = this.state.selectedRowKeys; const rowSelection = { selectedRowKeys: this.state.selectedRowKeys, onChange: (selectedRowKeys, selectedRows) => { this.setState({selectedRowKeys}); }, }; const hasSelected = selectedRowKeys.length > 0; const userOptions = this.state.users.map(d => <Select.Option key={d.id} value={d.id}>{d.nickname}</Select.Option>); const assetOptions = this.state.assets.map(d => <Select.Option key={d.id} value={d.id}>{d.name}</Select.Option>); return ( <> <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.inputRefOfClientIp} placeholder="来源IP" allowClear onSearch={this.handleSearchByClientIp} /> <Select style={{width: 140}} showSearch value={this.state.queryParams.userId} placeholder='用户昵称' onSearch={this.handleSearchByNickname} onChange={this.handleChangeByUserId} filterOption={false} allowClear > {userOptions} </Select> <Select style={{width: 140}} showSearch value={this.state.queryParams.assetId} placeholder='资产名称' onSearch={this.handleSearchByAssetName} onChange={this.handleChangeByAssetId} filterOption={false} > {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}}> <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.Option value="kubernetes">kubernetes</Select.Option> </Select> <Tooltip title='重置查询'> <Button icon={<UndoOutlined/>} onClick={() => { this.inputRefOfClientIp.current.setValue(''); this.loadTableData({ pageIndex: 1, pageSize: 10, protocol: '' }) }}> </Button> </Tooltip> <Divider type="vertical"/> <Tooltip title="刷新列表"> <Button icon={<SyncOutlined/>} onClick={() => { this.loadTableData(this.state.queryParams) }}> </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} 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> <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() { }, }); }}> </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, total: this.state.total, showTotal: total => `总计 ${total} 条` }} loading={this.state.loading} rowClassName={(record, index) => { return record['reviewed'] ? '' : 'unreviewed'; }} /> { this.state.playbackVisible ? <Modal className='modal-no-padding' title={`会话回放 来源IP:${this.state.selectedRow['clientIp']} 用户昵称:${this.state.selectedRow['creatorName']} 资产名称:${this.state.selectedRow['assetName']} 网络:${this.state.selectedRow['username']}@${this.state.selectedRow['ip']}:${this.state.selectedRow['port']}`} centered={true} visible={this.state.playbackVisible} onCancel={this.hidePlayback} width={window.innerWidth * 0.8} footer={null} destroyOnClose maskClosable={false} > { this.state.selectedRow['mode'] === 'native' || this.state.selectedRow['mode'] === 'terminal' ? <iframe title='recording' style={{ width: '100%', // height: this.state.iFrameHeight, overflow: 'visible' }} onLoad={() => { // constant obj = ReactDOM.findDOMNode(this); // this.setState({ // "iFrameHeight": obj.contentWindow.document.body.scrollHeight + 'px' // }); }} ref="iframe" src={'./asciinema.html?sessionId=' + this.state.selectedRow['id']} width="100%" height={window.innerHeight * 0.8} frameBorder="0" /> : <Playback sessionId={this.state.selectedRow['id']}/> } </Modal> : undefined } </Content> </> ); }
Example #22
Source File: Security.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() {
const columns = [{
title: '序号',
dataIndex: 'id',
key: 'id',
render: (id, record, index) => {
return index + 1;
}
}, {
title: 'IP',
dataIndex: 'ip',
key: 'ip',
sorter: true,
}, {
title: '规则',
dataIndex: 'rule',
key: 'rule',
render: (rule) => {
if (rule === 'allow') {
return <Tag color={'green'}>允许</Tag>
} else {
return <Tag color={'red'}>禁止</Tag>
}
}
}, {
title: '优先级',
dataIndex: 'priority',
key: 'priority',
sorter: true,
}, {
title: '来源',
dataIndex: 'source',
key: 'source',
}, {
title: '操作',
key: 'action',
render: (text, record, index) => {
return (
<div>
<Button type="link" size='small' loading={this.state.items[index]['execLoading']}
onClick={() => this.showModal('更新', record)}>编辑</Button>
<Button type="text" size='small' danger
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
</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={12} key={1}>
<Title level={3}>IP访问规则列表</Title>
</Col>
<Col span={12} key={2} style={{textAlign: 'right'}}>
<Space>
<Search
ref={this.inputRefOfName}
placeholder="IP"
allowClear
onSearch={this.handleSearchByName}
/>
<Tooltip title='重置查询'>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.current.setValue('');
this.loadTableData({pageIndex: 1, pageSize: 10, name: '', content: ''})
}}>
</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="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}
/>
{
this.state.modalVisible ?
<SecurityModal
visible={this.state.modalVisible}
title={this.state.modalTitle}
handleOk={this.handleOk}
handleCancel={this.handleCancelModal}
confirmLoading={this.state.modalConfirmLoading}
model={this.state.model}
>
</SecurityModal> : undefined
}
</Content>
</>
);
}
Example #23
Source File: admin.js From ctf_platform with MIT License | 4 votes |
render() {
return (
<Layout className="layout-style">
<Tabs activeKey={this.state.key} onTabClick={async (key) => {
await this.props.history.push("/Admin/" + key)
if (this.props.location.pathname === "/Admin/" + key) this.setState({ key: key })
}} style={{ overflowY: "auto", overflowX: "auto" }}>
<TabPane
tab={<span> Home </span>}
key=""
>
<h1>Welcome to Sieberrsec CTF Platform's admin panel.</h1>
<h1>Click on any of the tabs above to manage different parts of the portal.</h1>
<div className="settings-responsive2" style={{ display: "flex", justifyContent: "space-around" }}>
<Card className="settings-card">
<Button type='primary' onClick={this.downloadBackup} loading={this.state.backupLoading}><DownloadOutlined /> Download Backup</Button>
<p>Download all the data stored in the platform's database. Data can be uploaded onto a different platform/a later date to restore all the data</p>
</Card>
<Divider type="vertical" style={{ height: "inherit" }} />
<Card className="settings-card">
<div style={{width: "50%"}}>
<Dragger
fileList={this.state.fileList}
disabled={this.state.loadingUpload}
accept=".json"
maxCount={1}
onChange={(file) => {
if (file.fileList.length > 0) this.setState({ noFile: false })
else this.setState({ noFile: true })
this.setState({ fileList: file.fileList })
}}
beforeUpload={(file) => {
return false
}}>
<h4>Drag and drop backup .json file to upload.</h4><br/>
<p>Then, click the Upload button</p>
</Dragger>
<Button type="primary" icon={<UploadOutlined />} style={{ marginTop: "3ch" }} disabled={this.state.noFile} loading={this.state.loadingUpload} onClick={() => {
confirm({
confirmLoading: this.state.loadingUpload,
title: 'Are you sure you want to restore all platform data to this JSON file? This action is irreversible.',
icon: <ExclamationCircleOutlined />,
onOk: (close) => { this.uploadBackup(close) },
onCancel: () => { },
});
this.uploadBackup
}}>Upload Backup</Button>
</div>
<p>Restore and upload data stored in a backup json file. <span style={{ color: "#d32029" }}><b>Warning: This <u>WILL OVERRIDE ALL EXISTING DATA</u> stored in this platform</b> (including the current account used to upload the backup). Please re-login after restoration is completed.</span></p>
</Card>
</div>
</TabPane>
<TabPane
tab={<span><NotificationOutlined />Announcements</span>}
key="Announcements"
>
<AdminManageAnnouncements />
</TabPane>
<TabPane
tab={<span><UserOutlined />Users</span>}
key="Users"
>
<AdminUsers></AdminUsers>
</TabPane>
<TabPane
tab={<span><AppstoreOutlined />Challenges</span>}
key="Challenges"
>
<AdminChallenges history={this.props.history} location={this.props.location}></AdminChallenges>
</TabPane>
<TabPane
tab={<span><BarsOutlined />Transactions</span>}
key="Submissions"
>
<AdminSubmissions></AdminSubmissions>
</TabPane>
<TabPane
tab={<span><MailOutlined />Email Settings</span>}
key="Emails"
>
<AdminEmails></AdminEmails>
</TabPane>
</Tabs>
</Layout>
);
}
Example #24
Source File: Job.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
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: 'status',
key: 'status',
render: (status, record) => {
return <Switch checkedChildren="开启" unCheckedChildren="关闭" checked={status === 'running'}
onChange={async (checked) => {
let jobStatus = checked ? 'running' : 'not-running';
let result = await request.post(`/jobs/${record['id']}/change-status?status=${jobStatus}`);
if (result['code'] === 1) {
message.success('操作成功');
await this.loadTableData();
} else {
message.error(result['message']);
}
}}
/>
}
}, {
title: '任务类型',
dataIndex: 'func',
key: 'func',
render: (func, record) => {
switch (func) {
case "check-asset-status-job":
return <Tag color="green">资产状态检测</Tag>;
case "shell-job":
return <Tag color="volcano">Shell脚本</Tag>;
default:
return '';
}
}
}, {
title: 'cron表达式',
dataIndex: 'cron',
key: 'cron'
}, {
title: '创建日期',
dataIndex: 'created',
key: 'created',
render: (text, record) => {
return (
<Tooltip title={text}>
{dayjs(text).fromNow()}
</Tooltip>
)
},
sorter: true,
}, {
title: '最后执行日期',
dataIndex: 'updated',
key: 'updated',
render: (text, record) => {
if (text === '0001-01-01 00:00:00') {
return '';
}
return (
<Tooltip title={text}>
{dayjs(text).fromNow()}
</Tooltip>
)
},
sorter: true,
}, {
title: '操作',
key: 'action',
render: (text, record, index) => {
const menu = (
<Menu>
<Menu.Item key="0">
<Button type="text" size='small'
onClick={() => this.showModal('更新计划任务', record)}>编辑</Button>
</Menu.Item>
<Menu.Item key="2">
<Button type="text" size='small'
onClick={async () => {
this.setState({
logVisible: true,
logPending: true
})
let result = await request.get(`/jobs/${record['id']}/logs`);
if (result['code'] === 1) {
this.setState({
logPending: false,
logs: result['data'],
selectedRow: record
})
}
}}>日志</Button>
</Menu.Item>
<Menu.Divider/>
<Menu.Item key="3">
<Button type="text" size='small' danger
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
</Menu.Item>
</Menu>
);
return (
<div>
<Button type="link" size='small' loading={this.state.items[index]['execLoading']}
onClick={async () => {
let items = this.state.items;
items[index]['execLoading'] = true;
this.setState({
items: items
});
let result = await request.post(`/jobs/${record['id']}/exec`);
if (result['code'] === 1) {
message.success('执行成功');
await this.loadTableData();
} else {
message.error(result['message']);
items[index]['execLoading'] = false;
this.setState({
items: items
});
}
}}>执行</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={12} key={1}>
<Title level={3}>任务列表</Title>
</Col>
<Col span={12} key={2} style={{textAlign: 'right'}}>
<Space>
<Search
ref={this.inputRefOfName}
placeholder="任务名称"
allowClear
onSearch={this.handleSearchByName}
/>
<Tooltip title='重置查询'>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.current.setValue('');
this.loadTableData({pageIndex: 1, pageSize: 10, name: '', content: ''})
}}>
</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="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}
/>
{
this.state.modalVisible ?
<JobModal
visible={this.state.modalVisible}
title={this.state.modalTitle}
handleOk={this.handleOk}
handleCancel={this.handleCancelModal}
confirmLoading={this.state.modalConfirmLoading}
model={this.state.model}
>
</JobModal> : undefined
}
{
this.state.logVisible ?
<Modal
className='modal-no-padding'
width={window.innerWidth * 0.8}
title={'日志'}
visible={true}
maskClosable={false}
onOk={async () => {
let result = await request.delete(`/jobs/${this.state.selectedRow['id']}/logs`);
if (result['code'] === 1) {
this.setState({
logVisible: false,
selectedRow: undefined
})
message.success('日志清空成功');
} else {
message.error(result['message'], 10);
}
}}
onCancel={() => {
this.setState({
logVisible: false,
selectedRow: undefined
})
}}
okText='清空'
okType={'danger'}
cancelText='取消'
>
<Spin tip='加载中...' spinning={this.state.logPending}>
<pre className='cron-log'>
{
this.state.logs.map(item => {
return <><Divider
orientation="left"
style={{color: 'white'}}>{item['timestamp']}</Divider>{item['message']}</>;
})
}
</pre>
</Spin>
</Modal> : undefined
}
</Content>
</>
);
}
Example #25
Source File: FileSystem.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() {
const columns = [
{
title: '名称',
dataIndex: 'name',
key: 'name',
render: (value, item) => {
let icon;
if (item['isDir']) {
icon = <FolderTwoTone/>;
} else {
if (item['isLink']) {
icon = <LinkOutlined/>;
} else {
const fileExtension = item['name'].split('.').pop().toLowerCase();
switch (fileExtension) {
case "doc":
case "docx":
icon = <FileWordOutlined/>;
break;
case "xls":
case "xlsx":
icon = <FileExcelOutlined/>;
break;
case "bmp":
case "jpg":
case "jpeg":
case "png":
case "tif":
case "gif":
case "pcx":
case "tga":
case "exif":
case "svg":
case "psd":
case "ai":
case "webp":
icon = <FileImageOutlined/>;
break;
case "md":
icon = <FileMarkdownOutlined/>;
break;
case "pdf":
icon = <FilePdfOutlined/>;
break;
case "txt":
icon = <FileTextOutlined/>;
break;
case "zip":
case "gz":
case "tar":
case "tgz":
icon = <FileZipOutlined/>;
break;
default:
icon = <FileOutlined/>;
break;
}
}
}
return <span className={'dode'}>{icon} {item['name']}</span>;
},
sorter: (a, b) => {
if (a['key'] === '..') {
return 0;
}
if (b['key'] === '..') {
return 0;
}
return a.name.localeCompare(b.name);
},
sortDirections: ['descend', 'ascend'],
},
{
title: '大小',
dataIndex: 'size',
key: 'size',
render: (value, item) => {
if (!item['isDir'] && !item['isLink']) {
return <span className={'dode'}>{renderSize(value)}</span>;
}
return <span className={'dode'}/>;
},
sorter: (a, b) => {
if (a['key'] === '..') {
return 0;
}
if (b['key'] === '..') {
return 0;
}
return a.size - b.size;
},
}, {
title: '修改日期',
dataIndex: 'modTime',
key: 'modTime',
sorter: (a, b) => {
if (a['key'] === '..') {
return 0;
}
if (b['key'] === '..') {
return 0;
}
return a.modTime.localeCompare(b.modTime);
},
sortDirections: ['descend', 'ascend'],
render: (value, item) => {
return <span className={'dode'}>{value}</span>;
},
}, {
title: '属性',
dataIndex: 'mode',
key: 'mode',
render: (value, item) => {
return <span className={'dode'}>{value}</span>;
},
}, {
title: '操作',
dataIndex: 'action',
key: 'action',
width: 210,
render: (value, item) => {
if (item['key'] === '..') {
return undefined;
}
let disableDownload = !this.state.download;
let disableEdit = !this.state.edit;
if (item['isDir'] || item['isLink']) {
disableDownload = true;
disableEdit = true
}
return (
<>
<Button type="link" size='small' disabled={disableEdit}
onClick={() => this.showEditor(item['name'], item['key'])}>
编辑
</Button>
<Button type="link" size='small' disabled={disableDownload} onClick={async () => {
download(`${server}/${this.state.storageType}/${this.state.storageId}/download?file=${window.encodeURIComponent(item['key'])}&X-Auth-Token=${getToken()}&t=${new Date().getTime()}`);
}}>
下载
</Button>
<Button type={'link'} size={'small'} disabled={!this.state.rename} onClick={() => {
this.setState({
renameVisible: true,
currentFileKey: item['key']
})
}}>重命名</Button>
<Popconfirm
title="您确认要删除此文件吗?"
onConfirm={async () => {
await this.delete(item['key']);
await this.refresh();
}}
okText="是"
cancelText="否"
>
<Button type={'link'} size={'small'} disabled={!this.state.delete} danger>删除</Button>
</Popconfirm>
</>
);
},
}
];
const {selectedRowKeys} = this.state;
const rowSelection = {
selectedRowKeys,
onChange: (selectedRowKeys) => {
this.setState({selectedRowKeys});
},
getCheckboxProps: (record) => ({
disabled: record['disabled'],
}),
};
let hasSelected = selectedRowKeys.length > 0;
if (hasSelected) {
if (!this.state.delete) {
hasSelected = false;
}
}
const title = (
<div className='fs-header'>
<div className='fs-header-left'>
<Input value={this.state.currentDirectoryInput} onChange={this.handleCurrentDirectoryInputChange}
onPressEnter={this.handleCurrentDirectoryInputPressEnter}/>
</div>
<div className='fs-header-right'>
<Space>
<div className='fs-header-right-item'>
<Tooltip title="创建文件夹">
<Button type="primary" size="small"
disabled={!this.state.upload}
icon={<FolderAddOutlined/>}
onClick={() => {
this.setState({
mkdirVisible: true
})
}} ghost/>
</Tooltip>
</div>
<div className='fs-header-right-item'>
<Tooltip title="上传文件">
<Button type="primary" size="small"
icon={<CloudUploadOutlined/>}
disabled={!this.state.upload}
onClick={() => {
window.document.getElementById('file-upload').click();
}} ghost/>
<input type="file" id="file-upload" style={{display: 'none'}}
onChange={this.handleUploadFile} multiple/>
</Tooltip>
</div>
<div className='fs-header-right-item'>
<Tooltip title="上传文件夹">
<Button type="primary" size="small"
icon={<UploadOutlined/>}
disabled={!this.state.upload}
onClick={() => {
window.document.getElementById('dir-upload').click();
}} ghost/>
<input type="file" id="dir-upload" style={{display: 'none'}}
onChange={this.handleUploadDir} webkitdirectory='' multiple/>
</Tooltip>
</div>
<div className='fs-header-right-item'>
<Tooltip title="刷新">
<Button type="primary" size="small"
icon={<ReloadOutlined/>}
onClick={this.refresh}
ghost/>
</Tooltip>
</div>
<div className='fs-header-right-item'>
<Tooltip title="批量删除">
<Button type="primary" size="small" ghost danger disabled={!hasSelected}
icon={<DeleteOutlined/>}
loading={this.state.delBtnLoading}
onClick={() => {
let rowKeys = this.state.selectedRowKeys;
const content = <div>
您确定要删除选中的<Text style={{color: '#1890FF'}}
strong>{rowKeys.length}</Text>条记录吗?
</div>;
confirm({
icon: <ExclamationCircleOutlined/>,
content: content,
onOk: async () => {
for (let i = 0; i < rowKeys.length; i++) {
if (rowKeys[i] === '..') {
continue;
}
await this.delete(rowKeys[i]);
}
this.refresh();
},
onCancel() {
},
});
}}>
</Button>
</Tooltip>
</div>
</Space>
</div>
</div>
);
return (
<div>
<Card title={title} bordered={true} size="small" style={{minHeight: this.state.minHeight}}>
<Table columns={columns}
rowSelection={rowSelection}
dataSource={this.state.files}
size={'small'}
pagination={false}
loading={this.state.loading}
onRow={record => {
return {
onDoubleClick: event => {
if (record['isDir'] || record['isLink']) {
if (record['path'] === '..') {
// 获取当前目录的上级目录
let currentDirectory = this.state.currentDirectory;
let parentDirectory = currentDirectory.substring(0, currentDirectory.lastIndexOf('/'));
this.loadFiles(parentDirectory);
} else {
this.loadFiles(record['path']);
}
} else {
}
},
};
}}
/>
</Card>
{
this.state.mkdirVisible ?
<Modal
title="创建文件夹"
visible={this.state.mkdirVisible}
okButtonProps={{form: 'mkdir-form', key: 'submit', htmlType: 'submit'}}
onOk={() => {
this.mkdirFormRef.current
.validateFields()
.then(async values => {
this.mkdirFormRef.current.resetFields();
let params = {
'dir': this.state.currentDirectory + '/' + values['dir']
}
let paramStr = qs.stringify(params);
this.setState({
confirmLoading: true
})
let result = await request.post(`/${this.state.storageType}/${this.state.storageId}/mkdir?${paramStr}`);
if (result.code === 1) {
message.success('创建成功');
this.loadFiles(this.state.currentDirectory);
} else {
message.error(result.message);
}
this.setState({
confirmLoading: false,
mkdirVisible: false
})
})
.catch(info => {
});
}}
confirmLoading={this.state.confirmLoading}
onCancel={() => {
this.setState({
mkdirVisible: false
})
}}
>
<Form ref={this.mkdirFormRef} id={'mkdir-form'}>
<Form.Item name='dir' rules={[{required: true, message: '请输入文件夹名称'}]}>
<Input autoComplete="off" placeholder="请输入文件夹名称"/>
</Form.Item>
</Form>
</Modal> : undefined
}
{
this.state.renameVisible ?
<Modal
title="重命名"
visible={this.state.renameVisible}
okButtonProps={{form: 'rename-form', key: 'submit', htmlType: 'submit'}}
onOk={() => {
this.renameFormRef.current
.validateFields()
.then(async values => {
this.renameFormRef.current.resetFields();
try {
let currentDirectory = this.state.currentDirectory;
if (!currentDirectory.endsWith("/")) {
currentDirectory += '/';
}
let params = {
'oldName': this.state.currentFileKey,
'newName': currentDirectory + values['newName'],
}
if (params['oldName'] === params['newName']) {
message.success('重命名成功');
return;
}
let paramStr = qs.stringify(params);
this.setState({
confirmLoading: true
})
let result = await request.post(`/${this.state.storageType}/${this.state.storageId}/rename?${paramStr}`);
if (result['code'] === 1) {
message.success('重命名成功');
this.refresh();
} else {
message.error(result.message);
}
} finally {
this.setState({
confirmLoading: false,
renameVisible: false
})
}
})
.catch(info => {
});
}}
confirmLoading={this.state.confirmLoading}
onCancel={() => {
this.setState({
renameVisible: false
})
}}
>
<Form id={'rename-form'}
ref={this.renameFormRef}
initialValues={{newName: getFileName(this.state.currentFileKey)}}>
<Form.Item name='newName' rules={[{required: true, message: '请输入新的名称'}]}>
<Input autoComplete="off" placeholder="新的名称"/>
</Form.Item>
</Form>
</Modal> : undefined
}
<Modal
title={"编辑 " + this.state.fileName}
className='modal-no-padding'
visible={this.state.editorVisible}
destroyOnClose={true}
width={window.innerWidth * 0.8}
centered={true}
okButtonProps={{form: 'rename-form', key: 'submit', htmlType: 'submit'}}
onOk={this.edit}
confirmLoading={this.state.confirmLoading}
onCancel={this.hideEditor}
>
<MonacoEditor
language="javascript"
height={window.innerHeight * 0.8}
theme="vs-dark"
value={this.state.fileContent}
options={{
selectOnLineNumbers: true
}}
editorDidMount={(editor, monaco) => {
console.log('editorDidMount', editor);
editor.focus();
}}
onChange={(newValue, e) => {
console.log('onChange', newValue, e);
this.setState(
{
fileContent: newValue
}
)
}}
/>
</Modal>
</div>
);
}
Example #26
Source File: Credential.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() {
const columns = [{
title: '序号',
dataIndex: 'id',
key: 'id',
render: (id, record, index) => {
return index + 1;
}
}, {
title: '凭证名称',
dataIndex: 'name',
key: 'name',
render: (name, record, index) => {
let short = name;
if (short && short.length > 20) {
short = short.substring(0, 20) + " ...";
}
if (hasPermission(record['owner'])) {
return (
<Button type="link" size='small' loading={this.state.items[index].updateBtnLoading}
onClick={() => this.showModal('更新凭证', record.id, index)}>
<Tooltip placement="topLeft" title={name}>
{short}
</Tooltip>
</Button>
);
} else {
return (
<Tooltip placement="topLeft" title={name}>
{short}
</Tooltip>
);
}
},
sorter: true,
}, {
title: '凭证类型',
dataIndex: 'type',
key: 'type',
render: (type, record) => {
if (type === 'private-key') {
return (
<Tag color="green">密钥</Tag>
);
} else {
return (
<Tag color="red">密码</Tag>
);
}
}
}, {
title: '授权账户',
dataIndex: 'username',
key: 'username',
}, {
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, index) => {
const menu = (
<Menu>
<Menu.Item key="1">
<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>
<Menu.Divider/>
<Menu.Item key="3">
<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' loading={this.state.items[index].updateBtnLoading}
disabled={!hasPermission(record['owner'])}
onClick={() => this.showModal('更新凭证', record.id, index)}>编辑</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={12} key={1}>
<Title level={3}>授权凭证列表</Title>
</Col>
<Col span={12} key={2} style={{textAlign: 'right'}}>
<Space>
<Search
ref={this.inputRefOfName}
placeholder="凭证名称"
allowClear
onSearch={this.handleSearchByName}
/>
<Tooltip title='重置查询'>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.current.setValue('');
this.loadTableData({pageIndex: 1, pageSize: 10, name: ''})
}}>
</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="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}
rowKey='id'
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 ?
<CredentialModal
visible={this.state.modalVisible}
title={this.state.modalTitle}
handleOk={this.handleOk}
handleCancel={this.handleCancelModal}
confirmLoading={this.state.modalConfirmLoading}
model={this.state.model}
>
</CredentialModal>
: null
}
<Modal title={<Text>更换资源「<strong style={{color: '#1890ff'}}>{this.state.selected['name']}</strong>」的所有者
</Text>}
visible={this.state.changeOwnerModalVisible}
confirmLoading={this.state.changeOwnerConfirmLoading}
onOk={() => {
this.setState({
changeOwnerConfirmLoading: true
});
let changeOwnerModalVisible = false;
this.changeOwnerFormRef
.current
.validateFields()
.then(async values => {
let result = await request.post(`/credentials/${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>
</Form>
</Modal>
</Content>
</>
);
}
Example #27
Source File: DynamicCommand.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
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) + " ...";
}
if (hasPermission(record['owner'])) {
return (
<Button type="link" size='small' onClick={() => this.showModal('更新指令', record)}>
<Tooltip placement="topLeft" title={name}>
{short}
</Tooltip>
</Button>
);
} else {
return (
<Tooltip placement="topLeft" title={name}>
{short}
</Tooltip>
);
}
},
sorter: true,
}, {
title: '指令内容',
dataIndex: 'content',
key: 'content',
render: (content, record) => {
let short = content;
if (short && short.length > 20) {
short = short.substring(0, 20) + " ...";
}
return (
<Tooltip placement="topLeft" title={content}>
{short}
</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'
onClick={() => this.showModal('更新指令', record)}>编辑</Button>
</Menu.Item>
<Menu.Item key="2">
<Button type="text" size='small'
onClick={() => {
this.handleSearchByNickname('')
.then(() => {
this.setState({
changeOwnerModalVisible: true,
selected: record,
})
this.changeOwnerFormRef
.current
.setFieldsValue({
owner: record['owner']
})
});
}}>更换所有者</Button>
</Menu.Item>
<Menu.Divider/>
<Menu.Item key="3">
<Button type="text" size='small' danger
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
</Menu.Item>
</Menu>
);
let action;
if (isAdmin()) {
action = (
<div>
<Button type="link" size='small' onClick={async () => {
this.setState({
assetsVisible: true,
commandId: record['id']
});
}}>执行</Button>
<Dropdown overlay={menu}>
<Button type="link" size='small'>
更多 <DownOutlined/>
</Button>
</Dropdown>
</div>
)
} else {
action = (
<div>
<Button type="link" size='small'
disabled={!hasPermission(record['owner'])}
onClick={() => this.showModal('更新指令', record)}>编辑</Button>
<Button type="link" size='small' danger
disabled={!hasPermission(record['owner'])}
onClick={() => this.showDeleteConfirm(record.id, record.name)}>删除</Button>
</div>
)
}
return action;
},
}
];
const selectedRowKeys = this.state.selectedRowKeys;
const rowSelection = {
selectedRowKeys: this.state.selectedRowKeys,
onChange: (selectedRowKeys, selectedRows) => {
this.setState({selectedRowKeys});
},
};
const hasSelected = selectedRowKeys.length > 0;
let contentClassName = isAdmin() ? 'page-content' : 'page-content-user';
return (
<>
<Content className={["site-layout-background", contentClassName]}>
<div style={{marginBottom: 20}}>
<Row justify="space-around" align="middle" gutter={24}>
<Col span={12} key={1}>
<Title level={3}>指令列表</Title>
</Col>
<Col span={12} key={2} style={{textAlign: 'right'}}>
<Space>
<Search
ref={this.inputRefOfName}
placeholder="指令名称"
allowClear
onSearch={this.handleSearchByName}
/>
<Search
ref={this.inputRefOfContent}
placeholder="指令内容"
allowClear
onSearch={this.handleSearchByContent}
/>
<Tooltip title='重置查询'>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.current.setValue('');
this.inputRefOfContent.current.setValue('');
this.loadTableData({pageIndex: 1, pageSize: 10, name: '', content: ''})
}}>
</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="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}
/>
{
this.state.modalVisible ?
<DynamicCommandModal
visible={this.state.modalVisible}
title={this.state.modalTitle}
handleOk={this.handleOk}
handleCancel={this.handleCancelModal}
confirmLoading={this.state.modalConfirmLoading}
model={this.state.model}
>
</DynamicCommandModal>
: null
}
<Modal
title="选择资产"
visible={this.state.assetsVisible}
width={window.innerWidth * 0.8}
centered={true}
onOk={this.executeCommand}
onCancel={() => {
this.setState({
assetsVisible: false
});
}}
>
<ChooseAsset
setCheckedAssets={this.setCheckedAssets}
>
</ChooseAsset>
</Modal>
<Modal title={<Text>更换资源「<strong style={{color: '#1890ff'}}>{this.state.selected['name']}</strong>」的所有者
</Text>}
visible={this.state.changeOwnerModalVisible}
confirmLoading={this.state.changeOwnerConfirmLoading}
onOk={() => {
this.setState({
changeOwnerConfirmLoading: true
});
let changeOwnerModalVisible = false;
this.changeOwnerFormRef
.current
.validateFields()
.then(async values => {
let result = await request.post(`/commands/${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>
</Form>
</Modal>
</Content>
</>
);
}
Example #28
Source File: MyAsset.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() {
return (
<div style={{marginTop: 20}}>
<Content style={{background: 'white', padding: 24}}>
<div>
<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}}
/>
<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.Option value="kubernetes">kubernetes</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"/>
<Tooltip title="刷新列表">
<Button icon={<SyncOutlined/>} onClick={() => {
this.loadTableData(this.state.queryParams)
}}>
</Button>
</Tooltip>
</Space>
</Col>
</Row>
</div>
</Content>
<div style={{marginTop: 20}}>
<List
loading={this.state.loading}
grid={{gutter: 16, column: 4}}
dataSource={this.state.items}
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} 条`,
pageSizeOptions: [8, 16, 32, 64, 128]
}}
renderItem={item => {
const id = item['id'];
const protocol = item['protocol'];
const name = item['name'];
const sshMode = item['sshMode'];
let url = '';
if (protocol === 'ssh' && sshMode === 'native') {
url = `#/term?assetId=${id}&assetName=${name}`;
} else {
url = `#/access?assetId=${id}&assetName=${name}&protocol=${protocol}`;
}
return (
<List.Item>
<a target='_blank' href={url} rel='noreferrer noopener'>
<Card title={item['name']}
hoverable
extra={item['active'] ?
<Tag icon={<CheckCircleOutlined/>} color="success">
运行中
</Tag> : <Tag icon={<ExclamationCircleOutlined/>} color="error">
不可用
</Tag>}
actions={[]}>
<Descriptions title="" column={1}>
<Descriptions.Item label={<div><CodeOutlined/> 资产协议</div>}>
<strong>{item['protocol']}</strong>
</Descriptions.Item>
<Descriptions.Item label={<div><TagsOutlined/> 标签</div>}>
<strong>{this.renderTags(item['tags'])}</strong>
</Descriptions.Item>
</Descriptions>
</Card>
</a>
</List.Item>
)
}}
/>
</div>
</div>
);
}
Example #29
Source File: Asset.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
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 > 15) {
short = short.substring(0, 15) + " ...";
}
if (hasPermission(record['owner'])) {
return (
<Button type="link" size='small' onClick={() => this.update(record.id)}>
<Tooltip placement="topLeft" title={name}>
{short}
</Tooltip>
</Button>
);
} else {
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: 'network',
key: 'network',
render: (text, record) => {
return `${record['ip'] + ':' + record['port']}`;
}
}, {
title: '标签',
dataIndex: 'tags',
key: 'tags',
render: tags => {
if (!isEmpty(tags)) {
return this.renderTags(tags);
}
}
}, {
title: '状态',
dataIndex: 'active',
key: 'active',
render: text => {
if (text) {
return (
<Tooltip title='运行中'>
<Badge status="processing" text='运行中'/>
</Tooltip>
)
} else {
return (
<Tooltip title='不可用'>
<Badge status="error" text='不可用'/>
</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'
onClick={() => this.update(record.id)}>编辑</Button>
</Menu.Item>
<Menu.Item key="2">
<Button type="text" size='small'
onClick={() => this.copy(record.id)}>复制</Button>
</Menu.Item>
<Menu.Item key="3">
<Button type="text" size='small'
onClick={() => this.connTest(record.id)}>连通性测试</Button>
</Menu.Item>
<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>
<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>
);
const id = record['id'];
const protocol = record['protocol'];
const name = record['name'];
const sshMode = record['sshMode'];
let url = '';
if (protocol === 'ssh' && (sshMode === 'native' || sshMode === 'naive')) {
url = `#/term?assetId=${id}&assetName=${name}`;
} else {
url = `#/access?assetId=${id}&assetName=${name}&protocol=${protocol}`;
}
return (
<div>
<Button type="link" size='small' href={url} target='_blank'>接入</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 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.Option value="kubernetes">kubernetes</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"/>
<Tooltip title="批量导入">
<Button type="dashed" icon={<ImportOutlined/>}
onClick={() => {
this.setState({
importModalVisible: true
})
}}>
</Button>
</Tooltip>
<Tooltip title="新增">
<Button 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,tag1|tag2|tag3';
//前置的"\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}
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>
</Content>
</>
);
}