diff --git a/spug_web/src/components/LinkButton.js b/spug_web/src/components/LinkButton.js
new file mode 100644
index 0000000..4eb153e
--- /dev/null
+++ b/spug_web/src/components/LinkButton.js
@@ -0,0 +1,7 @@
+import React from 'react';
+import { Button } from 'antd';
+
+
+export default function LinkButton(props) {
+ return
+}
\ No newline at end of file
diff --git a/spug_web/src/components/index.js b/spug_web/src/components/index.js
index ae41a1f..dcc34c1 100644
--- a/spug_web/src/components/index.js
+++ b/spug_web/src/components/index.js
@@ -1,7 +1,9 @@
import StatisticsCard from './StatisticsCard';
import SearchForm from './SearchForm';
+import LinkButton from './LinkButton';
export {
StatisticsCard,
SearchForm,
+ LinkButton,
}
\ No newline at end of file
diff --git a/spug_web/src/index.css b/spug_web/src/index.css
index 07afb21..4e61174 100644
--- a/spug_web/src/index.css
+++ b/spug_web/src/index.css
@@ -16,6 +16,5 @@ code {
/* Common CSS style */
.span-button {
- cursor: pointer;
- color: #1890ff;
+ padding: 0;
}
\ No newline at end of file
diff --git a/spug_web/src/menus.js b/spug_web/src/menus.js
index cab947d..3fd57dc 100644
--- a/spug_web/src/menus.js
+++ b/spug_web/src/menus.js
@@ -1,5 +1,6 @@
export default [
{icon: 'desktop', title: '工作台', path: '/home'},
+ {icon: 'cloud-server', title: '主机管理', path: '/host'},
{
icon: 'setting', title: '系统管理', child: [
{title: '账户管理', path: '/system/account'},
diff --git a/spug_web/src/pages/host/Form.js b/spug_web/src/pages/host/Form.js
new file mode 100644
index 0000000..9252775
--- /dev/null
+++ b/spug_web/src/pages/host/Form.js
@@ -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 (
+
+ this.setState({password: val.target.value})}/>
+
+
+ )
+ };
+
+ 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 (
+ store.formVisible = false}
+ confirmLoading={this.state.loading}
+ onOk={this.handleSubmit}>
+
+ {getFieldDecorator('zone', {initialValue: info['zone']})(
+
+ )}
+
+
+ {getFieldDecorator('name', {initialValue: info['name']})(
+
+ )}
+
+
+
+ {getFieldDecorator('username', {initialValue: info['username']})(
+
+ )}
+
+
+ {getFieldDecorator('hostname', {initialValue: info['hostname']})(
+
+ )}
+
+
+ {getFieldDecorator('port', {initialValue: info['port']})(
+
+ )}
+
+
+
+ {getFieldDecorator('desc', {initialValue: info['desc']})(
+
+ )}
+
+
+ ⚠️ 首次验证时需要输入登录用户名对应的密码,但不会存储该密码。
+
+
+
+ )
+ }
+}
+
+export default Form.create()(ComForm)
\ No newline at end of file
diff --git a/spug_web/src/pages/host/Table.js b/spug_web/src/pages/host/Table.js
new file mode 100644
index 0000000..3596f1d
--- /dev/null
+++ b/spug_web/src/pages/host/Table.js
@@ -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 => (
+
+ store.showForm(info)}>编辑
+
+ this.handleDelete(info)}>删除
+
+ )
+ }];
+
+ 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 (
+
+
+ {store.formVisible && }
+
+ )
+ }
+}
+
+export default ComTable
\ No newline at end of file
diff --git a/spug_web/src/pages/host/index.js b/spug_web/src/pages/host/index.js
new file mode 100644
index 0000000..f458f8e
--- /dev/null
+++ b/spug_web/src/pages/host/index.js
@@ -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 (
+
+
+
+ store.f_name = e.target.value} placeholder="请输入"/>
+
+
+
+
+
+
+
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/spug_web/src/pages/host/routes.js b/spug_web/src/pages/host/routes.js
new file mode 100644
index 0000000..6e03f0b
--- /dev/null
+++ b/spug_web/src/pages/host/routes.js
@@ -0,0 +1,7 @@
+import { makeRoute } from "../../libs/router";
+import Index from './index';
+
+
+export default [
+ makeRoute('', Index),
+]
\ No newline at end of file
diff --git a/spug_web/src/pages/host/store.js b/spug_web/src/pages/host/store.js
new file mode 100644
index 0000000..83299a3
--- /dev/null
+++ b/spug_web/src/pages/host/store.js
@@ -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()
\ No newline at end of file
diff --git a/spug_web/src/pages/system/account/Table.js b/spug_web/src/pages/system/account/Table.js
index ea801e1..92a3581 100644
--- a/spug_web/src/pages/system/account/Table.js
+++ b/spug_web/src/pages/system/account/Table.js
@@ -1,6 +1,6 @@
import React from '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 http from 'libs/http';
import store from './store';
diff --git a/spug_web/src/routes.js b/spug_web/src/routes.js
index 3089dea..66f9ebc 100644
--- a/spug_web/src/routes.js
+++ b/spug_web/src/routes.js
@@ -1,10 +1,12 @@
import { makeModuleRoute } from "./libs/router";
import homeRoutes from './pages/home/routes';
+import hostRoutes from './pages/host/routes';
import systemRoutes from './pages/system/routes';
export default [
makeModuleRoute('/home', homeRoutes),
+ makeModuleRoute('/host', hostRoutes),
makeModuleRoute('/system', systemRoutes),
]
\ No newline at end of file