A web add monitor page
This commit is contained in:
parent
2054c35e23
commit
b28c24e734
|
@ -8,6 +8,7 @@ export default [
|
|||
]
|
||||
},
|
||||
{icon: 'schedule', title: '任务计划', path: '/schedule'},
|
||||
{icon: 'monitor', title: '监控中心', path: '/monitor'},
|
||||
{
|
||||
icon: 'setting', title: '系统管理', child: [
|
||||
{title: '账户管理', path: '/system/account'},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = () => (
|
||||
<Select style={{width: 90}} value={this.state.sitePrefix} onChange={v => this.setState({sitePrefix: v})}>
|
||||
<Select.Option value="http://">http://</Select.Option>
|
||||
<Select.Option value="https://">https://</Select.Option>
|
||||
</Select>
|
||||
);
|
||||
|
||||
render() {
|
||||
const info = store.record;
|
||||
const {loading, extra, addr, showTmp} = this.state;
|
||||
const {getFieldDecorator} = this.props.form;
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
width={800}
|
||||
maskClosable={false}
|
||||
title={store.record.id ? '编辑任务' : '新建任务'}
|
||||
onCancel={() => store.formVisible = false}
|
||||
confirmLoading={loading}
|
||||
onOk={this.handleSubmit}>
|
||||
<Form>
|
||||
<Form.Item {...this.itemLayout} label="监控类型">
|
||||
{getFieldDecorator('type', {initialValue: info['type'] || '1'})(
|
||||
<Select placeholder="请选择监控类型">
|
||||
<Select.Option value="1">站点检测</Select.Option>
|
||||
<Select.Option value="2">端口检测</Select.Option>
|
||||
<Select.Option value="3">进程检测</Select.Option>
|
||||
<Select.Option value="4">自定义脚本</Select.Option>
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...this.itemLayout} required label="任务名称">
|
||||
{getFieldDecorator('name', {initialValue: info['name']})(
|
||||
<Input placeholder="请输入任务名称"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...this.itemLayout} required label="监控地址" style={this.getStyle('1')}>
|
||||
<Input value={addr['1']} addonBefore={this.siteBefore()} placeholder="请输入监控地址"
|
||||
onChange={e => this.handleAddr('1', e)}/>
|
||||
</Form.Item>
|
||||
<Form.Item {...this.itemLayout} required label="监控地址" style={this.getStyle('2')}>
|
||||
<Input value={addr['2']} placeholder="请输入监控地址(IP/域名)" onChange={e => this.handleAddr('2', e)}/>
|
||||
</Form.Item>
|
||||
<Form.Item {...this.itemLayout} required label="监控主机" style={this.getStyle('34')}>
|
||||
<Select value={addr['3']} placeholder="请选择主机" onChange={v => this.handleAddr('3', v)}>
|
||||
{hostStore.records.map(item => (
|
||||
<Select.Option value={String(item.id)} key={item.id}>{item.name}({item.hostname}:{item.port})</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item {...this.itemLayout} required label="检测端口" style={this.getStyle('2')}>
|
||||
<Input value={extra['2']} placeholder="请输入端口号" onChange={e => this.handleExtra('2', e)}/>
|
||||
</Form.Item>
|
||||
<Form.Item {...this.itemLayout} required label="进程名称" style={this.getStyle('3')}>
|
||||
<Input value={extra['3']} placeholder="请输入进程名称" onChange={e => this.handleExtra('3', e)}/>
|
||||
</Form.Item>
|
||||
<Form.Item {...this.itemLayout} required label="脚本内容" style={this.getStyle('4')}
|
||||
extra={<LinkButton onClick={() => this.setState({showTmp: true})}>从模板添加</LinkButton>}>
|
||||
<SHEditor value={extra['4']} height="200px" onChange={e => this.handleExtra('4', e)}/>
|
||||
</Form.Item>
|
||||
<Form.Item {...this.itemLayout} label="监控频率">
|
||||
{getFieldDecorator('rate', {initialValue: info['rate'] || 5})(
|
||||
<Radio.Group>
|
||||
<Radio value={1}>1分钟</Radio>
|
||||
<Radio value={5}>5分钟</Radio>
|
||||
<Radio value={15}>15分钟</Radio>
|
||||
<Radio value={30}>30分钟</Radio>
|
||||
<Radio value={60}>60分钟</Radio>
|
||||
</Radio.Group>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...this.itemLayout} label="报警阈值" help="连续几次超过阈值后报警">
|
||||
{getFieldDecorator('threshold', {initialValue: info['threshold'] || 3})(
|
||||
<Radio.Group>
|
||||
<Radio value={1}>1次</Radio>
|
||||
<Radio value={2}>2次</Radio>
|
||||
<Radio value={3}>3次</Radio>
|
||||
<Radio value={4}>4次</Radio>
|
||||
<Radio value={5}>5次</Radio>
|
||||
</Radio.Group>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...this.itemLayout} label="通道沉默">
|
||||
{getFieldDecorator('quiet', {initialValue: info['quiet'] || 24 * 60})(
|
||||
<Select placeholder="请选择">
|
||||
<Select.Option value={5}>5分钟</Select.Option>
|
||||
<Select.Option value={10}>10分钟</Select.Option>
|
||||
<Select.Option value={15}>15分钟</Select.Option>
|
||||
<Select.Option value={30}>30分钟</Select.Option>
|
||||
<Select.Option value={60}>60分钟</Select.Option>
|
||||
<Select.Option value={3 * 60}>3小时</Select.Option>
|
||||
<Select.Option value={6 * 60}>6小时</Select.Option>
|
||||
<Select.Option value={12 * 60}>12小时</Select.Option>
|
||||
<Select.Option value={24 * 60}>24小时</Select.Option>
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...this.itemLayout} label="备注信息">
|
||||
{getFieldDecorator('desc', {initialValue: info['desc']})(
|
||||
<Input.TextArea placeholder="请输入备注信息"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
{showTmp && <TemplateSelector
|
||||
onOk={command => this.handleExtra('4', command)}
|
||||
onCancel={() => this.setState({showTmp: false})}/>}
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Form.create()(ComForm)
|
|
@ -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 => (
|
||||
<span>
|
||||
<LinkButton onClick={() => this.handleActive(info)}>{info['is_active'] ? '禁用' : '启用'}</LinkButton>
|
||||
<Divider type="vertical"/>
|
||||
<LinkButton onClick={() => store.showForm(info)}>编辑</LinkButton>
|
||||
<Divider type="vertical"/>
|
||||
<LinkButton onClick={() => this.handleDelete(info)}>删除</LinkButton>
|
||||
</span>
|
||||
),
|
||||
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 (
|
||||
<React.Fragment>
|
||||
<Table rowKey="id" loading={store.isFetching} dataSource={data} columns={this.columns}/>
|
||||
{store.formVisible && <ComForm/>}
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default ComTable
|
|
@ -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 (
|
||||
<Card>
|
||||
<SearchForm>
|
||||
<SearchForm.Item span={8} title="任务名称">
|
||||
<Input onChange={e => store.f_name = e.target.value} placeholder="请输入"/>
|
||||
</SearchForm.Item>
|
||||
<SearchForm.Item span={8} title="任务状态">
|
||||
<Select allowClear onChange={v => store.f_status = v} placeholder="请选择">
|
||||
<Select.Option value="true">正常</Select.Option>
|
||||
<Select.Option value="false">禁用</Select.Option>
|
||||
</Select>
|
||||
</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/monitor/')
|
||||
.then(res => this.records = res)
|
||||
.finally(() => this.isFetching = false)
|
||||
};
|
||||
|
||||
showForm = (info = {}) => {
|
||||
this.formVisible = true;
|
||||
this.record = info
|
||||
}
|
||||
}
|
||||
|
||||
export default new Store()
|
|
@ -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),
|
||||
]
|
Loading…
Reference in New Issue