mirror of https://github.com/openspug/spug
style migrate v3
parent
2ccf04e387
commit
55b4ea6b6f
|
@ -5,307 +5,27 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Modal, Form, Input, Select, Radio, message, Steps, Button, Transfer, Checkbox } from 'antd';
|
||||
import TemplateSelector from '../exec/task/TemplateSelector';
|
||||
import { LinkButton, ACEditor } from 'components';
|
||||
import { http, cleanCommand, hasHostPermission } from 'libs';
|
||||
import { Modal, Steps } from 'antd';
|
||||
import Step1 from './Step1';
|
||||
import Step2 from './Step2';
|
||||
import store from './store';
|
||||
import hostStore from '../host/store';
|
||||
import groupStore from '../alarm/group/store';
|
||||
import styles from './index.module.css';
|
||||
import lds from 'lodash';
|
||||
|
||||
@observer
|
||||
class ComForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.fieldMap = {
|
||||
'1': ['domain'],
|
||||
'2': ['addr', 'port'],
|
||||
'3': ['host', 'process'],
|
||||
'4': ['host', 'command'],
|
||||
'5': ['addr'],
|
||||
}
|
||||
this.modeOptions = [
|
||||
{label: '微信', 'value': '1'},
|
||||
{label: '短信', 'value': '2', disabled: true},
|
||||
{label: '钉钉', 'value': '3'},
|
||||
{label: '邮件', 'value': '4'},
|
||||
{label: '企业微信', 'value': '5'},
|
||||
]
|
||||
this.helpMap = {
|
||||
'1': '返回HTTP状态码200-399则判定为正常,其他为异常。',
|
||||
'4': '脚本执行退出状态码为 0 则判定为正常,其他为异常。'
|
||||
}
|
||||
this.state = {
|
||||
loading: false,
|
||||
sitePrefix: 'http://',
|
||||
domain: undefined,
|
||||
addr: undefined,
|
||||
port: undefined,
|
||||
host: undefined,
|
||||
process: undefined,
|
||||
command: '',
|
||||
showTmp: false,
|
||||
page: 0,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {type, addr, extra} = store.record;
|
||||
switch (type) {
|
||||
case '1':
|
||||
if (addr.startsWith('http://')) {
|
||||
this.setState({sitePrefix: 'http://', domain: addr.replace('http://', '')})
|
||||
} else {
|
||||
this.setState({sitePrefix: 'https://', domain: addr.replace('https://', '')})
|
||||
}
|
||||
break;
|
||||
case '2':
|
||||
this.setState({addr, port: extra});
|
||||
break;
|
||||
case '3':
|
||||
this.setState({host: addr, process: extra});
|
||||
break;
|
||||
case '4':
|
||||
this.setState({host: addr, command: extra});
|
||||
break;
|
||||
case '5':
|
||||
this.setState({addr});
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
_getFieldsValue = (type) => {
|
||||
const {sitePrefix, domain, addr, host, port, command, process} = this.state;
|
||||
switch (type) {
|
||||
case '1':
|
||||
return {addr: sitePrefix + domain}
|
||||
case '2':
|
||||
return {addr, extra: port}
|
||||
case '3':
|
||||
return {addr: host, extra: process}
|
||||
case '4':
|
||||
return {addr: host, extra: command}
|
||||
case '5':
|
||||
return {addr}
|
||||
default:
|
||||
throw Error('unknown type')
|
||||
}
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
this.setState({loading: true});
|
||||
const formData = this.props.form.getFieldsValue();
|
||||
const type = formData['type'];
|
||||
formData['id'] = store.record.id;
|
||||
Object.assign(formData, this._getFieldsValue(type))
|
||||
http.post('/api/monitor/', formData)
|
||||
.then(() => {
|
||||
message.success('操作成功');
|
||||
store.formVisible = false;
|
||||
store.fetchRecords()
|
||||
}, () => this.setState({loading: false}))
|
||||
};
|
||||
|
||||
handleTest = () => {
|
||||
this.setState({loading: true});
|
||||
const type = this.props.form.getFieldValue('type');
|
||||
const formData = this._getFieldsValue(type);
|
||||
formData['type'] = type;
|
||||
http.post('/api/monitor/test/', formData, {timeout: 120000})
|
||||
.then(res => {
|
||||
if (res.is_success) {
|
||||
Modal.success({content: res.message})
|
||||
} else {
|
||||
Modal.warning({content: res.message})
|
||||
}
|
||||
})
|
||||
.finally(() => this.setState({loading: false}))
|
||||
}
|
||||
|
||||
getStyle = (t) => {
|
||||
const type = this.props.form.getFieldValue('type');
|
||||
return this.fieldMap[type].includes(t) ? {display: 'block'} : {display: 'none'}
|
||||
};
|
||||
|
||||
handleInput = (key, value) => {
|
||||
this.setState({[key]: 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>
|
||||
);
|
||||
|
||||
verifyButtonStatus = () => {
|
||||
const data = this.props.form.getFieldsValue();
|
||||
const {notify_grp, notify_mode, type, name} = data;
|
||||
const fields = Object.values(lds.pick(this.state, this.fieldMap[type])).filter(x => x)
|
||||
const b1 = name && fields.length === this.fieldMap[type].length
|
||||
const b2 = notify_grp && notify_grp.length && notify_mode && notify_mode.length;
|
||||
return [b1, b2];
|
||||
};
|
||||
|
||||
render() {
|
||||
const info = store.record;
|
||||
const {loading, domain, host, port, process, command, addr, showTmp, page} = this.state;
|
||||
const {getFieldDecorator, getFieldValue} = this.props.form;
|
||||
const [b1, b2] = this.verifyButtonStatus();
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
width={800}
|
||||
maskClosable={false}
|
||||
title={store.record.id ? '编辑任务' : '新建任务'}
|
||||
onCancel={() => store.formVisible = false}
|
||||
footer={null}>
|
||||
<Steps current={page} className={styles.steps}>
|
||||
<Steps.Step key={0} title="创建任务"/>
|
||||
<Steps.Step key={1} title="设置规则"/>
|
||||
</Steps>
|
||||
<Form labelCol={{span: 6}} wrapperCol={{span: 14}}>
|
||||
<div style={{display: page === 0 ? 'block' : 'none'}}>
|
||||
<Form.Item label="监控类型" help={this.helpMap[getFieldValue('type') || '1']}>
|
||||
{getFieldDecorator('type', {initialValue: info['type'] || '1'})(
|
||||
<Select placeholder="请选择监控类型">
|
||||
<Select.Option value="1">站点检测</Select.Option>
|
||||
<Select.Option value="2">端口检测</Select.Option>
|
||||
<Select.Option value="5">Ping检测</Select.Option>
|
||||
<Select.Option value="3">进程检测</Select.Option>
|
||||
<Select.Option value="4">自定义脚本</Select.Option>
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item required label="任务名称">
|
||||
{getFieldDecorator('name', {initialValue: info['name']})(
|
||||
<Input placeholder="请输入任务名称"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item required label="监控地址" style={this.getStyle('domain')}>
|
||||
<Input value={domain} addonBefore={this.siteBefore()} placeholder="请输入监控地址"
|
||||
onChange={e => this.handleInput('domain', e.target.value)}/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="监控地址" style={this.getStyle('addr')}>
|
||||
<Input value={addr} placeholder="请输入监控地址(IP/域名)"
|
||||
onChange={e => this.handleInput('addr', e.target.value)}/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="监控主机" style={this.getStyle('host')}>
|
||||
<Select
|
||||
showSearch
|
||||
value={host}
|
||||
placeholder="请选择主机"
|
||||
optionFilterProp="children"
|
||||
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
|
||||
onChange={v => this.handleInput('host', v)}>
|
||||
{hostStore.records.filter(x => x.id === Number(host) || hasHostPermission(x.id)).map(item => (
|
||||
<Select.Option value={String(item.id)} key={item.id}>
|
||||
{`${item.name}(${item.hostname}:${item.port})`}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item required label="检测端口" style={this.getStyle('port')}>
|
||||
<Input value={port} placeholder="请输入端口号" onChange={e => this.handleInput('port', e.target.value)}/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="进程名称" style={this.getStyle('process')} help="执行 ps -ef 看到的进程名称。">
|
||||
<Input value={process} placeholder="请输入进程名称" onChange={e => this.handleInput('process', e.target.value)}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
required
|
||||
label="脚本内容"
|
||||
style={this.getStyle('command')}
|
||||
extra={<LinkButton onClick={() => this.setState({showTmp: true})}>从模板添加</LinkButton>}>
|
||||
<ACEditor
|
||||
mode="sh"
|
||||
value={command}
|
||||
width="100%"
|
||||
height="200px"
|
||||
onChange={e => this.handleInput('command', cleanCommand(e))}/>
|
||||
</Form.Item>
|
||||
<Form.Item label="备注信息">
|
||||
{getFieldDecorator('desc', {initialValue: info['desc']})(
|
||||
<Input.TextArea placeholder="请输入备注信息"/>
|
||||
)}
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div style={{display: page === 1 ? 'block' : 'none'}}>
|
||||
<Form.Item 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 label="报警阈值">
|
||||
{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 required label="报警联系人组">
|
||||
{getFieldDecorator('notify_grp', {valuePropName: 'targetKeys', initialValue: info['notify_grp']})(
|
||||
<Transfer
|
||||
lazy={false}
|
||||
rowKey={item => item.id}
|
||||
titles={['已有联系组', '已选联系组']}
|
||||
listStyle={{width: 199}}
|
||||
dataSource={groupStore.records}
|
||||
render={item => item.name}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item required label="报警方式">
|
||||
{getFieldDecorator('notify_mode', {initialValue: info['notify_mode']})(
|
||||
<Checkbox.Group options={this.modeOptions}/>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item label="通道沉默" help="相同的告警信息,沉默期内只发送一次。">
|
||||
{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>
|
||||
</div>
|
||||
<Form.Item wrapperCol={{span: 14, offset: 6}}>
|
||||
{page === 1 &&
|
||||
<Button disabled={!b2} type="primary" onClick={this.handleSubmit} loading={loading}>提交</Button>}
|
||||
{page === 0 && (
|
||||
<div>
|
||||
<Button disabled={!b1} type="primary" onClick={() => this.setState({page: page + 1})}>下一步</Button>
|
||||
<Button disabled={!b1} type="link" loading={loading} style={{marginLeft: 20}} onClick={this.handleTest}>执行测试</Button>
|
||||
</div>
|
||||
)}
|
||||
{page !== 0 &&
|
||||
<Button style={{marginLeft: 20}} onClick={() => this.setState({page: page - 1})}>上一步</Button>}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
{showTmp && <TemplateSelector
|
||||
onOk={v => this.handleInput('command', command + v)}
|
||||
onCancel={() => this.setState({showTmp: false})}/>}
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Form.create()(ComForm)
|
||||
export default observer(function () {
|
||||
return (
|
||||
<Modal
|
||||
visible
|
||||
width={800}
|
||||
maskClosable={false}
|
||||
title={store.record.id ? '编辑任务' : '新建任务'}
|
||||
onCancel={() => store.formVisible = false}
|
||||
footer={null}>
|
||||
<Steps current={store.page} className={styles.steps}>
|
||||
<Steps.Step key={0} title="创建任务"/>
|
||||
<Steps.Step key={1} title="设置规则"/>
|
||||
</Steps>
|
||||
{store.page === 0 && <Step1/>}
|
||||
{store.page === 1 && <Step2/>}
|
||||
</Modal>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
|
||||
* Copyright (c) <spug.dev@gmail.com>
|
||||
* Released under the AGPL-3.0 License.
|
||||
*/
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Modal, Form, Input, Select, Button } from 'antd';
|
||||
import TemplateSelector from '../exec/task/TemplateSelector';
|
||||
import { LinkButton, ACEditor } from 'components';
|
||||
import { http, cleanCommand, hasHostPermission } from 'libs';
|
||||
import store from './store';
|
||||
import hostStore from '../host/store';
|
||||
|
||||
const helpMap = {
|
||||
'1': '返回HTTP状态码200-399则判定为正常,其他为异常。',
|
||||
'4': '脚本执行退出状态码为 0 则判定为正常,其他为异常。'
|
||||
}
|
||||
|
||||
export default observer(function () {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [showTmp, setShowTmp] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const {type, addr} = store.record;
|
||||
if (type === '1' && addr) {
|
||||
store.record.sitePrefix = addr.startsWith('http://') ? 'http://' : 'https://';
|
||||
store.record.domain = store.record.addr.replace(store.record.sitePrefix, '')
|
||||
}
|
||||
}, [])
|
||||
|
||||
function handleTest() {
|
||||
setLoading(true)
|
||||
const {type, sitePrefix, domain} = store.record;
|
||||
if (type === '1') store.record.addr = sitePrefix + domain;
|
||||
http.post('/api/monitor/test/', store.record, {timeout: 120000})
|
||||
.then(res => {
|
||||
if (res.is_success) {
|
||||
Modal.success({content: res.message})
|
||||
} else {
|
||||
Modal.warning({content: res.message})
|
||||
}
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}
|
||||
|
||||
function handleChangeType(v) {
|
||||
store.record.type = v;
|
||||
store.record.addr = undefined;
|
||||
store.record.extra = undefined;
|
||||
}
|
||||
|
||||
const SiteBefore = (
|
||||
<Select style={{width: 90}} value={store.record.sitePrefix} onChange={v => store.record.sitePrefix = v}>
|
||||
<Select.Option value="http://">http://</Select.Option>
|
||||
<Select.Option value="https://">https://</Select.Option>
|
||||
</Select>
|
||||
)
|
||||
|
||||
function canNext() {
|
||||
const {type, addr, extra, domain} = store.record;
|
||||
if (type === '1') {
|
||||
return name && domain
|
||||
} else if (type === '5') {
|
||||
return name && addr
|
||||
} else {
|
||||
return name && addr && extra
|
||||
}
|
||||
}
|
||||
|
||||
function toNext() {
|
||||
store.page += 1;
|
||||
const {type, sitePrefix, domain} = store.record;
|
||||
if (type === '1') store.record.addr = sitePrefix + domain;
|
||||
}
|
||||
|
||||
function getStyle(t) {
|
||||
return t.includes(store.record.type) ? {display: 'flex'} : {display: 'none'}
|
||||
}
|
||||
|
||||
const {name, desc, type, addr, extra, domain} = store.record;
|
||||
return (
|
||||
<Form labelCol={{span: 6}} wrapperCol={{span: 14}}>
|
||||
<Form.Item label="监控类型" help={helpMap[type]}>
|
||||
<Select placeholder="请选择监控类型" value={type} onChange={handleChangeType}>
|
||||
<Select.Option value="1">站点检测</Select.Option>
|
||||
<Select.Option value="2">端口检测</Select.Option>
|
||||
<Select.Option value="5">Ping检测</Select.Option>
|
||||
<Select.Option value="3">进程检测</Select.Option>
|
||||
<Select.Option value="4">自定义脚本</Select.Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item required label="任务名称">
|
||||
<Input value={name} onChange={e => store.record.name = e.target.value} placeholder="请输入任务名称"/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="监控地址" style={getStyle(['1'])}>
|
||||
<Input
|
||||
value={domain}
|
||||
addonBefore={SiteBefore}
|
||||
placeholder="请输入监控地址"
|
||||
onChange={e => store.record.domain = e.target.value}/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="监控地址" style={getStyle(['2', '5'])}>
|
||||
<Input value={addr} placeholder="请输入监控地址(IP/域名)" onChange={e => store.record.addr = e.target.value}/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="监控主机" style={getStyle(['3', '4'])}>
|
||||
<Select
|
||||
showSearch
|
||||
value={addr}
|
||||
placeholder="请选择主机"
|
||||
optionFilterProp="children"
|
||||
filterOption={(input, option) => option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
|
||||
onChange={v => store.record.addr = v}>
|
||||
{hostStore.records.filter(x => x.id === Number(addr) || hasHostPermission(x.id)).map(item => (
|
||||
<Select.Option value={String(item.id)} key={item.id}>
|
||||
{`${item.name}(${item.hostname}:${item.port})`}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item required label="检测端口" style={getStyle(['2'])}>
|
||||
<Input value={extra} placeholder="请输入端口号" onChange={e => store.record.extra = e.target.value}/>
|
||||
</Form.Item>
|
||||
<Form.Item required label="进程名称" help="执行 ps -ef 看到的进程名称。" style={getStyle(['3'])}>
|
||||
<Input value={extra} placeholder="请输入进程名称" onChange={e => store.record.extra = e.target.value}/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
required
|
||||
label="脚本内容"
|
||||
style={getStyle(['4'])}
|
||||
extra={<LinkButton onClick={() => setShowTmp(true)}>从模板添加</LinkButton>}>
|
||||
<ACEditor
|
||||
mode="sh"
|
||||
value={extra || ''}
|
||||
width="100%"
|
||||
height="200px"
|
||||
onChange={e => store.record.extra = cleanCommand(e)}/>
|
||||
</Form.Item>
|
||||
<Form.Item label="备注信息">
|
||||
<Input.TextArea value={desc} onChange={e => store.record.desc = e.target.value} placeholder="请输入备注信息"/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item wrapperCol={{span: 14, offset: 6}} style={{marginTop: 12}}>
|
||||
<Button disabled={!canNext()} type="primary" onClick={toNext}>下一步</Button>
|
||||
<Button disabled={false} type="link" loading={loading} onClick={handleTest}>执行测试</Button>
|
||||
</Form.Item>
|
||||
{showTmp && <TemplateSelector onOk={v => store.record.extra += v} onCancel={() => setShowTmp(false)}/>}
|
||||
</Form>
|
||||
)
|
||||
})
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
|
||||
* Copyright (c) <spug.dev@gmail.com>
|
||||
* Released under the AGPL-3.0 License.
|
||||
*/
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Form, Select, Radio, Transfer, Checkbox, Button, message } from 'antd';
|
||||
import { http } from 'libs';
|
||||
import groupStore from '../alarm/group/store';
|
||||
import store from './store';
|
||||
import lds from 'lodash';
|
||||
|
||||
const modeOptions = [
|
||||
{label: '微信', 'value': '1'},
|
||||
{label: '短信', 'value': '2', disabled: true},
|
||||
{label: '钉钉', 'value': '3'},
|
||||
{label: '邮件', 'value': '4'},
|
||||
{label: '企业微信', 'value': '5'},
|
||||
];
|
||||
|
||||
export default observer(function () {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const {type, addr} = store.record;
|
||||
if (type === '1' && addr) {
|
||||
store.record.sitePrefix = addr.startsWith('http://') ? 'http://' : 'https://';
|
||||
store.record.domain = store.record.addr.replace(store.record.sitePrefix, '')
|
||||
}
|
||||
}, [])
|
||||
|
||||
function handleSubmit() {
|
||||
setLoading(true)
|
||||
const formData = form.getFieldsValue();
|
||||
Object.assign(formData, lds.pick(store.record, ['id', 'name', 'desc', 'addr', 'extra', 'type']))
|
||||
formData['id'] = store.record.id;
|
||||
http.post('/api/monitor/', formData)
|
||||
.then(() => {
|
||||
message.success('操作成功');
|
||||
store.formVisible = false;
|
||||
store.fetchRecords()
|
||||
}, () => setLoading(false))
|
||||
}
|
||||
|
||||
function canNext() {
|
||||
const {notify_grp, notify_mode} = form.getFieldsValue();
|
||||
return notify_grp && notify_grp.length && notify_mode && notify_mode.length;
|
||||
}
|
||||
|
||||
const info = store.record;
|
||||
return (
|
||||
<Form form={form} labelCol={{span: 6}} wrapperCol={{span: 14}}>
|
||||
<Form.Item name="rate" initialValue={info.rate || 5} label="监控频率">
|
||||
<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 name="threshold" initialValue={info.threshold || 3} label="报警阈值">
|
||||
<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 required name="notify_grp" valuePropName="targetKeys" initialValue={info.notify_grp} label="报警联系人组">
|
||||
<Transfer
|
||||
lazy={false}
|
||||
rowKey={item => item.id}
|
||||
titles={['已有联系组', '已选联系组']}
|
||||
listStyle={{width: 199}}
|
||||
dataSource={groupStore.records}
|
||||
render={item => item.name}/>
|
||||
</Form.Item>
|
||||
<Form.Item required name="notify_mode" initialValue={info.notify_mode} label="报警方式">
|
||||
<Checkbox.Group options={modeOptions}/>
|
||||
</Form.Item>
|
||||
<Form.Item name="quiet" initialValue={info.quiet || 24 * 60} label="通道沉默" help="相同的告警信息,沉默期内只发送一次。">
|
||||
<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 shouldUpdate wrapperCol={{span: 14, offset: 6}} style={{marginTop: 12}}>
|
||||
{() => (
|
||||
<React.Fragment>
|
||||
<Button disabled={!canNext()} loading={loading} type="primary" onClick={handleSubmit}>提交</Button>
|
||||
<Button style={{marginLeft: 20}} onClick={() => store.page -= 1}>上一步</Button>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
)
|
||||
})
|
|
@ -5,14 +5,14 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Table, Modal, Tag, message } from 'antd';
|
||||
import { Action } from 'components';
|
||||
import ComForm from './Form';
|
||||
import { Table, Modal, Radio, Tag, message } from 'antd';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Action, AuthButton, TableCard } from 'components';
|
||||
import { http, hasPermission } from 'libs';
|
||||
import store from './store';
|
||||
import groupStore from '../alarm/group/store';
|
||||
import hostStore from '../host/store';
|
||||
import store from './store';
|
||||
import lds from 'lodash';
|
||||
import groupStore from "pages/alarm/group/store";
|
||||
|
||||
@observer
|
||||
class ComTable extends React.Component {
|
||||
|
@ -70,75 +70,69 @@ class ComTable extends React.Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
let data = store.records;
|
||||
if (store.f_name) {
|
||||
data = data.filter(item => item['name'].toLowerCase().includes(store.f_name.toLowerCase()))
|
||||
}
|
||||
if (store.f_type) {
|
||||
data = data.filter(item => item['type_alias'] === store.f_type)
|
||||
}
|
||||
if (store.f_status !== undefined) {
|
||||
if (store.f_status === -3) {
|
||||
data = data.filter(item => !item['is_active'])
|
||||
} else if (store.f_status === -2) {
|
||||
data = data.filter(item => item['is_active'])
|
||||
} else if (store.f_status === -1) {
|
||||
data = data.filter(item => item['is_active'] && !item['latest_status_alias'])
|
||||
} else {
|
||||
data = data.filter(item => item['latest_status'] === store.f_status)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Table
|
||||
rowKey="id"
|
||||
loading={store.isFetching}
|
||||
dataSource={data}
|
||||
pagination={{
|
||||
showSizeChanger: true,
|
||||
showLessItems: true,
|
||||
hideOnSinglePage: true,
|
||||
showTotal: total => `共 ${total} 条`,
|
||||
pageSizeOptions: ['10', '20', '50', '100']
|
||||
}}>
|
||||
<Table.Column title="任务名称" dataIndex="name"/>
|
||||
<Table.Column title="类型" dataIndex="type_alias"/>
|
||||
<Table.Column ellipsis title="地址" render={info => {
|
||||
if ('34'.includes(info.type)) {
|
||||
return lds.get(this.state.hosts, `${info.addr}.name`)
|
||||
<TableCard
|
||||
rowKey="id"
|
||||
title="监控任务"
|
||||
loading={store.isFetching}
|
||||
dataSource={store.dataSource}
|
||||
onReload={store.fetchRecords}
|
||||
actions={[
|
||||
<AuthButton
|
||||
auth="monitor.monitor.add"
|
||||
type="primary"
|
||||
icon={<PlusOutlined/>}
|
||||
onClick={() => store.showForm()}>新建</AuthButton>,
|
||||
<Radio.Group value={store.f_active} onChange={e => store.f_active = e.target.value}>
|
||||
<Radio.Button value="">全部</Radio.Button>
|
||||
<Radio.Button value="1">已激活</Radio.Button>
|
||||
<Radio.Button value="0">未激活</Radio.Button>
|
||||
</Radio.Group>
|
||||
]}
|
||||
pagination={{
|
||||
showSizeChanger: true,
|
||||
showLessItems: true,
|
||||
hideOnSinglePage: true,
|
||||
showTotal: total => `共 ${total} 条`,
|
||||
pageSizeOptions: ['10', '20', '50', '100']
|
||||
}}>
|
||||
<Table.Column title="任务名称" dataIndex="name"/>
|
||||
<Table.Column title="类型" dataIndex="type_alias"/>
|
||||
<Table.Column ellipsis title="地址" render={info => {
|
||||
if ('34'.includes(info.type)) {
|
||||
return lds.get(this.state.hosts, `${info.addr}.name`)
|
||||
} else {
|
||||
return info.addr
|
||||
}
|
||||
}}/>
|
||||
<Table.Column title="频率" dataIndex="rate" render={value => `${value}分钟`}/>
|
||||
<Table.Column title="状态" render={info => {
|
||||
if (info.is_active) {
|
||||
if (info['latest_status'] === 0) {
|
||||
return <Tag color="green">正常</Tag>
|
||||
} else if (info['latest_status'] === 1) {
|
||||
return <Tag color="red">异常</Tag>
|
||||
} else {
|
||||
return info.addr
|
||||
return <Tag color="orange">待检测</Tag>
|
||||
}
|
||||
}}/>
|
||||
<Table.Column title="频率" dataIndex="rate" render={value => `${value}分钟`}/>
|
||||
<Table.Column title="状态" render={info => {
|
||||
if (info.is_active) {
|
||||
if (info['latest_status'] === 0) {
|
||||
return <Tag color="green">正常</Tag>
|
||||
} else if (info['latest_status'] === 1) {
|
||||
return <Tag color="red">异常</Tag>
|
||||
} else {
|
||||
return <Tag color="orange">待检测</Tag>
|
||||
}
|
||||
} else {
|
||||
return <Tag>未启用</Tag>
|
||||
}
|
||||
}}/>
|
||||
<Table.Column title="更新于" dataIndex="latest_run_time_alias"
|
||||
sorter={(a, b) => a.latest_run_time.localeCompare(b.latest_run_time)}/>
|
||||
{hasPermission('monitor.monitor.edit|monitor.monitor.del') && (
|
||||
<Table.Column width={180} title="操作" render={info => (
|
||||
<Action>
|
||||
<Action.Button auth="monitor.monitor.edit"
|
||||
onClick={() => this.handleActive(info)}>{info['is_active'] ? '禁用' : '启用'}</Action.Button>
|
||||
<Action.Button auth="monitor.monitor.edit" onClick={() => store.showForm(info)}>编辑</Action.Button>
|
||||
<Action.Button auth="monitor.monitor.del" onClick={() => this.handleDelete(info)}>删除</Action.Button>
|
||||
</Action>
|
||||
)}/>
|
||||
)}
|
||||
</Table>
|
||||
{store.formVisible && <ComForm/>}
|
||||
</React.Fragment>
|
||||
} else {
|
||||
return <Tag>未启用</Tag>
|
||||
}
|
||||
}}/>
|
||||
<Table.Column title="更新于" dataIndex="latest_run_time_alias"
|
||||
sorter={(a, b) => a.latest_run_time.localeCompare(b.latest_run_time)}/>
|
||||
<Table.Column hide title="描述" dataIndex="desc"/>
|
||||
{hasPermission('monitor.monitor.edit|monitor.monitor.del') && (
|
||||
<Table.Column width={180} title="操作" render={info => (
|
||||
<Action>
|
||||
<Action.Button auth="monitor.monitor.edit"
|
||||
onClick={() => this.handleActive(info)}>{info['is_active'] ? '禁用' : '启用'}</Action.Button>
|
||||
<Action.Button auth="monitor.monitor.edit" onClick={() => store.showForm(info)}>编辑</Action.Button>
|
||||
<Action.Button auth="monitor.monitor.del" onClick={() => this.handleDelete(info)}>删除</Action.Button>
|
||||
</Action>
|
||||
)}/>
|
||||
)}
|
||||
</TableCard>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,40 +5,38 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Input, Select, Button } from 'antd';
|
||||
import { SearchForm, AuthDiv, AuthCard } from 'components';
|
||||
import { Input, Select } from 'antd';
|
||||
import { SearchForm, AuthDiv, Breadcrumb } from 'components';
|
||||
import ComTable from './Table';
|
||||
import ComForm from './Form';
|
||||
import store from './store';
|
||||
|
||||
export default observer(function () {
|
||||
return (
|
||||
<AuthCard auth="monitor.monitor.view">
|
||||
<AuthDiv auth="monitor.monitor.view">
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item>首页</Breadcrumb.Item>
|
||||
<Breadcrumb.Item>监控中心</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<SearchForm>
|
||||
<SearchForm.Item span={6} title="任务名称">
|
||||
<SearchForm.Item span={7} title="任务名称">
|
||||
<Input allowClear value={store.f_name} onChange={e => store.f_name = e.target.value} placeholder="请输入"/>
|
||||
</SearchForm.Item>
|
||||
<SearchForm.Item span={6} title="检测类型">
|
||||
<SearchForm.Item span={7} title="检测类型">
|
||||
<Select allowClear value={store.f_type} onChange={v => store.f_type = v} placeholder="请选择">
|
||||
{store.types.map(item => <Select.Option key={item} value={item}>{item}</Select.Option>)}
|
||||
</Select>
|
||||
</SearchForm.Item>
|
||||
<SearchForm.Item span={6} title="任务状态">
|
||||
<SearchForm.Item span={7} title="任务状态">
|
||||
<Select allowClear value={store.f_status} onChange={v => store.f_status = v} placeholder="请选择">
|
||||
<Select.Option value={-3}>未激活</Select.Option>
|
||||
<Select.Option value={-2}>已激活</Select.Option>
|
||||
<Select.Option value={-1}>待检测</Select.Option>
|
||||
<Select.Option value={0}>正常</Select.Option>
|
||||
<Select.Option value={1}>异常</Select.Option>
|
||||
</Select>
|
||||
</SearchForm.Item>
|
||||
<SearchForm.Item span={6}>
|
||||
<Button type="primary" icon="sync" onClick={store.fetchRecords}>刷新</Button>
|
||||
</SearchForm.Item>
|
||||
</SearchForm>
|
||||
<AuthDiv auth="monitor.monitor.add" style={{marginBottom: 16}}>
|
||||
<Button type="primary" icon="plus" onClick={() => store.showForm()}>新建</Button>
|
||||
</AuthDiv>
|
||||
<ComTable/>
|
||||
</AuthCard>
|
||||
{store.formVisible && <ComForm/>}
|
||||
</AuthDiv>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) OpenSpug Organization. https://github.com/openspug/spug
|
||||
* Copyright (c) <spug.dev@gmail.com>
|
||||
* Released under the AGPL-3.0 License.
|
||||
*/
|
||||
import { makeRoute } from "../../libs/router";
|
||||
import Index from './index';
|
||||
|
||||
|
||||
export default [
|
||||
makeRoute('', Index),
|
||||
]
|
|
@ -3,20 +3,38 @@
|
|||
* Copyright (c) <spug.dev@gmail.com>
|
||||
* Released under the AGPL-3.0 License.
|
||||
*/
|
||||
import { observable } from "mobx";
|
||||
import { observable, computed } from 'mobx';
|
||||
import http from 'libs/http';
|
||||
import moment from "moment";
|
||||
import moment from 'moment';
|
||||
import lds from 'lodash';
|
||||
|
||||
class Store {
|
||||
@observable records = [];
|
||||
@observable record = {};
|
||||
@observable types = [];
|
||||
@observable page = 0;
|
||||
@observable isFetching = false;
|
||||
@observable formVisible = false;
|
||||
|
||||
@observable f_name;
|
||||
@observable f_type;
|
||||
@observable f_status;
|
||||
@observable f_active = '';
|
||||
|
||||
@computed get dataSource() {
|
||||
let records = this.records;
|
||||
if (this.f_active) records = records.filter(x => x.is_active === (this.f_active === '1'));
|
||||
if (this.f_name) records = records.filter(x => x.name.toLowerCase().includes(this.f_name.toLowerCase()));
|
||||
if (this.f_type) records = records.filter(x => x.type_alias === this.f_type);
|
||||
if (this.f_status !== undefined) {
|
||||
if (this.f_status === -1) {
|
||||
records = records.filter(x => x.is_active && !x.latest_status_alias);
|
||||
} else {
|
||||
records = records.filter(x => x.latest_status === this.f_status)
|
||||
}
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
fetchRecords = () => {
|
||||
this.isFetching = true;
|
||||
|
@ -35,9 +53,11 @@ class Store {
|
|||
.finally(() => this.isFetching = false)
|
||||
};
|
||||
|
||||
showForm = (info = {}) => {
|
||||
showForm = (info) => {
|
||||
info = info || {type: '1', sitePrefix: 'http://'};
|
||||
this.page = 0;
|
||||
this.record = lds.cloneDeep(info);
|
||||
this.formVisible = true;
|
||||
this.record = info
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue