refactor: AlertCenter拆分(723→390行)+修复TopologyView类型错误
- AlertCenter.tsx: 723→390行,提取类型/helpers/渠道Tab到子组件 - alert-center-helpers.tsx: 新建(127行),共享Alert/AlertRule/NotificationChannel类型+mapAlert+badge helpers - AlertChannelDialog.tsx: 新建(121行),通知渠道创建/编辑对话框(自包含useEffect同步) - AlertChannelsTab.tsx: 新建(152行),通知渠道Tab自包含(状态+API+渲染+CRUD) - AlertCenterDetail.tsx: 去重getSeverityBadge,统一从helpers导入 - TopologyView.tsx: 修复editForm.type类型推断(string→TopologyNode['type'])
This commit is contained in:
1 parent
805cb767a8
commit
76a41f5eeb
6 files changed
+458
-406
No files matched your search
@@ -0,0 +1,121 @@
|
||||
// 通知渠道创建/编辑对话框 // 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 {
|
||||
Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { toast } from 'sonner';
|
||||
import { alertApi } from '@/services/api';
|
||||
import type { NotificationChannel } from './alert-center-helpers';
|
||||
|
||||
const CHANNEL_TYPES = [
|
||||
{ value: 'dingtalk', label: '钉钉', hint: '钉钉机器人: webhook URL + secret' },
|
||||
{ value: 'wechat', label: '企业微信', hint: '企业微信: webhook URL' },
|
||||
{ value: 'email', label: '邮件', hint: '邮件: {"to":"ops@ezops.io"}' },
|
||||
{ value: 'sms', label: '短信', hint: '短信: {"phones":"13800138000"}' },
|
||||
{ value: 'webhook', label: 'Webhook', hint: 'Webhook: {"url":"https://..."}' },
|
||||
];
|
||||
|
||||
export function AlertChannelDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
editingChannel,
|
||||
onSaved,
|
||||
}: {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
editingChannel: NotificationChannel | null;
|
||||
onSaved: () => void;
|
||||
}) {
|
||||
const [form, setForm] = useState({ name: '', type: 'dingtalk', config: '' });
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
// Logan.Li — 对话框打开时同步编辑数据
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
if (editingChannel) {
|
||||
setForm({ name: editingChannel.name, type: editingChannel.type, config: editingChannel.config || '' });
|
||||
} else {
|
||||
setForm({ name: '', type: 'dingtalk', config: '' });
|
||||
}
|
||||
}
|
||||
}, [open, editingChannel]);
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!form.name.trim()) return;
|
||||
setSaving(true);
|
||||
try {
|
||||
const payload = { name: form.name.trim(), type: form.type, config: form.config.trim() };
|
||||
if (editingChannel) {
|
||||
await alertApi.updateNotifyChannel(editingChannel.id, payload);
|
||||
toast.success('渠道已更新');
|
||||
} else {
|
||||
await alertApi.createNotifyChannel(payload);
|
||||
toast.success('渠道已添加');
|
||||
}
|
||||
onOpenChange(false);
|
||||
onSaved();
|
||||
} catch {
|
||||
toast.error('保存失败');
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const currentHint = CHANNEL_TYPES.find(t => t.value === form.type)?.hint || '';
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingChannel ? '编辑通知渠道' : '添加通知渠道'}</DialogTitle>
|
||||
<DialogDescription>配置告警通知渠道(钉钉/企业微信/邮件/短信/Webhook)</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>
|
||||
<Select value={form.type} onValueChange={(v) => setForm({ ...form, type: v })}>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{CHANNEL_TYPES.map(t => (
|
||||
<SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>配置(JSON)</Label>
|
||||
<Input
|
||||
value={form.config}
|
||||
onChange={(e) => setForm({ ...form, config: e.target.value })}
|
||||
placeholder='{"webhook":"https://...","secret":"..."}'
|
||||
className="font-mono text-sm"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">{currentHint}</p>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>取消</Button>
|
||||
<Button onClick={handleSave} disabled={!form.name.trim() || saving}>
|
||||
{saving ? '保存中...' : editingChannel ? '保存修改' : '添加渠道'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user