/**
* 预警管理页面组件
*/
import { api } from '../core/api.js';
import { formatDate, formatRelativeTime } from '../core/utils.js';
import { confirm, alert } from '../components/modal.js';
import store from '../core/store.js';
export default class Alerts {
constructor(container, route) {
this.container = container;
this.route = route;
this.filters = {
level: '',
status: '',
type: '',
page: 1,
per_page: 10
};
this.init();
}
async init() {
try {
this.render();
await this.loadData();
this.bindEvents();
} catch (error) {
console.error('Alerts page init error:', error);
this.showError(error);
}
}
render() {
this.container.innerHTML = `
`;
// 渲染规则模态框
this.renderRuleModal();
}
renderRuleModal() {
const modalHTML = `
`;
document.body.insertAdjacentHTML('beforeend', modalHTML);
}
async loadData() {
try {
// 并行加载数据
const [alertsRes, rulesRes, statistics, monitorStatus] = await Promise.all([
api.alerts.list(this.filters),
api.rules.list(),
api.alerts.statistics(),
api.monitor.status()
]);
// 更新统计数据
this.updateStatistics(statistics);
// 更新监控状态
this.updateMonitorStatus(monitorStatus);
// 更新规则列表
this.updateRulesList(rulesRes);
// 更新预警列表
this.updateAlertsList(alertsRes);
} catch (error) {
console.error('Load alerts data error:', error);
this.showError(error);
}
}
updateStatistics(statistics) {
document.getElementById('critical-alerts').textContent = statistics.critical || 0;
document.getElementById('warning-alerts').textContent = statistics.warning || 0;
document.getElementById('info-alerts').textContent = statistics.info || 0;
document.getElementById('total-alerts').textContent = statistics.total || 0;
}
updateMonitorStatus(status) {
const statusEl = document.getElementById('monitor-status');
const textEl = document.getElementById('monitor-text');
if (status.status === 'running') {
statusEl.className = 'status-indicator online';
textEl.textContent = '监控运行中';
} else {
statusEl.className = 'status-indicator offline';
textEl.textContent = '监控已停止';
}
}
updateRulesList(rules) {
const tbody = document.getElementById('rules-table');
if (!tbody) return;
if (!rules || rules.length === 0) {
tbody.innerHTML = `
| 暂无预警规则 |
`;
return;
}
tbody.innerHTML = rules.map(rule => `
| ${rule.name} |
${rule.alert_type} |
${rule.level} |
${rule.threshold} |
${rule.enabled ? '启用' : '禁用'}
|
|
`).join('');
}
updateAlertsList(response) {
const tbody = document.getElementById('alerts-table');
if (!tbody) return;
const alerts = response.alerts || [];
if (alerts.length === 0) {
tbody.innerHTML = `
| 暂无预警记录 |
`;
return;
}
tbody.innerHTML = alerts.map(alert => `
| ${formatDate(alert.created_at, 'MM-DD HH:mm')} |
${alert.level} |
${alert.rule_name} |
${alert.message} |
${alert.is_active ? '活跃' : '已解决'}
|
${alert.is_active ? `
` : '-'}
|
`).join('');
// 更新分页
this.updatePagination(response);
}
updatePagination(response) {
const pagination = document.getElementById('pagination');
if (!pagination) return;
const { page = 1, total_pages = 1 } = response;
if (total_pages <= 1) {
pagination.innerHTML = '';
return;
}
let html = '';
// 上一页
if (page > 1) {
html += `
上一页
`;
}
// 页码
for (let i = Math.max(1, page - 2); i <= Math.min(total_pages, page + 2); i++) {
html += `
${i}
`;
}
// 下一页
if (page < total_pages) {
html += `
下一页
`;
}
pagination.innerHTML = html;
}
getLevelColor(level) {
const colors = {
'critical': 'danger',
'error': 'danger',
'warning': 'warning',
'info': 'info'
};
return colors[level] || 'secondary';
}
bindEvents() {
// 监控控制
document.getElementById('start-monitor')?.addEventListener('click', () => {
this.startMonitor();
});
document.getElementById('stop-monitor')?.addEventListener('click', () => {
this.stopMonitor();
});
document.getElementById('check-alerts')?.addEventListener('click', () => {
this.checkAlerts();
});
// 规则表单
document.getElementById('save-rule')?.addEventListener('click', () => {
this.saveRule();
});
// 筛选器
document.getElementById('filter-level')?.addEventListener('change', (e) => {
this.filters.level = e.target.value;
this.filters.page = 1;
this.loadAlerts();
});
document.getElementById('filter-status')?.addEventListener('change', (e) => {
this.filters.status = e.target.value === 'active' ? 'active' :
e.target.value === 'resolved' ? 'resolved' : '';
this.filters.page = 1;
this.loadAlerts();
});
document.getElementById('filter-search')?.addEventListener('input',
this.debounce((e) => {
this.filters.search = e.target.value;
this.filters.page = 1;
this.loadAlerts();
}, 300)
);
document.getElementById('reset-filters')?.addEventListener('click', () => {
this.resetFilters();
});
// 分页
document.getElementById('pagination')?.addEventListener('click', (e) => {
if (e.target.classList.contains('page-link')) {
e.preventDefault();
const page = parseInt(e.target.dataset.page);
if (page) {
this.filters.page = page;
this.loadAlerts();
}
}
});
}
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
async startMonitor() {
try {
await api.monitor.start();
await this.loadData();
store.dispatch('showToast', {
type: 'success',
message: '监控已启动'
});
} catch (error) {
console.error('Start monitor error:', error);
store.dispatch('showToast', {
type: 'error',
message: '启动监控失败'
});
}
}
async stopMonitor() {
try {
await api.monitor.stop();
await this.loadData();
store.dispatch('showToast', {
type: 'success',
message: '监控已停止'
});
} catch (error) {
console.error('Stop monitor error:', error);
store.dispatch('showToast', {
type: 'error',
message: '停止监控失败'
});
}
}
async checkAlerts() {
try {
const result = await api.monitor.checkAlerts();
await this.loadData();
store.dispatch('showToast', {
type: 'success',
message: `检查完成,发现 ${result.alerts_count || 0} 个新预警`
});
} catch (error) {
console.error('Check alerts error:', error);
store.dispatch('showToast', {
type: 'error',
message: '检查预警失败'
});
}
}
async saveRule() {
const form = document.getElementById('rule-form');
const formData = new FormData(form);
const data = {
name: formData.get('name'),
alert_type: formData.get('alert_type'),
level: formData.get('level'),
threshold: parseFloat(formData.get('threshold')),
description: formData.get('description'),
condition: formData.get('condition'),
check_interval: parseInt(formData.get('check_interval')),
cooldown: parseInt(formData.get('cooldown')),
enabled: formData.has('enabled')
};
try {
await api.rules.create(data);
await this.loadData();
// 关闭模态框
const modal = bootstrap.Modal.getInstance(document.getElementById('ruleModal'));
modal.hide();
// 重置表单
form.reset();
store.dispatch('showToast', {
type: 'success',
message: '规则创建成功'
});
} catch (error) {
console.error('Save rule error:', error);
store.dispatch('showToast', {
type: 'error',
message: '创建规则失败'
});
}
}
async editRule(name) {
// TODO: 实现编辑规则功能
store.dispatch('showToast', {
type: 'info',
message: '编辑功能开发中'
});
}
async deleteRule(name) {
const confirmed = await confirm({
title: '删除规则',
message: `确定要删除规则 "${name}" 吗?`
});
if (!confirmed) return;
try {
await api.rules.delete(name);
await this.loadData();
store.dispatch('showToast', {
type: 'success',
message: '规则已删除'
});
} catch (error) {
console.error('Delete rule error:', error);
store.dispatch('showToast', {
type: 'error',
message: '删除规则失败'
});
}
}
async resolveAlert(alertId) {
try {
await api.alerts.resolve(alertId, { resolved_by: 'admin', resolution: '手动解决' });
await this.loadAlerts();
store.dispatch('showToast', {
type: 'success',
message: '预警已解决'
});
} catch (error) {
console.error('Resolve alert error:', error);
store.dispatch('showToast', {
type: 'error',
message: '解决预警失败'
});
}
}
resetFilters() {
this.filters = {
level: '',
status: '',
search: '',
page: 1,
per_page: 10
};
// 重置筛选器UI
document.getElementById('filter-level').value = '';
document.getElementById('filter-status').value = '';
document.getElementById('filter-search').value = '';
// 重新加载数据
this.loadAlerts();
}
async loadAlerts() {
try {
const response = await api.alerts.list(this.filters);
this.updateAlertsList(response);
} catch (error) {
console.error('Load alerts error:', error);
}
}
showError(error) {
this.container.innerHTML = `
加载失败
${error.message || '未知错误'}
`;
}
}
// 暴露到全局供内联事件使用
window.alertsPage = null;