feat: 网络双线图+告警联系人/联系组+Agent v2.0.0 + 一键报警阈值
- HostMonitor: 网络改为双线图 rx/tx 网卡带宽吞吐(双Area线), 移除单rx图 - Agent: 默认版本号从 dev 改为 v2.0.0, 重建二进制 5.2MB - 告警中心: 新增报警联系人/报警联系组 Tab, ContactDialog/ContactGroupDialog - 后端: AlertContact/AlertContactGroup 模型+CRUD, monitor_alert_contacts/groups 表 - 路由: /alerts/contacts + /alerts/contact-groups 8个REST端点 - 一键报警: 更新描述为6条规则(含TCP+进程), 切换到 /alerts/quick-setup - 测试规则: 内存>10% 触发告警已创建, 飞书通知验证通过
This commit is contained in:
1 parent
5333f6f04a
commit
e1cfbaf45e
11 files changed
+810
-16
No files matched your search
@@ -0,0 +1,143 @@
|
||||
// 报警联系人创建/编辑对话框 // Logan.Li
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { toast } from 'sonner';
|
||||
import { alertApi } from '@/services/api';
|
||||
import type { Contact } from './alert-center-helpers';
|
||||
|
||||
export function ContactDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
editingContact,
|
||||
onSaved,
|
||||
}: {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
editingContact: Contact | null;
|
||||
onSaved: () => void;
|
||||
}) {
|
||||
const [form, setForm] = useState({
|
||||
name: '', phone: '', email: '', dingtalk: '', feishu: '', wechat: '',
|
||||
});
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
if (editingContact) {
|
||||
setForm({
|
||||
name: editingContact.name || '',
|
||||
phone: editingContact.phone || '',
|
||||
email: editingContact.email || '',
|
||||
dingtalk: editingContact.dingtalk || '',
|
||||
feishu: editingContact.feishu || '',
|
||||
wechat: editingContact.wechat || '',
|
||||
});
|
||||
} else {
|
||||
setForm({ name: '', phone: '', email: '', dingtalk: '', feishu: '', wechat: '' });
|
||||
}
|
||||
}
|
||||
}, [open, editingContact]);
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!form.name.trim()) return;
|
||||
setSaving(true);
|
||||
try {
|
||||
const payload = {
|
||||
name: form.name.trim(),
|
||||
phone: form.phone.trim() || undefined,
|
||||
email: form.email.trim() || undefined,
|
||||
dingtalk: form.dingtalk.trim() || undefined,
|
||||
feishu: form.feishu.trim() || undefined,
|
||||
wechat: form.wechat.trim() || undefined,
|
||||
};
|
||||
if (editingContact) {
|
||||
await alertApi.updateContact(editingContact.id, payload);
|
||||
toast.success('联系人已更新');
|
||||
} else {
|
||||
await alertApi.createContact(payload);
|
||||
toast.success('联系人已添加');
|
||||
}
|
||||
onOpenChange(false);
|
||||
onSaved();
|
||||
} catch {
|
||||
toast.error('保存失败');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingContact ? '编辑报警联系人' : '添加报警联系人'}</DialogTitle>
|
||||
<DialogDescription>配置报警联系人信息(姓名必填,渠道选填)</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label>姓名 *</Label>
|
||||
<Input
|
||||
value={form.name}
|
||||
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
||||
placeholder="如:张三"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>手机号</Label>
|
||||
<Input
|
||||
value={form.phone}
|
||||
onChange={(e) => setForm({ ...form, phone: e.target.value })}
|
||||
placeholder="如:13800138000"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>邮箱</Label>
|
||||
<Input
|
||||
value={form.email}
|
||||
onChange={(e) => setForm({ ...form, email: e.target.value })}
|
||||
placeholder="如:zhangsan@example.com"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>钉钉(webhook URL)</Label>
|
||||
<Input
|
||||
value={form.dingtalk}
|
||||
onChange={(e) => setForm({ ...form, dingtalk: e.target.value })}
|
||||
placeholder="https://oapi.dingtalk.com/robot/send?access_token=..."
|
||||
className="font-mono text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>飞书(webhook URL)</Label>
|
||||
<Input
|
||||
value={form.feishu}
|
||||
onChange={(e) => setForm({ ...form, feishu: e.target.value })}
|
||||
placeholder="https://open.feishu.cn/open-apis/bot/v2/hook/..."
|
||||
className="font-mono text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>企业微信(webhook URL)</Label>
|
||||
<Input
|
||||
value={form.wechat}
|
||||
onChange={(e) => setForm({ ...form, wechat: e.target.value })}
|
||||
placeholder="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=..."
|
||||
className="font-mono text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>取消</Button>
|
||||
<Button onClick={handleSave} disabled={!form.name.trim() || saving}>
|
||||
{saving ? '保存中...' : editingContact ? '保存修改' : '添加联系人'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user