@ant-design/icons#SyncOutlined JavaScript Examples
The following examples show how to use
@ant-design/icons#SyncOutlined.
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: ShowWarehouseCapacity.jsx From schema-plugin-flow with MIT License | 6 votes |
ShowWarehouseCapacity = props => {
const [counts, setCount] = useState({});
const refreshCount = () => setTimeout(() => {
const newCounts = { ...counts };
(props.options || []).forEach(item => {
newCounts[item.value] = Math.ceil(Math.random() * 100);
});
setCount(newCounts);
}, 50);
useEffect(() => {
refreshCount();
}, []);
const {
style = {}, options = [], onChange, value
} = props;
return (
<div style={style}>
<SyncOutlined className="capacity-refresh" onClick={refreshCount} />
<div className="capacity-select">
{
options.map(item => {
const cls = item.value === value ? 'capacity-item selected' : 'capacity-item';
return (
<div
className={cls}
onClick={() => onChange(item.value)}
>
{`${item.label} 库存:${counts[item.value] || ''}`}
</div>
);
})
}
</div>
</div>
);
}
Example #2
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 #3
Source File: basic.jsx From virtuoso-design-system with MIT License | 6 votes |
storiesOf('antd/Icon', module).add('basic', () =>
<div className="icons-list">
<HomeOutlined />
<SettingFilled />
<SmileOutlined />
<SyncOutlined spin />
<SmileOutlined rotate={180} />
<LoadingOutlined />
</div>,
{ docs: { page: () => (<><h1 id="enus">en-US</h1>
<p>Import icons from <code>@ant-design/icons</code>, component name of icons with different theme is the icon name suffixed by the theme name. Specify the <code>spin</code> property to show spinning animation.</p></>) } });
Example #4
Source File: index.js From gobench with Apache License 2.0 | 6 votes |
render() {
return (
<div>
<h5 className="mb-3">
<strong>Icons Usage</strong>
</h5>
<div className="mb-5">
<HomeOutlined className="mr-3 mb-3 font-size-24" />
<SettingFilled className="mr-3 mb-3 font-size-24" />
<SmileOutlined className="mr-3 mb-3 font-size-24" />
<SyncOutlined spin className="mr-3 mb-3 font-size-24" />
<SmileOutlined rotate={180} className="mr-3 mb-3 font-size-24" />
<LoadingOutlined className="mr-3 mb-3 font-size-24" />
</div>
</div>
)
}
Example #5
Source File: settings.js From hashcat.launcher with MIT License | 5 votes |
render() {
return (
<>
<PageHeader
title="Settings"
/>
<Content style={{ padding: '16px 24px' }}>
<Row gutter={[16, 14]}>
<Col span={12}>
<Statistic title="Hashes" value={this.state._hashes.length} />
</Col>
<Col span={12}>
<Statistic title="Algorithms" value={Object.keys(this.state._algorithms).length} />
</Col>
<Col span={12}>
<Statistic title="Dictionaries" value={this.state._dictionaries.length} />
</Col>
<Col span={12}>
<Statistic title="Rules" value={this.state._rules.length} />
</Col>
<Col span={12}>
<Statistic title="Masks" value={this.state._masks.length} />
</Col>
<Col span={24}>
<Button
icon={<SyncOutlined />}
type="primary"
onClick={this.onClickRescan}
loading={this.state.isLoadingRescan}
>
Rescan
</Button>
</Col>
</Row>
<Row style={{ marginTop: "2rem" }} gutter={[16, 14]}>
<Col span={24}>
<Statistic
title="Task counter"
value={this.state.taskCounter}
/>
<Space>
<Button
style={{ marginTop: 16 }}
type="default"
onClick={this.onClickRefreshTaskCounter}
loading={this.state.isLoadingRefreshTaskCounter}
>
Refresh
</Button>
<Popconfirm
placement="topRight"
title="Are you sure you want to reset the task counter?"
onConfirm={this.onClickResetTaskCounter}
okText="Yes"
cancelText="No"
>
<Button
style={{ marginTop: 16 }}
type="danger"
loading={this.state.isLoadingResetTaskCounter}
>
Reset counter
</Button>
</Popconfirm>
</Space>
</Col>
</Row>
</Content>
</>
)
}
Example #6
Source File: ExampleUI.jsx From nft-e2e-example with MIT License | 4 votes |
export default function ExampleUI({
purpose,
setPurposeEvents,
address,
mainnetProvider,
userProvider,
localProvider,
yourLocalBalance,
price,
tx,
readContracts,
writeContracts,
}) {
const [newPurpose, setNewPurpose] = useState("loading...");
return (
<div>
{/*
⚙️ Here is an example UI that displays and sets the purpose in your smart contract:
*/}
<div style={{ border: "1px solid #cccccc", padding: 16, width: 400, margin: "auto", marginTop: 64 }}>
<h2>Example UI:</h2>
<h4>purpose: {purpose}</h4>
<Divider />
<div style={{ margin: 8 }}>
<Input
onChange={e => {
setNewPurpose(e.target.value);
}}
/>
<Button
onClick={() => {
console.log("newPurpose", newPurpose);
/* look how you call setPurpose on your contract: */
tx(writeContracts.YourContract.setPurpose(newPurpose));
}}
>
Set Purpose
</Button>
</div>
<Divider />
Your Address:
<Address address={address} ensProvider={mainnetProvider} fontSize={16} />
<Divider />
ENS Address Example:
<Address
address="0x34aA3F359A9D614239015126635CE7732c18fDF3" /* this will show as austingriffith.eth */
ensProvider={mainnetProvider}
fontSize={16}
/>
<Divider />
{/* use formatEther to display a BigNumber: */}
<h2>Your Balance: {yourLocalBalance ? formatEther(yourLocalBalance) : "..."}</h2>
<div>OR</div>
<Balance address={address} provider={localProvider} price={price} />
<Divider />
<div>? Example Whale Balance:</div>
<Balance balance={parseEther("1000")} provider={localProvider} price={price} />
<Divider />
{/* use formatEther to display a BigNumber: */}
<h2>Your Balance: {yourLocalBalance ? formatEther(yourLocalBalance) : "..."}</h2>
<Divider />
Your Contract Address:
<Address
address={readContracts ? readContracts.YourContract.address : readContracts}
ensProvider={mainnetProvider}
fontSize={16}
/>
<Divider />
<div style={{ margin: 8 }}>
<Button
onClick={() => {
/* look how you call setPurpose on your contract: */
tx(writeContracts.YourContract.setPurpose("? Cheers"));
}}
>
Set Purpose to "? Cheers"
</Button>
</div>
<div style={{ margin: 8 }}>
<Button
onClick={() => {
/*
you can also just craft a transaction and send it to the tx() transactor
here we are sending value straight to the contract's address:
*/
tx({
to: writeContracts.YourContract.address,
value: parseEther("0.001"),
});
/* this should throw an error about "no fallback nor receive function" until you add it */
}}
>
Send Value
</Button>
</div>
<div style={{ margin: 8 }}>
<Button
onClick={() => {
/* look how we call setPurpose AND send some value along */
tx(
writeContracts.YourContract.setPurpose("? Paying for this one!", {
value: parseEther("0.001"),
}),
);
/* this will fail until you make the setPurpose function payable */
}}
>
Set Purpose With Value
</Button>
</div>
<div style={{ margin: 8 }}>
<Button
onClick={() => {
/* you can also just craft a transaction and send it to the tx() transactor */
tx({
to: writeContracts.YourContract.address,
value: parseEther("0.001"),
data: writeContracts.YourContract.interface.encodeFunctionData("setPurpose(string)", [
"? Whoa so 1337!",
]),
});
/* this should throw an error about "no fallback nor receive function" until you add it */
}}
>
Another Example
</Button>
</div>
</div>
{/*
? Maybe display a list of events?
(uncomment the event and emit line in YourContract.sol! )
*/}
<div style={{ width: 600, margin: "auto", marginTop: 32, paddingBottom: 32 }}>
<h2>Events:</h2>
<List
bordered
dataSource={setPurposeEvents}
renderItem={item => {
return (
<List.Item key={item.blockNumber + "_" + item.sender + "_" + item.purpose}>
<Address address={item[0]} ensProvider={mainnetProvider} fontSize={16} /> =>
{item[1]}
</List.Item>
);
}}
/>
</div>
<div style={{ width: 600, margin: "auto", marginTop: 32, paddingBottom: 256 }}>
<Card>
Check out all the{" "}
<a
href="https://github.com/austintgriffith/scaffold-eth/tree/master/packages/react-app/src/components"
target="_blank"
rel="noopener noreferrer"
>
? components
</a>
</Card>
<Card style={{ marginTop: 32 }}>
<div>
There are tons of generic components included from{" "}
<a href="https://ant.design/components/overview/" target="_blank" rel="noopener noreferrer">
? ant.design
</a>{" "}
too!
</div>
<div style={{ marginTop: 8 }}>
<Button type="primary">Buttons</Button>
</div>
<div style={{ marginTop: 8 }}>
<SyncOutlined spin /> Icons
</div>
<div style={{ marginTop: 8 }}>
Date Pickers?
<div style={{ marginTop: 2 }}>
<DatePicker onChange={() => {}} />
</div>
</div>
<div style={{ marginTop: 32 }}>
<Slider range defaultValue={[20, 50]} onChange={() => {}} />
</div>
<div style={{ marginTop: 32 }}>
<Switch defaultChecked onChange={() => {}} />
</div>
<div style={{ marginTop: 32 }}>
<Progress percent={50} status="active" />
</div>
<div style={{ marginTop: 32 }}>
<Spin />
</div>
</Card>
</div>
</div>
);
}
Example #7
Source File: ExampleUI.jsx From Tai-Shang-NFT-Wallet with MIT License | 4 votes |
export default function ExampleUI({
purpose,
setPurposeEvents,
address,
mainnetProvider,
localProvider,
yourLocalBalance,
price,
tx,
readContracts,
writeContracts,
}) {
const [newPurpose, setNewPurpose] = useState("loading...");
return (
<div>
{/*
⚙️ Here is an example UI that displays and sets the purpose in your smart contract:
*/}
<div style={{ border: "1px solid #cccccc", padding: 16, width: 400, margin: "auto", marginTop: 64 }}>
<h2>Example UI:</h2>
<h4>purpose: {purpose}</h4>
<Divider />
<div style={{ margin: 8 }}>
<Input
onChange={e => {
setNewPurpose(e.target.value);
}}
/>
<Button
style={{ marginTop: 8 }}
onClick={async () => {
/* look how you call setPurpose on your contract: */
/* notice how you pass a call back for tx updates too */
const result = tx(writeContracts.YourContract.setPurpose(newPurpose), update => {
console.log("? Transaction Update:", update);
if (update && (update.status === "confirmed" || update.status === 1)) {
console.log(" ? Transaction " + update.hash + " finished!");
console.log(
" ⛽️ " +
update.gasUsed +
"/" +
(update.gasLimit || update.gas) +
" @ " +
parseFloat(update.gasPrice) / 1000000000 +
" gwei",
);
}
});
console.log("awaiting metamask/web3 confirm result...", result);
console.log(await result);
}}
>
Set Purpose!
</Button>
</div>
<Divider />
Your Address:
<Address address={address} ensProvider={mainnetProvider} fontSize={16} />
<Divider />
ENS Address Example:
<Address
address="0x34aA3F359A9D614239015126635CE7732c18fDF3" /* this will show as austingriffith.eth */
ensProvider={mainnetProvider}
fontSize={16}
/>
<Divider />
{/* use utils.formatEther to display a BigNumber: */}
<h2>Your Balance: {yourLocalBalance ? utils.formatEther(yourLocalBalance) : "..."}</h2>
<div>OR</div>
<Balance address={address} provider={localProvider} price={price} />
<Divider />
<div>? Example Whale Balance:</div>
<Balance balance={utils.parseEther("1000")} provider={localProvider} price={price} />
<Divider />
{/* use utils.formatEther to display a BigNumber: */}
<h2>Your Balance: {yourLocalBalance ? utils.formatEther(yourLocalBalance) : "..."}</h2>
<Divider />
Your Contract Address:
<Address
address={readContracts && readContracts.YourContract ? readContracts.YourContract.address : null}
ensProvider={mainnetProvider}
fontSize={16}
/>
<Divider />
<div style={{ margin: 8 }}>
<Button
onClick={() => {
/* look how you call setPurpose on your contract: */
tx(writeContracts.YourContract.setPurpose("? Cheers"));
}}
>
Set Purpose to "? Cheers"
</Button>
</div>
<div style={{ margin: 8 }}>
<Button
onClick={() => {
/*
you can also just craft a transaction and send it to the tx() transactor
here we are sending value straight to the contract's address:
*/
tx({
to: writeContracts.YourContract.address,
value: utils.parseEther("0.001"),
});
/* this should throw an error about "no fallback nor receive function" until you add it */
}}
>
Send Value
</Button>
</div>
<div style={{ margin: 8 }}>
<Button
onClick={() => {
/* look how we call setPurpose AND send some value along */
tx(
writeContracts.YourContract.setPurpose("? Paying for this one!", {
value: utils.parseEther("0.001"),
}),
);
/* this will fail until you make the setPurpose function payable */
}}
>
Set Purpose With Value
</Button>
</div>
<div style={{ margin: 8 }}>
<Button
onClick={() => {
/* you can also just craft a transaction and send it to the tx() transactor */
tx({
to: writeContracts.YourContract.address,
value: utils.parseEther("0.001"),
data: writeContracts.YourContract.interface.encodeFunctionData("setPurpose(string)", [
"? Whoa so 1337!",
]),
});
/* this should throw an error about "no fallback nor receive function" until you add it */
}}
>
Another Example
</Button>
</div>
</div>
{/*
? Maybe display a list of events?
(uncomment the event and emit line in YourContract.sol! )
*/}
<div style={{ width: 600, margin: "auto", marginTop: 32, paddingBottom: 32 }}>
<h2>Events:</h2>
<List
bordered
dataSource={setPurposeEvents}
renderItem={item => {
return (
<List.Item key={item.blockNumber + "_" + item.sender + "_" + item.purpose}>
<Address address={item[0]} ensProvider={mainnetProvider} fontSize={16} />
{item[1]}
</List.Item>
);
}}
/>
</div>
<div style={{ width: 600, margin: "auto", marginTop: 32, paddingBottom: 256 }}>
<Card>
Check out all the{" "}
<a
href="https://github.com/austintgriffith/scaffold-eth/tree/master/packages/react-app/src/components"
target="_blank"
rel="noopener noreferrer"
>
? components
</a>
</Card>
<Card style={{ marginTop: 32 }}>
<div>
There are tons of generic components included from{" "}
<a href="https://ant.design/components/overview/" target="_blank" rel="noopener noreferrer">
? ant.design
</a>{" "}
too!
</div>
<div style={{ marginTop: 8 }}>
<Button type="primary">Buttons</Button>
</div>
<div style={{ marginTop: 8 }}>
<SyncOutlined spin /> Icons
</div>
<div style={{ marginTop: 8 }}>
Date Pickers?
<div style={{ marginTop: 2 }}>
<DatePicker onChange={() => {}} />
</div>
</div>
<div style={{ marginTop: 32 }}>
<Slider range defaultValue={[20, 50]} onChange={() => {}} />
</div>
<div style={{ marginTop: 32 }}>
<Switch defaultChecked onChange={() => {}} />
</div>
<div style={{ marginTop: 32 }}>
<Progress percent={50} status="active" />
</div>
<div style={{ marginTop: 32 }}>
<Spin />
</div>
</Card>
</div>
</div>
);
}
Example #8
Source File: main.js From ant-simple-pro with MIT License | 4 votes |
LayoutTable = memo(function LayoutTable({ btnGrounp,
iconGrounp, tableTitle, tableProps, pagaTionProps, loading, receive, children }) {
const [tableSize, setTableSize] = useState('middle');
const [pagaTionSize, setPagaTionSize] = useState('default');
const [columns, setColumns] = useState(tableProps.columns);
const elemet = useRef(null);
const tableSizeFunc = (size) => {
setTableSize(size)
setPagaTionSize(size === "small" ? 'small' : 'default')
}
const filterColunsFunc = (val) => { // 动态控制colum
setColumns(val);
}
const defauluIcon = [
{
component: <Tooltip title='刷新' placement="bottom">
<SyncOutlined onClick={(event) => receive(event)} className='svg-fontSize' />
</Tooltip>
}, {
component: <Filter tablecolumns={tableProps.columns} filterColunsFunc={filterColunsFunc} className='svg-fontSize' />
}, {
component: <TableSize tableSize={tableSizeFunc} className='svg-fontSize' />
}, {
component: <FullScreeOut elementObj={elemet.current} className='svg-fontSize' />
}
];
return (
<ConfigProvider getPopupContainer={() => (elemet.current || document.body)}>
<div className={style.layoutTable} ref={elemet}>
{
// 占位,可以是select或者其他外传的组件
children ? <div className={style['header-others']}>{children}</div> : null
}
<div className={style['header-option']}>
<div className={style['header-option-title']}>{tableTitle}</div>
<div className={style['header-option-func']}>
<div className={style['option-btn']}>
{
btnGrounp?.length ? btnGrounp.map((item, index) => (
<div key={index}>
{
item.component ? <>{item.component}</> : (<Buttons
title={item.title}
iconClass={item.iconClass}
{...item}
/>
)
}
</div>
)) : null
}
</div>
<div className={style['option-icon']}>
{(btnGrounp?.length) ? <Line /> : null}
<div className={style['icon-grounp']}>
{
defauluIcon.map((item, index) => <div className={style['icon-data']} key={index}>
<div>{item.component}</div>
</div>)
}
{
iconGrounp ? iconGrounp.map((item, index) => (
(<div className={style['icon-data']} key={index}>
{
item.component ? <div>{item.component}</div> : (
<Tooltip title={item.title} placement="bottom">
<div>{item.icon}</div>
</Tooltip>
)
}
</div>))) : null
}
</div>
</div>
</div>
</div>
<Table loading={loading} size={tableSize} {...tableProps} columns={columns} />
<Pagination {...pagaTionProps} className='view-pagitaion' size={pagaTionSize} />
</div>
</ConfigProvider>
)
})
Example #9
Source File: UserShareSelectedAsset.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) + " ...";
}
return (
<Tooltip placement="topLeft" title={name}>
{short}
</Tooltip>
);
}
}, {
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" 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>
)
}
},
];
const selectedRowKeys = this.state.selectedRowKeys;
const rowSelection = {
selectedRowKeys: this.state.selectedRowKeys,
onChange: (selectedRowKeys, selectedRows) => {
this.setState({selectedRowKeys, selectedRows});
},
getCheckboxProps: (record) => ({
disabled: record['disabled'],
}),
};
let hasSelected = false;
if (selectedRowKeys.length > 0) {
let totalSelectedRows = this.state.totalSelectedRows;
let allSelectedRowKeys = totalSelectedRows.map(item => item['id']);
for (let i = 0; i < selectedRowKeys.length; i++) {
let selectedRowKey = selectedRowKeys[i];
if (!allSelectedRowKeys.includes(selectedRowKey)) {
hasSelected = true;
break;
}
}
}
const renderStatus = (text) => {
if (text === '1') {
return <Tag color={'green'}>允许</Tag>
} else {
return <Tag color={'red'}>禁止</Tag>
}
}
return (
<>
<Row gutter={16}>
<Col span={6}>
<Title level={3}>授权策略</Title>
<Select style={{minWidth: 200}} onChange={(strategyId) => {
this.setState({
'strategyId': strategyId
})
}}>
{this.state.strategies.map(item => {
return (
<Select.Option key={item.id}>{item.name}</Select.Option>
);
})}
</Select>
</Col>
<Col span={18}>
<Title level={3}>已授权资产列表</Title>
<div>
{
this.state.totalSelectedRows.map(item => {
let strategyName = '「未配置策略」';
let content = '';
if (item['strategy'] !== undefined) {
strategyName = item['strategy']['name'];
content = (
<div>
<p>上传:{renderStatus(item['strategy']['upload'])}</p>
<p>下载:{renderStatus(item['strategy']['download'])}</p>
<p>编辑:{renderStatus(item['strategy']['edit'])}</p>
<p>删除:{renderStatus(item['strategy']['delete'])}</p>
<p>重命名:{renderStatus(item['strategy']['rename'])}</p>
<p>复制:{renderStatus(item['strategy']['copy'])}</p>
<p>粘贴:{renderStatus(item['strategy']['paste'])}</p>
</div>
);
}
return (
<Popover content={content} title={strategyName}>
<Tag color={PROTOCOL_COLORS[item['protocol']]} closable
onClose={(e) => {
e.preventDefault()
this.unSelectRow(item['id'])
}}
key={item['id']}>{[item['name'], strategyName].join(':')}</Tag>
</Popover>
);
})
}
</div>
</Col>
</Row>
<Divider/>
<Content key='page-content' className="site-layout-background">
<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
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.loadTableData({
...this.state.queryParams,
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 type="primary" disabled={!hasSelected} icon={<PlusOutlined/>}
onClick={async () => {
let totalSelectedRows = this.state.totalSelectedRows;
let totalSelectedRowKeys = totalSelectedRows.map(item => item['id']);
let selectedRows = this.state.selectedRows;
let newRowKeys = []
for (let i = 0; i < selectedRows.length; i++) {
let selectedRow = selectedRows[i];
if (totalSelectedRowKeys.includes(selectedRow['id'])) {
continue;
}
selectedRow['strategy'] = this.getStrategyByAssetId(this.state.strategies, this.state.sharers, selectedRow['id'], this.state.strategyId);
totalSelectedRows.push(selectedRow);
newRowKeys.push(selectedRow['id']);
}
let userId = this.state.sharer;
let userGroupId = this.state.userGroupId;
let strategyId = this.state.strategyId;
let result = await request.post(`/resource-sharers/add-resources`, {
userGroupId: userGroupId,
userId: userId,
strategyId: strategyId,
resourceType: 'asset',
resourceIds: newRowKeys
});
if (result['code'] === 1) {
message.success('操作成功', 3);
this.setState({
totalSelectedRows: totalSelectedRows
})
await this.loadTableData();
} else {
message.error(result['message'], 10);
}
}}>
</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}
/>
</Content>
</>
);
}
Example #10
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 #11
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 #12
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 #13
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 #14
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 #15
Source File: Storage.js From next-terminal with GNU Affero General Public License v3.0 | 4 votes |
render() {
return (
<div>
<Content className="site-layout-background page-content">
<div>
<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>
</Space>
</Col>
</Row>
</div>
</Content>
<div style={{margin: '0 16px'}}>
<List
loading={this.state.loading}
grid={{gutter: 16, column: 4}}
dataSource={this.state.items}
renderItem={item => {
let delBtn;
if (item['isDefault']) {
delBtn = <DeleteOutlined key="delete" className={'disabled-icon'}/>
} else {
delBtn = <Popconfirm
title="您确认要删除此空间吗?"
onConfirm={() => {
this.delete(item['id']);
}}
okText="是"
cancelText="否"
>
<DeleteOutlined key="delete"/>
</Popconfirm>
}
return (
<List.Item>
<Card title={item['name']}
hoverable
actions={[
<FolderOutlined key='file' onClick={() => {
this.setState({
fileSystemVisible: true,
storageId: item['id']
});
if (this.storageRef) {
this.storageRef.reSetStorageId(item['id']);
}
}}/>,
<EditOutlined key="edit" onClick={() => {
// 转换文件大小限制单位为MB
let model = cloneObj(item);
if(model['limitSize'] > 0){
model['limitSize'] = model['limitSize'] / 1024 / 1024;
}
this.showModal('修改磁盘空间', model);
}}/>,
delBtn
,
]}>
<Descriptions title="" column={1}>
<Descriptions.Item label={<div><TeamOutlined/> 是否共享</div>}>
<strong>{item['isShare'] ? '是' : '否'}</strong>
</Descriptions.Item>
<Descriptions.Item label={<div><SafetyCertificateOutlined/> 是否默认</div>}>
<strong>{item['isDefault'] ? '是' : '否'}</strong>
</Descriptions.Item>
<Descriptions.Item label={<div><FireOutlined/> 大小限制</div>}>
<strong>{item['limitSize'] < 0 ? '无限制' : renderSize(item['limitSize'])}</strong>
</Descriptions.Item>
<Descriptions.Item label={<div><HeartOutlined/> 已用大小</div>}>
<strong>{renderSize(item['usedSize'])}</strong>
</Descriptions.Item>
<Descriptions.Item label={<div><UserOutlined/> 所属用户</div>}>
<strong>{item['ownerName']}</strong>
</Descriptions.Item>
</Descriptions>
</Card>
</List.Item>
)
}}
/>
</div>
<Drawer
title={'文件管理'}
placement="right"
width={window.innerWidth * 0.8}
closable={true}
maskClosable={true}
onClose={() => {
this.setState({
fileSystemVisible: false
});
this.loadTableData(this.state.queryParams);
}}
visible={this.state.fileSystemVisible}
>
<FileSystem
storageId={this.state.storageId}
storageType={'storages'}
onRef={this.onRef}
upload={true}
download={true}
delete={true}
rename={true}
edit={true}
minHeight={window.innerHeight - 103}/>
</Drawer>
{
this.state.modalVisible ?
<StorageModal
visible={this.state.modalVisible}
title={this.state.modalTitle}
handleOk={this.handleOk}
handleCancel={() => {
this.setState({
modalTitle: '',
modalVisible: false
});
}}
confirmLoading={this.state.modalConfirmLoading}
model={this.state.model}
>
</StorageModal> : undefined
}
</div>
);
}
Example #16
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 #17
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 #18
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 #19
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 #20
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 #21
Source File: ChooseAsset.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>
);
}
}, {
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>{tagArr[i]}</Tag>)
}
return tagDocuments;
}
}
}, {
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>
)
}
},
];
const selectedRowKeys = this.state.selectedRowKeys;
const rowSelection = {
selectedRowKeys: selectedRowKeys,
onChange: (selectedRowKeys, selectedRows) => {
this.setState({selectedRowKeys, selectedRows});
let totalSelectedRows = this.state.totalSelectedRows;
let totalSelectedRowKeys = totalSelectedRows.map(item => item['id']);
for (let i = 0; i < selectedRows.length; i++) {
let selectedRow = selectedRows[i];
if (totalSelectedRowKeys.includes(selectedRow['id'])) {
continue;
}
totalSelectedRows.push(selectedRow);
}
this.setState({
totalSelectedRows: totalSelectedRows
})
if (this.checkedAssets) {
this.checkedAssets(totalSelectedRows);
}
console.log(totalSelectedRows);
},
getCheckboxProps: (record) => ({
disabled: record['disabled'],
}),
};
return (
<>
<Title level={3}>已选择资产列表</Title>
<div>
{
this.state.totalSelectedRows.map(item => {
return <Tag color={PROTOCOL_COLORS[item['protocol']]} closable
onClose={() => this.unSelectRow(item['id'])}
key={item['id']}>{item['name']}</Tag>
})
}
</div>
<Divider/>
<Content key='page-content' className="site-layout-background">
<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
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>
<Tooltip title='重置查询'>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.current.setValue('');
this.inputRefOfIp.current.setValue('');
this.loadTableData({
...this.state.queryParams,
pageIndex: 1,
pageSize: 10,
protocol: 'ssh',
name: '',
ip: ''
})
}}>
</Button>
</Tooltip>
<Divider type="vertical"/>
<Tooltip title="刷新列表">
<Button icon={<SyncOutlined/>} onClick={() => {
this.loadTableData(this.state.queryParams)
}}>
</Button>
</Tooltip>
</Space>
</Col>
</Row>
</div>
<Table key='assets-table'
rowSelection={rowSelection}
dataSource={this.state.items}
columns={columns}
position={'both'}
size="middle"
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}
/>
</Content>
</>
);
}
Example #22
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 #23
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>
</>
);
}
Example #24
Source File: AccessGateway.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.update(record['id'])}>
<Tooltip placement="topLeft" title={name}>
{short}
</Tooltip>
</Button>
);
} else {
return (
<Tooltip placement="topLeft" title={name}>
{short}
</Tooltip>
);
}
},
sorter: true,
}, {
title: 'IP',
dataIndex: 'ip',
key: 'ip',
sorter: true,
}, {
title: '端口',
dataIndex: 'port',
key: 'port',
}, {
title: '账户类型',
dataIndex: 'accountType',
key: 'accountType',
render: (accountType) => {
if (accountType === 'private-key') {
return (
<Tag color="green">密钥</Tag>
);
} else {
return (
<Tag color="red">密码</Tag>
);
}
}
}, {
title: '授权账户',
dataIndex: 'username',
key: 'username',
}, {
title: '状态',
dataIndex: 'connected',
key: 'connected',
render: (text, record) => {
if (text) {
return (
<Tooltip title='连接成功'>
<Badge status="success" text='已连接'/>
</Tooltip>
)
} else {
return (
<Tooltip title={record['message']}>
<Badge status="error" text='已断开'/>
</Tooltip>
)
}
}
}, {
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) => {
return (
<div>
<Button type="link" size='small'
onClick={() => this.update(record['id'])}>编辑</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}
/>
<Search
ref={this.inputRefOfIp}
placeholder="IP"
allowClear
onSearch={this.handleSearchByIp}
/>
<Tooltip title='重置查询'>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.current.setValue('');
this.inputRefOfIp.current.setValue('');
this.loadTableData({
pageIndex: 1,
pageSize: 10,
name: '',
ip: '',
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 ?
<AccessGatewayModal
visible={this.state.modalVisible}
title={this.state.modalTitle}
handleOk={this.handleOk}
handleCancel={this.handleCancelModal}
confirmLoading={this.state.modalConfirmLoading}
model={this.state.model}
>
</AccessGatewayModal> : undefined
}
</Content>
</>
);
}
Example #25
Source File: TopicConsumerGroupOffset.js From kafka-map with Apache License 2.0 | 4 votes |
render() {
const columns = [{
title: 'Partition',
dataIndex: 'partition',
key: 'partition',
defaultSortOrder: 'ascend',
sorter: (a, b) => a['partition'] - b['partition'],
}, {
title: 'Beginning Offset',
dataIndex: 'beginningOffset',
key: 'beginningOffset',
sorter: (a, b) => a['beginningOffset'] - b['beginningOffset'],
}, {
title: 'End Offset',
dataIndex: 'endOffset',
key: 'endOffset',
sorter: (a, b) => a['endOffset'] - b['endOffset'],
}, {
title: 'Consumer Offset',
dataIndex: 'consumerOffset',
key: 'consumerOffset',
sorter: (a, b) => a['consumerOffset'] - b['consumerOffset'],
}, {
title: 'Lag',
dataIndex: 'lag',
key: 'lag',
sorter: (a, b) => a['lag'] - b['lag'],
render: (lag, record, index) => {
return record['endOffset'] - record['consumerOffset']
}
}, {
title: 'Operate',
key: 'action',
render: (text, record, index) => {
return (
<div>
<Button type="link" size='small' onClick={() => {
this.setState({
resetOffsetVisible: true,
selectedRow: record
})
}}>Reset Offset</Button>
</div>
)
},
}];
return (
<div>
<div style={{marginBottom: 20}}>
<Row justify="space-around" align="middle" gutter={24}>
<Col span={20} key={1}>
<Title level={3}><FormattedMessage id="consume-detail" /></Title>
</Col>
<Col span={4} key={2} style={{textAlign: 'right'}}>
<Space>
<Tooltip title={<FormattedMessage id="refresh" />}>
<Button icon={<SyncOutlined/>} onClick={() => {
let clusterId = this.state.clusterId;
let topic = this.state.topic;
let groupId = this.state.groupId;
this.loadItems(clusterId, topic, groupId);
}}>
</Button>
</Tooltip>
</Space>
</Col>
</Row>
</div>
<Table key='table'
dataSource={this.state.items}
columns={columns}
position={'both'}
pagination={{
showSizeChanger: true,
total: this.state.items.length,
showTotal: total => <FormattedMessage id="total-items" values={{total}}/>
}}
loading={this.state.loading}
/>
<Drawer
title={'Partition: ' + this.state.selectedRow['partition']}
width={window.innerWidth * 0.3}
closable={true}
onClose={() => {
this.setState({
resetOffsetVisible: false
})
}}
visible={this.state.resetOffsetVisible}
footer={
<div
style={{
textAlign: 'right',
}}
>
<Button
loading={this.state.resetting}
onClick={() => {
this.form.current
.validateFields()
.then(async values => {
this.setState({
resetting: true
})
let topic = this.state.topic;
let groupId = this.state.groupId;
let clusterId = this.state.clusterId;
await request.put(`/topics/${topic}/consumerGroups/${groupId}/offset?clusterId=${clusterId}`, values);
this.form.current.resetFields();
this.setState({
resetOffsetVisible: false
})
this.loadItems(clusterId, topic, groupId);
})
.catch(info => {
}).finally(() => this.setState({resetting: false}));
}} type="primary">
重置
</Button>
</div>
}
>
<Alert message={<FormattedMessage id="reset-warning" />} description={<FormattedMessage id="reset-warning-description" />} type="warning" showIcon
style={{marginBottom: 16}}/>
<Form ref={this.form}>
<Form.Item label={<FormattedMessage id="reset-partition" />} name='seek' rules={[{required: true}]}>
<Radio.Group onChange={(e) => {
let seek = e.target.value;
this.setState({
'seek': seek
})
}}>
<Radio value={'end'}><FormattedMessage id="latest" /></Radio>
<Radio value={'beginning'}><FormattedMessage id="earliest" /></Radio>
<Radio value={'custom'}><FormattedMessage id="customization" /></Radio>
</Radio.Group>
</Form.Item>
{
this.state.seek === 'custom' ?
<Form.Item label="offset" name='offset' rules={[{required: true}]}>
<InputNumber min={0}/>
</Form.Item> : undefined
}
</Form>
</Drawer>
</div>
);
}
Example #26
Source File: Topic.js From kafka-map with Apache License 2.0 | 4 votes |
render() {
const columns = [{
title: <FormattedMessage id="index" />,
dataIndex: 'index',
key: 'index',
render: (id, record, index) => {
return index + 1;
}
}, {
title: <FormattedMessage id="name" />,
dataIndex: 'name',
key: 'name',
defaultSortOrder: 'ascend',
sorter: (a, b) => a.name.localeCompare(b.name),
width: window.innerWidth * 0.3,
render: (name, record, index) => {
return <Link to={`/topic-info?clusterId=${record['clusterId']}&topic=${name}`}>
{name}
</Link>
}
}, {
title: <FormattedMessage id="partitions" />,
dataIndex: 'partitionsCount',
key: 'partitionsCount',
sorter: (a, b) => a['partitionsCount'] - b['partitionsCount'],
}, {
title: <FormattedMessage id="replicas" />,
dataIndex: 'replicaCount',
key: 'replicaCount',
sorter: (a, b) => a['replicaCount'] - b['replicaCount'],
}, {
title: <FormattedMessage id="average-log-size" />,
dataIndex: 'x',
key: 'x',
sorter: (a, b) => a['totalLogSize'] / a['replicaCount'] - b['totalLogSize'] / b['replicaCount'],
render: (x, record) => {
if (record['totalLogSize'] < 0) {
return '不支持';
}
return renderSize(record['totalLogSize'] / record['replicaCount'])
}
}, {
title: <FormattedMessage id="log-size" />,
dataIndex: 'totalLogSize',
key: 'totalLogSize',
sorter: (a, b) => a['totalLogSize'] - b['totalLogSize'],
render: (totalLogSize) => {
if (totalLogSize < 0) {
return '不支持';
} else {
return renderSize(totalLogSize);
}
}
},
{
title: <FormattedMessage id="operate" />,
key: 'action',
render: (text, record, index) => {
return (
<div>
<Link to={`/topic-data?clusterId=${this.state.clusterId}&topic=${record['name']}`}>
<Button key="1" type="link" size='small'>
<FormattedMessage id="consume-message" />
</Button>
</Link>
<Button type="link" size='small' onClick={() => {
this.setState({
createPartitionVisible: true,
selectedRow: record
})
}}><FormattedMessage id="topic-alter" /></Button>
<Popconfirm
title={<FormattedMessage id="delete-confirm" />}
onConfirm={() => this.delete(record['name'], index)}
>
<Button type="text" size='small' danger
loading={this.state.items[index]['deleting']}><FormattedMessage id="delete" /></Button>
</Popconfirm>
</div>
)
},
}
]
;
return (
<div>
<div className='kd-page-header'>
<PageHeader
className="site-page-header"
onBack={() => {
this.props.history.goBack();
}}
subTitle={<FormattedMessage id="topic-management" />}
title={this.state.clusterName}
/>
</div>
<div className='kd-content'>
<div style={{marginBottom: 20}}>
<Row justify="space-around" align="middle" gutter={24}>
<Col span={4} key={1}>
<Title level={3}>Topic</Title>
</Col>
<Col span={20} key={2} style={{textAlign: 'right'}}>
<Space>
<Search
ref={this.inputRefOfName}
placeholder="topic"
allowClear
onSearch={this.handleSearchByName}
/>
<Tooltip title={<FormattedMessage id="reset" />}>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.current.setValue('');
this.loadTableData({
pageIndex: 1,
pageSize: 10,
clusterId: this.state.clusterId,
name: ''
})
}}>
</Button>
</Tooltip>
<Divider type="vertical"/>
<Tooltip title={<FormattedMessage id="create-topic" />}>
<Button type="dashed" icon={<PlusOutlined/>}
onClick={() => this.showModal(<FormattedMessage id="create-topic" />)}>
</Button>
</Tooltip>
<Tooltip title={<FormattedMessage id="refresh" />}>
<Button icon={<SyncOutlined/>} onClick={() => {
this.loadTableData(this.state.queryParams)
}}>
</Button>
</Tooltip>
</Space>
</Col>
</Row>
</div>
<Table
rowKey='name'
dataSource={this.state.items}
columns={columns}
position={'both'}
size={'middle'}
loading={this.state.loading}
pagination={{
showSizeChanger: true,
total: this.state.items.length,
showTotal: total => <FormattedMessage id="total-items" values={{total}}/>
}}
/>
{
this.state.modalVisible ?
<TopicModal
title={this.state.modalTitle}
handleOk={this.handleOk}
handleCancel={this.handleCancelModal}
confirmLoading={this.state.modalConfirmLoading}
model={null}
brokerCount={this.state.brokerCount}
/> : undefined
}
<Modal title={<FormattedMessage id="topic-alter" />}
visible={this.state.createPartitionVisible}
confirmLoading={this.state.createPartitionConfirmLoading}
onOk={() => {
this.form.current
.validateFields()
.then(async values => {
this.setState({
createPartitionConfirmLoading: true
})
let topic = this.state.selectedRow['name'];
let clusterId = this.state.clusterId;
await request.post(`/topics/${topic}/partitions?clusterId=${clusterId}&totalCount=${values['totalCount']}`);
this.form.current.resetFields();
this.setState({
createPartitionVisible: false
})
this.loadTableData();
})
.catch(info => {
}).finally(() => this.setState({createPartitionConfirmLoading: false}));
}}
onCancel={() => {
this.setState({
createPartitionVisible: false
})
}}>
<Form ref={this.form} {...formItemLayout}>
<Form.Item label={<FormattedMessage id="numPartitions" />} name='totalCount' rules={[{required: true}]}>
<InputNumber min={this.state.selectedRow['partitionsCount']}
placeholder={"Can't be less than the current: " + this.state.selectedRow["partitionsCount"]}
style={{width: '100%'}}/>
</Form.Item>
</Form>
</Modal>
</div>
</div>
);
}
Example #27
Source File: ConsumerGroup.js From kafka-map with Apache License 2.0 | 4 votes |
render() {
const columns = [{
title: <FormattedMessage id="index"/>,
dataIndex: 'id',
key: 'id',
render: (id, record, index) => {
return index + 1;
}
}, {
title: <FormattedMessage id="name"/>,
dataIndex: 'groupId',
key: 'groupId',
defaultSortOrder: 'ascend',
sorter: (a, b) => a.groupId.localeCompare(b.groupId),
width: window.innerWidth * 0.3,
render: (groupId, record, index) => {
return <Link to={`/consumer-group-info?clusterId=${this.state.clusterId}&groupId=${groupId}`}>
{groupId}
</Link>
}
}, {
title: <FormattedMessage id="topic-count"/>,
dataIndex: 'topicCount',
key: 'topicCount',
sorter: (a, b) => a['topics'].length - b['topics'].length,
render: (topicCount, record, index) => {
const content = <List
size="small"
dataSource={record['topics']}
renderItem={item => <List.Item><Link
to={`/topic-info?clusterId=${this.state.clusterId}&topic=${item}`}>{item}</Link></List.Item>}
/>
return <Popover content={content} title={<FormattedMessage id="topic-list"/>}>
<Button type="link" size='small'>{record['topics'].length}</Button>
</Popover>
}
},
{
title: <FormattedMessage id="operate"/>,
key: 'action',
render: (text, record, index) => {
return (
<div>
<Popconfirm
title={<FormattedMessage id="delete-confirm"/>}
onConfirm={() => this.delete(record['groupId'], index)}
>
<Button type="text" size='small' danger
loading={this.state.items[index]['deleting']}><FormattedMessage
id="delete"/></Button>
</Popconfirm>
</div>
)
},
}
]
;
return (
<div>
<div className='kd-page-header'>
<PageHeader
className="site-page-header"
onBack={() => {
this.props.history.goBack();
}}
subTitle={<FormattedMessage id="consumer-group-management"/>}
title={this.state.clusterName}
/>
</div>
<div className='kd-content'>
<div style={{marginBottom: 20}}>
<Row justify="space-around" align="middle" gutter={24}>
<Col span={8} key={1}>
<Title level={3}>Consumer Group</Title>
</Col>
<Col span={16} key={2} style={{textAlign: 'right'}}>
<Space>
<Search
ref={this.inputRefOfName}
placeholder="Group ID"
allowClear
onSearch={this.handleSearchByName}
/>
<Tooltip title={<FormattedMessage id="reset"/>}>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.current.setValue('');
this.loadTableData({
pageIndex: 1,
pageSize: 10,
clusterId: this.state.clusterId,
name: ''
})
}}>
</Button>
</Tooltip>
<Divider type="vertical"/>
<Tooltip title={<FormattedMessage id="refresh"/>}>
<Button icon={<SyncOutlined/>} onClick={() => {
this.loadTableData(this.state.queryParams)
}}>
</Button>
</Tooltip>
</Space>
</Col>
</Row>
</div>
<Table
rowKey='groupId'
dataSource={this.state.items}
columns={columns}
position={'both'}
size={'middle'}
loading={this.state.loading}
pagination={{
showSizeChanger: true,
total: this.state.items.length,
showTotal: total => <FormattedMessage id="total-items" values={{total}}/>
}}
/>
</div>
</div>
);
}
Example #28
Source File: Cluster.js From kafka-map with Apache License 2.0 | 4 votes |
render() {
const columns = [{
title: <FormattedMessage id="index"/>,
dataIndex: 'id',
key: 'id',
render: (id, record, index) => {
return index + 1;
}
}, {
title: <FormattedMessage id="name"/>,
dataIndex: 'name',
key: 'name',
render: (name, record) => {
let short = name;
if (short && short.length > 30) {
short = short.substring(0, 30) + " ...";
}
return (
<Tooltip placement="topLeft" title={name}>
{short}
</Tooltip>
);
},
sorter: true,
}, {
title: <FormattedMessage id="servers"/>,
dataIndex: 'servers',
key: 'servers',
}, {
title: <FormattedMessage id="delay-message"/>,
dataIndex: 'delayMessageStatus',
key: 'delayMessageStatus',
render: (delayMessageStatus, record, index) => {
return <Popover content={content}>
<Switch checked={delayMessageStatus === 'enabled'}
onChange={async (checked) => {
let url = `/clusters/${record['id']}/disableDelayMessage`;
if (checked) {
url = `/clusters/${record['id']}/enableDelayMessage`;
}
await request.post(url);
this.loadTableData()
}
}/>
</Popover>
}
}, {
title: <FormattedMessage id="topic"/>,
dataIndex: 'topicCount',
key: 'topicCount',
render: (topicCount, record, index) => {
return <Link
to={`/topic?clusterId=${record['id']}&clusterName=${record['name']}&brokerCount=${record['brokerCount']}`}>
{topicCount}
</Link>
}
}, {
title: <FormattedMessage id="broker"/>,
dataIndex: 'brokerCount',
key: 'brokerCount',
render: (brokerCount, record, index) => {
return <Link to={`/broker?clusterId=${record['id']}&clusterName=${record['name']}`}>
{brokerCount}
</Link>
}
}, {
title: <FormattedMessage id="consumer-group"/>,
dataIndex: 'consumerCount',
key: 'consumerCount',
render: (consumerCount, record, index) => {
return <Link to={`/consumer-group?clusterId=${record['id']}&clusterName=${record['name']}`}>
{consumerCount}
</Link>
}
}, {
title: <FormattedMessage id="created"/>,
dataIndex: 'created',
key: 'created',
render: (text, record) => {
return (
<Tooltip title={text}>
{dayjs(text).fromNow()}
</Tooltip>
)
},
sorter: true,
},
{
title: <FormattedMessage id="operate"/>,
key: 'action',
render: (text, record, index) => {
return (
<div>
<Button type="link" size='small' onClick={() => {
this.showModal(<FormattedMessage id="edit"/>, record)
}}><FormattedMessage id="edit"/></Button>
<Popconfirm
title={<FormattedMessage id="delete-confirm"/>}
onConfirm={() => this.delete(record['id'])}
>
<Button type="text" size='small' danger><FormattedMessage id="delete"/></Button>
</Popconfirm>
</div>
)
},
}
];
const selectedRowKeys = this.state.selectedRowKeys;
const rowSelection = {
selectedRowKeys: this.state.selectedRowKeys,
onChange: (selectedRowKeys, selectedRows) => {
this.setState({selectedRowKeys});
},
};
const hasSelected = selectedRowKeys.length > 0;
return (
<div className='kd-content'>
<div style={{marginBottom: 20}}>
<Row justify="space-around" align="middle" gutter={24}>
<Col span={12} key={1}>
<Title level={3}><FormattedMessage id="cluster"/></Title>
</Col>
<Col span={12} key={2} style={{textAlign: 'right'}}>
<Space>
<Search
ref={this.inputRefOfName}
placeholder={'name'}
allowClear
onSearch={this.handleSearchByName}
/>
<Tooltip title={<FormattedMessage id="reset"/>}>
<Button icon={<UndoOutlined/>} onClick={() => {
this.inputRefOfName.current.setValue('');
this.loadTableData({pageIndex: 1, pageSize: 10, name: ''})
}}>
</Button>
</Tooltip>
<Divider type="vertical"/>
<Tooltip title={<FormattedMessage id="import-cluster"/>}>
<Button type="dashed" icon={<PlusOutlined/>}
onClick={() => this.showModal(<FormattedMessage id="import-cluster"/>)}>
</Button>
</Tooltip>
<Tooltip title={<FormattedMessage id="refresh"/>}>
<Button icon={<SyncOutlined/>} onClick={() => {
this.loadTableData(this.state.queryParams)
}}>
</Button>
</Tooltip>
<Tooltip title={<FormattedMessage id="batch-delete"/>}>
<Button type="primary" danger disabled={!hasSelected} icon={<DeleteOutlined/>}
loading={this.state.delBtnLoading}
onClick={() => {
const content = <div>
Are you sure delete <Text style={{color: '#1890FF'}}
strong>{this.state.selectedRowKeys.length}</Text> items?
</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 => {
return <FormattedMessage id="total-items" values={{total: total}}/>
}
}}
loading={this.state.loading}
/>
{
this.state.modalVisible ?
<ClusterModal
title={this.state.modalTitle}
handleOk={this.handleOk}
handleCancel={this.handleCancelModal}
confirmLoading={this.state.modalConfirmLoading}
model={this.state.model}
/> : undefined
}
</div>
);
}
Example #29
Source File: ExampleUI.jsx From moonshot with MIT License | 4 votes |
export default function ExampleUI({purpose, setPurposeEvents, address, mainnetProvider, userProvider, localProvider, yourLocalBalance, price, tx, readContracts, writeContracts }) {
const [newPurpose, setNewPurpose] = useState("loading...");
return (
<div>
{/*
⚙️ Here is an example UI that displays and sets the purpose in your smart contract:
*/}
<div style={{border:"1px solid #cccccc", padding:16, width:400, margin:"auto",marginTop:64}}>
<h2>Example UI:</h2>
<h4>purpose: {purpose}</h4>
<Divider/>
<div style={{margin:8}}>
<Input onChange={(e)=>{setNewPurpose(e.target.value)}} />
<Button onClick={()=>{
console.log("newPurpose",newPurpose)
/* look how you call setPurpose on your contract: */
tx( writeContracts.YourContract.setPurpose(newPurpose) )
}}>Set Purpose</Button>
</div>
<Divider />
Your Address:
<Address
address={address}
ensProvider={mainnetProvider}
fontSize={16}
/>
<Divider />
ENS Address Example:
<Address
address={"0x34aA3F359A9D614239015126635CE7732c18fDF3"} /* this will show as austingriffith.eth */
ensProvider={mainnetProvider}
fontSize={16}
/>
<Divider/>
{ /* use formatEther to display a BigNumber: */ }
<h2>Your Balance: {yourLocalBalance?formatEther(yourLocalBalance):"..."}</h2>
<div>OR</div>
<Balance
address={address}
provider={localProvider}
price={price}
/>
<Divider/>
<div>? Example Whale Balance:</div>
<Balance
balance={parseEther("1000")}
provider={localProvider}
price={price}
/>
<Divider/>
{ /* use formatEther to display a BigNumber: */ }
<h2>Your Balance: {yourLocalBalance?formatEther(yourLocalBalance):"..."}</h2>
<Divider/>
Your Contract Address:
<Address
address={readContracts?readContracts.YourContract.address:readContracts}
ensProvider={mainnetProvider}
fontSize={16}
/>
<Divider />
<div style={{margin:8}}>
<Button onClick={()=>{
/* look how you call setPurpose on your contract: */
tx( writeContracts.YourContract.setPurpose("? Cheers") )
}}>Set Purpose to "? Cheers"</Button>
</div>
<div style={{margin:8}}>
<Button onClick={()=>{
/*
you can also just craft a transaction and send it to the tx() transactor
here we are sending value straight to the contract's address:
*/
tx({
to: writeContracts.YourContract.address,
value: parseEther("0.001")
});
/* this should throw an error about "no fallback nor receive function" until you add it */
}}>Send Value</Button>
</div>
<div style={{margin:8}}>
<Button onClick={()=>{
/* look how we call setPurpose AND send some value along */
tx( writeContracts.YourContract.setPurpose("? Paying for this one!",{
value: parseEther("0.001")
}))
/* this will fail until you make the setPurpose function payable */
}}>Set Purpose With Value</Button>
</div>
<div style={{margin:8}}>
<Button onClick={()=>{
/* you can also just craft a transaction and send it to the tx() transactor */
tx({
to: writeContracts.YourContract.address,
value: parseEther("0.001"),
data: writeContracts.YourContract.interface.encodeFunctionData("setPurpose(string)",["? Whoa so 1337!"])
});
/* this should throw an error about "no fallback nor receive function" until you add it */
}}>Another Example</Button>
</div>
</div>
{/*
? Maybe display a list of events?
(uncomment the event and emit line in YourContract.sol! )
*/}
<div style={{ width:600, margin: "auto", marginTop:32, paddingBottom:32 }}>
<h2>Events:</h2>
<List
bordered
dataSource={setPurposeEvents}
renderItem={(item) => {
return (
<List.Item key={item.blockNumber+"_"+item.sender+"_"+item.purpose}>
<Address
address={item[0]}
ensProvider={mainnetProvider}
fontSize={16}
/> =>
{item[1]}
</List.Item>
)
}}
/>
</div>
<div style={{ width:600, margin: "auto", marginTop:32, paddingBottom:256 }}>
<Card>
Check out all the <a href="https://github.com/austintgriffith/scaffold-eth/tree/master/packages/react-app/src/components" target="_blank" rel="noopener noreferrer">? components</a>
</Card>
<Card style={{marginTop:32}}>
<div>
There are tons of generic components included from <a href="https://ant.design/components/overview/" target="_blank" rel="noopener noreferrer">? ant.design</a> too!
</div>
<div style={{marginTop:8}}>
<Button type="primary">
Buttons
</Button>
</div>
<div style={{marginTop:8}}>
<SyncOutlined spin /> Icons
</div>
<div style={{marginTop:8}}>
Date Pickers?
<div style={{marginTop:2}}>
<DatePicker onChange={()=>{}}/>
</div>
</div>
<div style={{marginTop:32}}>
<Slider range defaultValue={[20, 50]} onChange={()=>{}}/>
</div>
<div style={{marginTop:32}}>
<Switch defaultChecked onChange={()=>{}} />
</div>
<div style={{marginTop:32}}>
<Progress percent={50} status="active" />
</div>
<div style={{marginTop:32}}>
<Spin />
</div>
</Card>
</div>
</div>
);
}