mirror of https://github.com/openspug/spug
U web add host module
parent
bad6431553
commit
7925e4ce0b
|
@ -0,0 +1,7 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Button } from 'antd';
|
||||||
|
|
||||||
|
|
||||||
|
export default function LinkButton(props) {
|
||||||
|
return <Button type="link" style={{padding: 0}} {...props}>{props.children}</Button>
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
import StatisticsCard from './StatisticsCard';
|
import StatisticsCard from './StatisticsCard';
|
||||||
import SearchForm from './SearchForm';
|
import SearchForm from './SearchForm';
|
||||||
|
import LinkButton from './LinkButton';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
StatisticsCard,
|
StatisticsCard,
|
||||||
SearchForm,
|
SearchForm,
|
||||||
|
LinkButton,
|
||||||
}
|
}
|
|
@ -16,6 +16,5 @@ code {
|
||||||
|
|
||||||
/* Common CSS style */
|
/* Common CSS style */
|
||||||
.span-button {
|
.span-button {
|
||||||
cursor: pointer;
|
padding: 0;
|
||||||
color: #1890ff;
|
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
export default [
|
export default [
|
||||||
{icon: 'desktop', title: '工作台', path: '/home'},
|
{icon: 'desktop', title: '工作台', path: '/home'},
|
||||||
|
{icon: 'cloud-server', title: '主机管理', path: '/host'},
|
||||||
{
|
{
|
||||||
icon: 'setting', title: '系统管理', child: [
|
icon: 'setting', title: '系统管理', child: [
|
||||||
{title: '账户管理', path: '/system/account'},
|
{title: '账户管理', path: '/system/account'},
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Modal, Form, Input, message } from 'antd';
|
||||||
|
import http from 'libs/http';
|
||||||
|
import store from './store';
|
||||||
|
|
||||||
|
class ComForm extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
loading: false,
|
||||||
|
password: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = () => {
|
||||||
|
this.setState({loading: true});
|
||||||
|
const formData = this.props.form.getFieldsValue();
|
||||||
|
formData['id'] = store.record.id;
|
||||||
|
http.post('/api/host/', formData)
|
||||||
|
.then(res => {
|
||||||
|
if (res === 'auth fail') {
|
||||||
|
this.setState({loading: false});
|
||||||
|
Modal.confirm({
|
||||||
|
icon: 'exclamation-circle',
|
||||||
|
title: '首次验证请输入密码',
|
||||||
|
content: this.confirmForm(formData.username),
|
||||||
|
onOk: () => this.handleConfirm(formData),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
message.success('操作成功');
|
||||||
|
store.formVisible = false;
|
||||||
|
store.fetchRecords()
|
||||||
|
}
|
||||||
|
}, () => this.setState({loading: false}))
|
||||||
|
};
|
||||||
|
|
||||||
|
handleConfirm = (formData) => {
|
||||||
|
if (this.state.password) {
|
||||||
|
formData['password'] = this.state.password;
|
||||||
|
return http.post('/api/host/', formData).then(res => {
|
||||||
|
message.success('验证成功');
|
||||||
|
store.formVisible = false;
|
||||||
|
store.fetchRecords()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
message.error('请输入授权密码')
|
||||||
|
};
|
||||||
|
|
||||||
|
confirmForm = (username) => {
|
||||||
|
return (
|
||||||
|
<Form>
|
||||||
|
<Form.Item required label="授权密码" help={`用户 ${username} 的密码, 该密码仅做首次验证使用,不会存储该密码。`}>
|
||||||
|
<Input.Password onChange={val => this.setState({password: val.target.value})}/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const info = store.record;
|
||||||
|
const {getFieldDecorator} = this.props.form;
|
||||||
|
const itemLayout = {
|
||||||
|
labelCol: {span: 6},
|
||||||
|
wrapperCol: {span: 14}
|
||||||
|
};
|
||||||
|
const itemTailLayout = {
|
||||||
|
labelCol: {span: 6},
|
||||||
|
wrapperCol: {span: 14, offset: 6}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
visible
|
||||||
|
width={800}
|
||||||
|
maskClosable={false}
|
||||||
|
title={store.record.id ? '编辑主机' : '新建主机'}
|
||||||
|
okText="验证"
|
||||||
|
onCancel={() => store.formVisible = false}
|
||||||
|
confirmLoading={this.state.loading}
|
||||||
|
onOk={this.handleSubmit}>
|
||||||
|
<Form>
|
||||||
|
<Form.Item {...itemLayout} required label="主机类别">
|
||||||
|
{getFieldDecorator('zone', {initialValue: info['zone']})(
|
||||||
|
<Input placeholder="请输入主机类别/区域/分组"/>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item {...itemLayout} required label="主机别名">
|
||||||
|
{getFieldDecorator('name', {initialValue: info['name']})(
|
||||||
|
<Input placeholder="请输入主机别名"/>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item {...itemLayout} required label="连接地址" style={{marginBottom: 0}}>
|
||||||
|
<Form.Item style={{display: 'inline-block', width: 'calc(30%)'}}>
|
||||||
|
{getFieldDecorator('username', {initialValue: info['username']})(
|
||||||
|
<Input addonBefore="ssh" placeholder="用户名"/>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item style={{display: 'inline-block', width: 'calc(40%)'}}>
|
||||||
|
{getFieldDecorator('hostname', {initialValue: info['hostname']})(
|
||||||
|
<Input addonBefore="@" placeholder="主机名/IP"/>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item style={{display: 'inline-block', width: 'calc(30%)'}}>
|
||||||
|
{getFieldDecorator('port', {initialValue: info['port']})(
|
||||||
|
<Input addonBefore="-p" placeholder="端口"/>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item {...itemLayout} label="备注信息">
|
||||||
|
{getFieldDecorator('desc', {initialValue: info['desc']})(
|
||||||
|
<Input.TextArea placeholder="请输入主机备注信息"/>
|
||||||
|
)}
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item {...itemTailLayout}>
|
||||||
|
<span role="img" aria-label="notice">⚠️ 首次验证时需要输入登录用户名对应的密码,但不会存储该密码。</span>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Form.create()(ComForm)
|
|
@ -0,0 +1,73 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { Table, Divider, Modal, message } from 'antd';
|
||||||
|
import { LinkButton } from 'components';
|
||||||
|
import ComForm from './Form';
|
||||||
|
import http from 'libs/http';
|
||||||
|
import store from './store';
|
||||||
|
|
||||||
|
@observer
|
||||||
|
class ComTable extends React.Component {
|
||||||
|
componentDidMount() {
|
||||||
|
store.fetchRecords()
|
||||||
|
}
|
||||||
|
|
||||||
|
columns = [{
|
||||||
|
title: '序号',
|
||||||
|
key: 'series',
|
||||||
|
render: (_, __, index) => index + 1
|
||||||
|
}, {
|
||||||
|
title: '类别',
|
||||||
|
dataIndex: 'zone',
|
||||||
|
}, {
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
}, {
|
||||||
|
title: '主机',
|
||||||
|
dataIndex: 'hostname',
|
||||||
|
}, {
|
||||||
|
title: '端口',
|
||||||
|
dataIndex: 'port'
|
||||||
|
}, {
|
||||||
|
title: '备注',
|
||||||
|
dataIndex: 'desc'
|
||||||
|
}, {
|
||||||
|
title: '操作',
|
||||||
|
render: info => (
|
||||||
|
<span>
|
||||||
|
<LinkButton onClick={() => store.showForm(info)}>编辑</LinkButton>
|
||||||
|
<Divider type="vertical"/>
|
||||||
|
<LinkButton onClick={() => this.handleDelete(info)}>删除</LinkButton>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}];
|
||||||
|
|
||||||
|
handleDelete = (text) => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '删除确认',
|
||||||
|
content: `确定要删除【${text['name']}】?`,
|
||||||
|
onOk: () => {
|
||||||
|
return http.delete('/api/host/', {params: {id: text.id}})
|
||||||
|
.then(() => {
|
||||||
|
message.success('删除成功');
|
||||||
|
store.fetchRecords()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let data = store.records;
|
||||||
|
if (store.f_name) {
|
||||||
|
data = data.filter(item => item['name'].toLowerCase().includes(store.f_name.toLowerCase()))
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Table rowKey="id" loading={store.isFetching} dataSource={data} columns={this.columns}/>
|
||||||
|
{store.formVisible && <ComForm/>}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ComTable
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Card, Input, Button } from 'antd';
|
||||||
|
import { SearchForm } from 'components';
|
||||||
|
import ComTable from './Table';
|
||||||
|
import store from './store';
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<SearchForm>
|
||||||
|
<SearchForm.Item span={8} title="主机别名">
|
||||||
|
<Input onChange={e => store.f_name = e.target.value} placeholder="请输入"/>
|
||||||
|
</SearchForm.Item>
|
||||||
|
<SearchForm.Item span={8}>
|
||||||
|
<Button type="primary" icon="sync" onClick={store.fetchRecords}>刷新</Button>
|
||||||
|
</SearchForm.Item>
|
||||||
|
</SearchForm>
|
||||||
|
<div style={{marginBottom: 16}}>
|
||||||
|
<Button type="primary" icon="plus" onClick={() => store.showForm()}>新建</Button>
|
||||||
|
</div>
|
||||||
|
<ComTable/>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { makeRoute } from "../../libs/router";
|
||||||
|
import Index from './index';
|
||||||
|
|
||||||
|
|
||||||
|
export default [
|
||||||
|
makeRoute('', Index),
|
||||||
|
]
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { observable } from "mobx";
|
||||||
|
import http from 'libs/http';
|
||||||
|
|
||||||
|
class Store {
|
||||||
|
@observable records = [];
|
||||||
|
@observable record = {};
|
||||||
|
@observable isFetching = false;
|
||||||
|
@observable formVisible = false;
|
||||||
|
|
||||||
|
@observable f_name;
|
||||||
|
@observable f_status;
|
||||||
|
|
||||||
|
fetchRecords = () => {
|
||||||
|
this.isFetching = true;
|
||||||
|
http.get('/api/host/')
|
||||||
|
.then(res => this.records = res)
|
||||||
|
.finally(() => this.isFetching = false)
|
||||||
|
};
|
||||||
|
|
||||||
|
showForm = (info = {}) => {
|
||||||
|
this.formVisible = true;
|
||||||
|
this.record = info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Store()
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { Table, Divider, Modal, Badge, message, Button } from 'antd';
|
import { Table, Divider, Modal, Badge, message } from 'antd';
|
||||||
import ComForm from './Form';
|
import ComForm from './Form';
|
||||||
import http from 'libs/http';
|
import http from 'libs/http';
|
||||||
import store from './store';
|
import store from './store';
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import { makeModuleRoute } from "./libs/router";
|
import { makeModuleRoute } from "./libs/router";
|
||||||
|
|
||||||
import homeRoutes from './pages/home/routes';
|
import homeRoutes from './pages/home/routes';
|
||||||
|
import hostRoutes from './pages/host/routes';
|
||||||
import systemRoutes from './pages/system/routes';
|
import systemRoutes from './pages/system/routes';
|
||||||
|
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
makeModuleRoute('/home', homeRoutes),
|
makeModuleRoute('/home', homeRoutes),
|
||||||
|
makeModuleRoute('/host', hostRoutes),
|
||||||
makeModuleRoute('/system', systemRoutes),
|
makeModuleRoute('/system', systemRoutes),
|
||||||
]
|
]
|
Loading…
Reference in New Issue