diff --git a/spug_web/src/menus.js b/spug_web/src/menus.js
index 0eefd5b..9b44f6c 100644
--- a/spug_web/src/menus.js
+++ b/spug_web/src/menus.js
@@ -8,6 +8,7 @@ export default [
]
},
{icon: 'schedule', title: '任务计划', path: '/schedule'},
+ {icon: 'monitor', title: '监控中心', path: '/monitor'},
{
icon: 'setting', title: '系统管理', child: [
{title: '账户管理', path: '/system/account'},
diff --git a/spug_web/src/pages/host/store.js b/spug_web/src/pages/host/store.js
index f2c05bf..0092b4f 100644
--- a/spug_web/src/pages/host/store.js
+++ b/spug_web/src/pages/host/store.js
@@ -13,7 +13,7 @@ class Store {
fetchRecords = () => {
this.isFetching = true;
- http.get('/api/host/')
+ return http.get('/api/host/')
.then(({hosts, zones}) => {
this.records = hosts;
this.zones = zones;
diff --git a/spug_web/src/pages/monitor/Form.js b/spug_web/src/pages/monitor/Form.js
new file mode 100644
index 0000000..a897140
--- /dev/null
+++ b/spug_web/src/pages/monitor/Form.js
@@ -0,0 +1,189 @@
+import React from 'react';
+import { observer } from 'mobx-react';
+import { Modal, Form, Input, Select, Radio, message } from 'antd';
+import TemplateSelector from '../exec/task/TemplateSelector';
+import { LinkButton, SHEditor } from 'components';
+import http from 'libs/http';
+import store from './store';
+import hostStore from '../host/store';
+
+@observer
+class ComForm extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ loading: false,
+ sitePrefix: 'http://',
+ extra: {[store.record.type]: store.record.extra},
+ addr: {},
+ showTmp: false,
+ }
+ }
+
+ componentDidMount() {
+ let [sitePrefix, value] = ['http://', ''];
+ if (store.record.type === '1') {
+ if (store.record.addr.includes('http://')) {
+ value = store.record.addr.replace('http://', '')
+ } else {
+ sitePrefix = 'https://';
+ value = store.record.addr.replace('https://', '')
+ }
+ this.setState({sitePrefix, addr: {'1': value}})
+ } else if ('34'.includes(store.record.type)) {
+ this.setState({addr: {'3': store.record.addr, '4': store.record.addr}})
+ } else {
+ this.setState({addr: {[store.record.type]: store.record.addr}})
+ }
+ }
+
+ handleSubmit = () => {
+ this.setState({loading: true});
+ const formData = this.props.form.getFieldsValue();
+ const type = formData['type'];
+ formData['id'] = store.record.id;
+ formData['extra'] = this.state.extra[type];
+ formData['addr'] = type === '1' ? this.state.sitePrefix + this.state.addr[type] : this.state.addr[type];
+ http.post('/api/monitor/', formData)
+ .then(() => {
+ message.success('操作成功');
+ store.formVisible = false;
+ store.fetchRecords()
+ }, () => this.setState({loading: false}))
+ };
+
+ itemLayout = {
+ labelCol: {span: 6},
+ wrapperCol: {span: 14}
+ };
+
+ getStyle = (t) => {
+ const type = this.props.form.getFieldValue('type');
+ return t.indexOf(type) !== -1 ? {display: 'block'} : {display: 'none'}
+ };
+
+ handleExtra = (t, e) => {
+ const value = t === '4' ? e : e.target.value;
+ this.setState({extra: Object.assign({}, this.state.extra, {[t]: value})})
+ };
+
+ handleAddr = (t, e) => {
+ if (t === '3') {
+ this.setState({addr: Object.assign({}, this.state.addr, {'3': e, '4': e})})
+ } else {
+ this.setState({addr: Object.assign({}, this.state.addr, {[t]: e.target.value})})
+ }
+ };
+
+ siteBefore = () => (
+
+ );
+
+ render() {
+ const info = store.record;
+ const {loading, extra, addr, showTmp} = this.state;
+ const {getFieldDecorator} = this.props.form;
+ return (
+ store.formVisible = false}
+ confirmLoading={loading}
+ onOk={this.handleSubmit}>
+
+ {getFieldDecorator('type', {initialValue: info['type'] || '1'})(
+
+ )}
+
+
+ {getFieldDecorator('name', {initialValue: info['name']})(
+
+ )}
+
+
+ this.handleAddr('1', e)}/>
+
+
+ this.handleAddr('2', e)}/>
+
+
+
+
+
+ this.handleExtra('2', e)}/>
+
+
+ this.handleExtra('3', e)}/>
+
+ this.setState({showTmp: true})}>从模板添加}>
+ this.handleExtra('4', e)}/>
+
+
+ {getFieldDecorator('rate', {initialValue: info['rate'] || 5})(
+
+ 1分钟
+ 5分钟
+ 15分钟
+ 30分钟
+ 60分钟
+
+ )}
+
+
+ {getFieldDecorator('threshold', {initialValue: info['threshold'] || 3})(
+
+ 1次
+ 2次
+ 3次
+ 4次
+ 5次
+
+ )}
+
+
+ {getFieldDecorator('quiet', {initialValue: info['quiet'] || 24 * 60})(
+
+ )}
+
+
+ {getFieldDecorator('desc', {initialValue: info['desc']})(
+
+ )}
+
+
+ {showTmp && this.handleExtra('4', command)}
+ onCancel={() => this.setState({showTmp: false})}/>}
+
+ )
+ }
+}
+
+export default Form.create()(ComForm)
\ No newline at end of file
diff --git a/spug_web/src/pages/monitor/Table.js b/spug_web/src/pages/monitor/Table.js
new file mode 100644
index 0000000..832bc3a
--- /dev/null
+++ b/spug_web/src/pages/monitor/Table.js
@@ -0,0 +1,124 @@
+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';
+import hostStore from '../host/store';
+import lds from 'lodash';
+
+@observer
+class ComTable extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ hosts: {}
+ }
+ }
+
+ componentDidMount() {
+ store.fetchRecords();
+ if (hostStore.records.length === 0) {
+ hostStore.fetchRecords().then(() => {
+ const tmp = {};
+ for (let item of hostStore.records) {
+ tmp[item.id] = item
+ }
+ this.setState({hosts: tmp})
+ })
+ }
+ }
+
+ columns = [{
+ title: '序号',
+ key: 'series',
+ render: (_, __, index) => index + 1,
+ width: 80
+ }, {
+ title: '任务名称',
+ dataIndex: 'name',
+ }, {
+ title: '类型',
+ dataIndex: 'type_alias',
+ }, {
+ title: '地址',
+ render: info => {
+ if ('34'.includes(info.type)) {
+ return lds.get(this.state.hosts, `${info.addr}.name`)
+ } else {
+ return info.addr
+ }
+ },
+ ellipsis: true
+ }, {
+ title: '频率',
+ dataIndex: 'rate',
+ render: value => `${value}分钟`
+ }, {
+ title: '状态',
+ dataIndex: 'xx'
+ }, {
+ title: '备注',
+ dataIndex: 'desc',
+ ellipsis: true
+ }, {
+ title: '操作',
+ render: info => (
+
+ this.handleActive(info)}>{info['is_active'] ? '禁用' : '启用'}
+
+ store.showForm(info)}>编辑
+
+ this.handleDelete(info)}>删除
+
+ ),
+ width: 180
+ }];
+
+ handleActive = (text) => {
+ Modal.confirm({
+ title: '操作确认',
+ content: `确定要${text['is_active'] ? '禁用' : '启用'}【${text['nickname']}】?`,
+ onOk: () => {
+ return http.patch(`/api/monitor/`, {id: text.id, is_active: !text['is_active']})
+ .then(() => {
+ message.success('操作成功');
+ store.fetchRecords()
+ })
+ }
+ })
+ };
+
+ handleDelete = (text) => {
+ Modal.confirm({
+ title: '删除确认',
+ content: `确定要删除【${text['name']}】?`,
+ onOk: () => {
+ return http.delete('/api/monitor/', {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()))
+ }
+ if (store.f_status) {
+ data = data.filter(item => String(item['is_active']) === store.f_status)
+ }
+ return (
+
+
+ {store.formVisible && }
+
+ )
+ }
+}
+
+export default ComTable
\ No newline at end of file
diff --git a/spug_web/src/pages/monitor/index.js b/spug_web/src/pages/monitor/index.js
new file mode 100644
index 0000000..c8c1261
--- /dev/null
+++ b/spug_web/src/pages/monitor/index.js
@@ -0,0 +1,30 @@
+import React from 'react';
+import { Card, Input, Select, 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/monitor/routes.js b/spug_web/src/pages/monitor/routes.js
new file mode 100644
index 0000000..6e03f0b
--- /dev/null
+++ b/spug_web/src/pages/monitor/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/monitor/store.js b/spug_web/src/pages/monitor/store.js
new file mode 100644
index 0000000..9371dd9
--- /dev/null
+++ b/spug_web/src/pages/monitor/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/monitor/')
+ .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/routes.js b/spug_web/src/routes.js
index 5b77924..8406ac7 100644
--- a/spug_web/src/routes.js
+++ b/spug_web/src/routes.js
@@ -5,6 +5,7 @@ import hostRoutes from './pages/host/routes';
import systemRoutes from './pages/system/routes';
import execRoutes from './pages/exec/routes';
import scheduleRoutes from './pages/schedule/routes';
+import monitorRoutes from './pages/monitor/routes';
export default [
@@ -13,4 +14,5 @@ export default [
makeModuleRoute('/system', systemRoutes),
makeModuleRoute('/exec', execRoutes),
makeModuleRoute('/schedule', scheduleRoutes),
+ makeModuleRoute('/monitor', monitorRoutes),
]
\ No newline at end of file