From b28c24e734e84650ecd56773d1197d9f3576e409 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E4=BA=8C=E7=8C=9B?= Date: Mon, 2 Dec 2019 12:38:40 +0800 Subject: [PATCH] A web add monitor page --- spug_web/src/menus.js | 1 + spug_web/src/pages/host/store.js | 2 +- spug_web/src/pages/monitor/Form.js | 189 +++++++++++++++++++++++++++ spug_web/src/pages/monitor/Table.js | 124 ++++++++++++++++++ spug_web/src/pages/monitor/index.js | 30 +++++ spug_web/src/pages/monitor/routes.js | 7 + spug_web/src/pages/monitor/store.js | 26 ++++ spug_web/src/routes.js | 2 + 8 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 spug_web/src/pages/monitor/Form.js create mode 100644 spug_web/src/pages/monitor/Table.js create mode 100644 spug_web/src/pages/monitor/index.js create mode 100644 spug_web/src/pages/monitor/routes.js create mode 100644 spug_web/src/pages/monitor/store.js 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