1508 lines
57 KiB
JavaScript
1508 lines
57 KiB
JavaScript
// TSP智能助手综合管理平台前端脚本
|
||
|
||
class TSPDashboard {
|
||
constructor() {
|
||
this.currentTab = 'dashboard';
|
||
this.charts = {};
|
||
this.refreshIntervals = {};
|
||
this.websocket = null;
|
||
this.sessionId = null;
|
||
this.isAgentMode = true;
|
||
|
||
this.init();
|
||
this.restorePageState();
|
||
}
|
||
|
||
init() {
|
||
this.bindEvents();
|
||
this.loadInitialData();
|
||
this.startAutoRefresh();
|
||
this.initCharts();
|
||
}
|
||
|
||
bindEvents() {
|
||
// 标签页切换
|
||
document.querySelectorAll('[data-tab]').forEach(tab => {
|
||
tab.addEventListener('click', (e) => {
|
||
e.preventDefault();
|
||
this.switchTab(tab.dataset.tab);
|
||
});
|
||
});
|
||
|
||
// 对话控制
|
||
document.getElementById('start-chat').addEventListener('click', () => this.startChat());
|
||
document.getElementById('end-chat').addEventListener('click', () => this.endChat());
|
||
document.getElementById('create-work-order').addEventListener('click', () => this.showCreateWorkOrderModal());
|
||
document.getElementById('send-button').addEventListener('click', () => this.sendMessage());
|
||
document.getElementById('message-input').addEventListener('keypress', (e) => {
|
||
if (e.key === 'Enter') this.sendMessage();
|
||
});
|
||
|
||
// 快速操作按钮
|
||
document.querySelectorAll('.quick-action-btn').forEach(btn => {
|
||
btn.addEventListener('click', () => {
|
||
const message = btn.dataset.message;
|
||
document.getElementById('message-input').value = message;
|
||
this.sendMessage();
|
||
});
|
||
});
|
||
|
||
// Agent控制
|
||
document.getElementById('agent-mode-toggle').addEventListener('change', (e) => {
|
||
this.toggleAgentMode(e.target.checked);
|
||
});
|
||
document.getElementById('start-agent-monitoring').addEventListener('click', () => this.startAgentMonitoring());
|
||
document.getElementById('stop-agent-monitoring').addEventListener('click', () => this.stopAgentMonitoring());
|
||
document.getElementById('proactive-monitoring').addEventListener('click', () => this.proactiveMonitoring());
|
||
document.getElementById('intelligent-analysis').addEventListener('click', () => this.intelligentAnalysis());
|
||
|
||
// 预警管理
|
||
document.getElementById('refresh-alerts').addEventListener('click', () => this.loadAlerts());
|
||
document.getElementById('alert-filter').addEventListener('change', () => this.updateAlertsDisplay());
|
||
|
||
// 知识库管理
|
||
document.getElementById('search-knowledge').addEventListener('click', () => this.searchKnowledge());
|
||
document.getElementById('knowledge-search').addEventListener('keypress', (e) => {
|
||
if (e.key === 'Enter') this.searchKnowledge();
|
||
});
|
||
|
||
// 工单管理
|
||
document.getElementById('refresh-workorders').addEventListener('click', () => this.loadWorkOrders());
|
||
document.getElementById('workorder-status-filter').addEventListener('change', () => this.loadWorkOrders());
|
||
document.getElementById('workorder-priority-filter').addEventListener('change', () => this.loadWorkOrders());
|
||
|
||
// 模态框
|
||
document.getElementById('create-work-order-btn').addEventListener('click', () => this.createWorkOrder());
|
||
document.getElementById('add-knowledge-btn').addEventListener('click', () => this.addKnowledge());
|
||
document.getElementById('upload-file-btn').addEventListener('click', () => this.uploadFile());
|
||
|
||
// 置信度滑块
|
||
document.getElementById('knowledge-confidence').addEventListener('input', (e) => {
|
||
document.getElementById('confidence-value').textContent = e.target.value;
|
||
});
|
||
document.getElementById('file-confidence').addEventListener('input', (e) => {
|
||
document.getElementById('file-confidence-value').textContent = e.target.value;
|
||
});
|
||
|
||
// 处理方式选择
|
||
document.getElementById('process-method').addEventListener('change', (e) => {
|
||
const manualDiv = document.getElementById('manual-question-div');
|
||
if (e.target.value === 'manual') {
|
||
manualDiv.style.display = 'block';
|
||
} else {
|
||
manualDiv.style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// 系统设置
|
||
document.getElementById('system-settings-form').addEventListener('submit', (e) => {
|
||
e.preventDefault();
|
||
this.saveSystemSettings();
|
||
});
|
||
}
|
||
|
||
switchTab(tabName) {
|
||
// 更新导航状态
|
||
document.querySelectorAll('.nav-link').forEach(link => {
|
||
link.classList.remove('active');
|
||
});
|
||
document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
|
||
|
||
// 显示对应内容
|
||
document.querySelectorAll('.tab-content').forEach(content => {
|
||
content.style.display = 'none';
|
||
});
|
||
document.getElementById(`${tabName}-tab`).style.display = 'block';
|
||
|
||
this.currentTab = tabName;
|
||
|
||
// 保存当前页面状态
|
||
this.savePageState();
|
||
|
||
// 加载对应数据
|
||
switch(tabName) {
|
||
case 'dashboard':
|
||
this.loadDashboardData();
|
||
break;
|
||
case 'chat':
|
||
this.loadChatData();
|
||
break;
|
||
case 'agent':
|
||
this.loadAgentData();
|
||
break;
|
||
case 'alerts':
|
||
this.loadAlerts();
|
||
break;
|
||
case 'knowledge':
|
||
this.loadKnowledge();
|
||
break;
|
||
case 'workorders':
|
||
this.loadWorkOrders();
|
||
break;
|
||
case 'analytics':
|
||
this.loadAnalytics();
|
||
break;
|
||
case 'settings':
|
||
this.loadSettings();
|
||
break;
|
||
}
|
||
}
|
||
|
||
savePageState() {
|
||
const state = {
|
||
currentTab: this.currentTab,
|
||
timestamp: Date.now()
|
||
};
|
||
localStorage.setItem('tsp_dashboard_state', JSON.stringify(state));
|
||
}
|
||
|
||
restorePageState() {
|
||
try {
|
||
const savedState = localStorage.getItem('tsp_dashboard_state');
|
||
if (savedState) {
|
||
const state = JSON.parse(savedState);
|
||
// 如果状态保存时间不超过1小时,则恢复
|
||
if (Date.now() - state.timestamp < 3600000) {
|
||
this.switchTab(state.currentTab);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.warn('恢复页面状态失败:', error);
|
||
}
|
||
}
|
||
|
||
async loadInitialData() {
|
||
await Promise.all([
|
||
this.loadHealth(),
|
||
this.loadDashboardData(),
|
||
this.loadSystemInfo()
|
||
]);
|
||
}
|
||
|
||
startAutoRefresh() {
|
||
// 每5秒刷新健康状态
|
||
this.refreshIntervals.health = setInterval(() => {
|
||
this.loadHealth();
|
||
}, 5000);
|
||
|
||
// 每10秒刷新当前标签页数据
|
||
this.refreshIntervals.currentTab = setInterval(() => {
|
||
this.refreshCurrentTab();
|
||
}, 10000);
|
||
}
|
||
|
||
refreshCurrentTab() {
|
||
switch(this.currentTab) {
|
||
case 'dashboard':
|
||
this.loadDashboardData();
|
||
break;
|
||
case 'alerts':
|
||
this.loadAlerts();
|
||
break;
|
||
case 'agent':
|
||
this.loadAgentData();
|
||
break;
|
||
}
|
||
}
|
||
|
||
async loadHealth() {
|
||
try {
|
||
const response = await fetch('/api/health');
|
||
const data = await response.json();
|
||
this.updateHealthDisplay(data);
|
||
} catch (error) {
|
||
console.error('加载健康状态失败:', error);
|
||
}
|
||
}
|
||
|
||
updateHealthDisplay(health) {
|
||
const healthScore = health.health_score || 0;
|
||
const healthStatus = health.health_status || 'unknown';
|
||
|
||
// 更新健康指示器
|
||
const healthDot = document.getElementById('health-dot');
|
||
const healthStatusText = document.getElementById('health-status');
|
||
const systemHealthDot = document.getElementById('system-health-dot');
|
||
const systemHealthText = document.getElementById('system-health-text');
|
||
const healthProgress = document.getElementById('health-progress');
|
||
|
||
if (healthDot) {
|
||
healthDot.className = `health-dot ${healthStatus}`;
|
||
}
|
||
if (healthStatusText) {
|
||
healthStatusText.textContent = this.getHealthStatusText(healthStatus);
|
||
}
|
||
if (systemHealthDot) {
|
||
systemHealthDot.className = `health-dot ${healthStatus}`;
|
||
}
|
||
if (systemHealthText) {
|
||
systemHealthText.textContent = this.getHealthStatusText(healthStatus);
|
||
}
|
||
if (healthProgress) {
|
||
healthProgress.style.width = `${healthScore * 100}%`;
|
||
healthProgress.className = `progress-bar ${this.getHealthColor(healthScore)}`;
|
||
}
|
||
|
||
// 更新内存和CPU使用率
|
||
const memoryUsage = health.memory_usage || 0;
|
||
const memoryProgress = document.getElementById('memory-progress');
|
||
if (memoryProgress) {
|
||
memoryProgress.style.width = `${memoryUsage}%`;
|
||
}
|
||
|
||
const cpuUsage = health.cpu_usage || 0;
|
||
const cpuProgress = document.getElementById('cpu-progress');
|
||
if (cpuProgress) {
|
||
cpuProgress.style.width = `${cpuUsage}%`;
|
||
}
|
||
}
|
||
|
||
getHealthStatusText(status) {
|
||
const statusMap = {
|
||
'excellent': '优秀',
|
||
'good': '良好',
|
||
'fair': '一般',
|
||
'poor': '较差',
|
||
'critical': '严重',
|
||
'unknown': '未知'
|
||
};
|
||
return statusMap[status] || status;
|
||
}
|
||
|
||
getHealthColor(score) {
|
||
if (score >= 0.8) return 'bg-success';
|
||
if (score >= 0.6) return 'bg-info';
|
||
if (score >= 0.4) return 'bg-warning';
|
||
return 'bg-danger';
|
||
}
|
||
|
||
async loadDashboardData() {
|
||
try {
|
||
const [sessionsResponse, alertsResponse, workordersResponse, knowledgeResponse] = await Promise.all([
|
||
fetch('/api/chat/sessions'),
|
||
fetch('/api/alerts'),
|
||
fetch('/api/workorders'),
|
||
fetch('/api/knowledge/stats')
|
||
]);
|
||
|
||
const sessions = await sessionsResponse.json();
|
||
const alerts = await alertsResponse.json();
|
||
const workorders = await workordersResponse.json();
|
||
const knowledge = await knowledgeResponse.json();
|
||
|
||
// 更新统计卡片
|
||
document.getElementById('total-sessions').textContent = sessions.sessions?.length || 0;
|
||
document.getElementById('total-alerts').textContent = alerts.length || 0;
|
||
document.getElementById('total-workorders').textContent = workorders.filter(w => w.status === 'open').length || 0;
|
||
document.getElementById('knowledge-count').textContent = knowledge.total_entries || 0;
|
||
|
||
// 更新知识库详细统计
|
||
document.getElementById('knowledge-total').textContent = knowledge.total_entries || 0;
|
||
document.getElementById('knowledge-active').textContent = knowledge.active_entries || 0;
|
||
const confidencePercent = Math.round((knowledge.average_confidence || 0) * 100);
|
||
document.getElementById('knowledge-confidence').style.width = `${confidencePercent}%`;
|
||
document.getElementById('knowledge-confidence').setAttribute('aria-valuenow', confidencePercent);
|
||
document.getElementById('knowledge-confidence').textContent = `${confidencePercent}%`;
|
||
|
||
// 更新性能图表
|
||
this.updatePerformanceChart(sessions, alerts, workorders);
|
||
|
||
} catch (error) {
|
||
console.error('加载仪表板数据失败:', error);
|
||
}
|
||
}
|
||
|
||
initCharts() {
|
||
// 性能趋势图
|
||
const performanceCtx = document.getElementById('performanceChart');
|
||
if (performanceCtx) {
|
||
this.charts.performance = new Chart(performanceCtx, {
|
||
type: 'line',
|
||
data: {
|
||
labels: [],
|
||
datasets: [{
|
||
label: '活跃会话',
|
||
data: [],
|
||
borderColor: '#007bff',
|
||
backgroundColor: 'rgba(0, 123, 255, 0.1)',
|
||
tension: 0.4
|
||
}, {
|
||
label: '活跃预警',
|
||
data: [],
|
||
borderColor: '#dc3545',
|
||
backgroundColor: 'rgba(220, 53, 69, 0.1)',
|
||
tension: 0.4
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
scales: {
|
||
y: {
|
||
beginAtZero: true
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// 分析图表
|
||
const analyticsCtx = document.getElementById('analyticsChart');
|
||
if (analyticsCtx) {
|
||
this.charts.analytics = new Chart(analyticsCtx, {
|
||
type: 'line',
|
||
data: {
|
||
labels: [],
|
||
datasets: [{
|
||
label: '满意度',
|
||
data: [],
|
||
borderColor: '#28a745',
|
||
backgroundColor: 'rgba(40, 167, 69, 0.1)',
|
||
tension: 0.4
|
||
}, {
|
||
label: '解决时间(小时)',
|
||
data: [],
|
||
borderColor: '#ffc107',
|
||
backgroundColor: 'rgba(255, 193, 7, 0.1)',
|
||
tension: 0.4
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
scales: {
|
||
y: {
|
||
beginAtZero: true
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// 类别分布图
|
||
const categoryCtx = document.getElementById('categoryChart');
|
||
if (categoryCtx) {
|
||
this.charts.category = new Chart(categoryCtx, {
|
||
type: 'doughnut',
|
||
data: {
|
||
labels: [],
|
||
datasets: [{
|
||
data: [],
|
||
backgroundColor: [
|
||
'#007bff',
|
||
'#28a745',
|
||
'#ffc107',
|
||
'#dc3545',
|
||
'#17a2b8'
|
||
]
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
updatePerformanceChart(sessions, alerts, workorders) {
|
||
if (!this.charts.performance) return;
|
||
|
||
const now = new Date();
|
||
const labels = [];
|
||
const sessionData = [];
|
||
const alertData = [];
|
||
|
||
// 生成过去24小时的数据点
|
||
for (let i = 23; i >= 0; i--) {
|
||
const time = new Date(now.getTime() - i * 60 * 60 * 1000);
|
||
labels.push(time.getHours() + ':00');
|
||
sessionData.push(Math.floor(Math.random() * 10) + 5); // 模拟数据
|
||
alertData.push(Math.floor(Math.random() * 5)); // 模拟数据
|
||
}
|
||
|
||
this.charts.performance.data.labels = labels;
|
||
this.charts.performance.data.datasets[0].data = sessionData;
|
||
this.charts.performance.data.datasets[1].data = alertData;
|
||
this.charts.performance.update();
|
||
}
|
||
|
||
// 对话功能
|
||
async startChat() {
|
||
try {
|
||
const userId = document.getElementById('user-id').value;
|
||
const workOrderId = document.getElementById('work-order-id').value;
|
||
|
||
const response = await fetch('/api/chat/session', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
user_id: userId,
|
||
work_order_id: workOrderId ? parseInt(workOrderId) : null
|
||
})
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
this.sessionId = data.session_id;
|
||
document.getElementById('start-chat').disabled = true;
|
||
document.getElementById('end-chat').disabled = false;
|
||
document.getElementById('message-input').disabled = false;
|
||
document.getElementById('send-button').disabled = false;
|
||
document.getElementById('session-info').textContent = `会话ID: ${this.sessionId}`;
|
||
document.getElementById('connection-status').className = 'badge bg-success';
|
||
document.getElementById('connection-status').innerHTML = '<i class="fas fa-circle me-1"></i>已连接';
|
||
|
||
this.showNotification('对话已开始', 'success');
|
||
} else {
|
||
this.showNotification('开始对话失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('开始对话失败:', error);
|
||
this.showNotification('开始对话失败', 'error');
|
||
}
|
||
}
|
||
|
||
async endChat() {
|
||
try {
|
||
if (!this.sessionId) return;
|
||
|
||
const response = await fetch(`/api/chat/session/${this.sessionId}`, {
|
||
method: 'DELETE'
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
this.sessionId = null;
|
||
document.getElementById('start-chat').disabled = false;
|
||
document.getElementById('end-chat').disabled = true;
|
||
document.getElementById('message-input').disabled = true;
|
||
document.getElementById('send-button').disabled = true;
|
||
document.getElementById('session-info').textContent = '未开始对话';
|
||
document.getElementById('connection-status').className = 'badge bg-secondary';
|
||
document.getElementById('connection-status').innerHTML = '<i class="fas fa-circle me-1"></i>未连接';
|
||
|
||
this.showNotification('对话已结束', 'info');
|
||
} else {
|
||
this.showNotification('结束对话失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('结束对话失败:', error);
|
||
this.showNotification('结束对话失败', 'error');
|
||
}
|
||
}
|
||
|
||
async sendMessage() {
|
||
const messageInput = document.getElementById('message-input');
|
||
const message = messageInput.value.trim();
|
||
|
||
if (!message || !this.sessionId) return;
|
||
|
||
// 显示用户消息
|
||
this.addMessage('user', message);
|
||
messageInput.value = '';
|
||
|
||
// 发送消息到服务器
|
||
try {
|
||
const response = await fetch('/api/chat/message', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
session_id: this.sessionId,
|
||
message: message
|
||
})
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
this.addMessage('assistant', data.response, data.knowledge_used);
|
||
} else {
|
||
this.addMessage('assistant', '抱歉,处理您的消息时出现了错误。', null, true);
|
||
}
|
||
} catch (error) {
|
||
console.error('发送消息失败:', error);
|
||
this.addMessage('assistant', '网络连接错误,请稍后重试。', null, true);
|
||
}
|
||
}
|
||
|
||
addMessage(role, content, knowledgeUsed = null, isError = false) {
|
||
const messagesContainer = document.getElementById('chat-messages');
|
||
|
||
// 移除欢迎消息
|
||
const welcomeMsg = messagesContainer.querySelector('.text-center');
|
||
if (welcomeMsg) {
|
||
welcomeMsg.remove();
|
||
}
|
||
|
||
const messageDiv = document.createElement('div');
|
||
messageDiv.className = `message ${role}`;
|
||
|
||
const avatar = document.createElement('div');
|
||
avatar.className = 'message-avatar';
|
||
avatar.innerHTML = role === 'user' ? '<i class="fas fa-user"></i>' : '<i class="fas fa-robot"></i>';
|
||
|
||
const contentDiv = document.createElement('div');
|
||
contentDiv.className = 'message-content';
|
||
contentDiv.innerHTML = `
|
||
<div>${content}</div>
|
||
<div class="message-time">${new Date().toLocaleTimeString()}</div>
|
||
`;
|
||
|
||
if (knowledgeUsed && knowledgeUsed.length > 0) {
|
||
const knowledgeDiv = document.createElement('div');
|
||
knowledgeDiv.className = 'knowledge-info';
|
||
knowledgeDiv.innerHTML = `
|
||
<i class="fas fa-lightbulb me-1"></i>
|
||
使用了知识库: ${knowledgeUsed.map(k => k.question).join(', ')}
|
||
`;
|
||
contentDiv.appendChild(knowledgeDiv);
|
||
}
|
||
|
||
if (isError) {
|
||
contentDiv.style.borderLeft = '4px solid #dc3545';
|
||
}
|
||
|
||
messageDiv.appendChild(avatar);
|
||
messageDiv.appendChild(contentDiv);
|
||
messagesContainer.appendChild(messageDiv);
|
||
|
||
// 滚动到底部
|
||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||
}
|
||
|
||
// Agent功能
|
||
async toggleAgentMode(enabled) {
|
||
try {
|
||
const response = await fetch('/api/agent/toggle', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({ enabled })
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
this.isAgentMode = enabled;
|
||
this.showNotification(`Agent模式已${enabled ? '启用' : '禁用'}`, 'success');
|
||
this.loadAgentData();
|
||
} else {
|
||
this.showNotification('切换Agent模式失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('切换Agent模式失败:', error);
|
||
this.showNotification('切换Agent模式失败', 'error');
|
||
}
|
||
}
|
||
|
||
async loadAgentData() {
|
||
try {
|
||
const response = await fetch('/api/agent/status');
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
document.getElementById('agent-current-state').textContent = data.status || '未知';
|
||
document.getElementById('agent-active-goals').textContent = data.active_goals || 0;
|
||
document.getElementById('agent-available-tools').textContent = data.available_tools || 0;
|
||
|
||
// 更新工具列表
|
||
this.updateToolsList(data.tools || []);
|
||
|
||
// 更新执行历史
|
||
this.updateExecutionHistory(data.execution_history || []);
|
||
}
|
||
} catch (error) {
|
||
console.error('加载Agent数据失败:', error);
|
||
}
|
||
}
|
||
|
||
updateToolsList(tools) {
|
||
const toolsList = document.getElementById('tools-list');
|
||
if (tools.length === 0) {
|
||
toolsList.innerHTML = '<div class="empty-state"><i class="fas fa-tools"></i><p>暂无工具</p></div>';
|
||
return;
|
||
}
|
||
|
||
const toolsHtml = tools.map(tool => `
|
||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||
<div>
|
||
<strong>${tool.name}</strong>
|
||
<br>
|
||
<small class="text-muted">使用次数: ${tool.usage_count || 0}</small>
|
||
</div>
|
||
<div>
|
||
<span class="badge ${tool.success_rate >= 0.8 ? 'bg-success' : 'bg-warning'}">
|
||
${Math.round((tool.success_rate || 0) * 100)}%
|
||
</span>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
|
||
toolsList.innerHTML = toolsHtml;
|
||
}
|
||
|
||
updateExecutionHistory(history) {
|
||
const historyContainer = document.getElementById('agent-execution-history');
|
||
if (history.length === 0) {
|
||
historyContainer.innerHTML = '<div class="empty-state"><i class="fas fa-history"></i><p>暂无执行历史</p></div>';
|
||
return;
|
||
}
|
||
|
||
const historyHtml = history.slice(-5).map(item => `
|
||
<div class="border-bottom pb-2 mb-2">
|
||
<div class="d-flex justify-content-between">
|
||
<strong>${item.type || '未知任务'}</strong>
|
||
<small class="text-muted">${new Date(item.timestamp).toLocaleString()}</small>
|
||
</div>
|
||
<div class="text-muted small">${item.description || '无描述'}</div>
|
||
<div class="mt-1">
|
||
<span class="badge ${item.success ? 'bg-success' : 'bg-danger'}">
|
||
${item.success ? '成功' : '失败'}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
|
||
historyContainer.innerHTML = historyHtml;
|
||
}
|
||
|
||
async startAgentMonitoring() {
|
||
try {
|
||
const response = await fetch('/api/agent/monitoring/start', { method: 'POST' });
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
this.showNotification('Agent监控已启动', 'success');
|
||
} else {
|
||
this.showNotification('启动Agent监控失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('启动Agent监控失败:', error);
|
||
this.showNotification('启动Agent监控失败', 'error');
|
||
}
|
||
}
|
||
|
||
async stopAgentMonitoring() {
|
||
try {
|
||
const response = await fetch('/api/agent/monitoring/stop', { method: 'POST' });
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
this.showNotification('Agent监控已停止', 'success');
|
||
} else {
|
||
this.showNotification('停止Agent监控失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('停止Agent监控失败:', error);
|
||
this.showNotification('停止Agent监控失败', 'error');
|
||
}
|
||
}
|
||
|
||
async proactiveMonitoring() {
|
||
try {
|
||
const response = await fetch('/api/agent/proactive-monitoring', { method: 'POST' });
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
this.showNotification(`主动监控完成,发现 ${data.proactive_actions?.length || 0} 个行动机会`, 'info');
|
||
} else {
|
||
this.showNotification('主动监控失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('主动监控失败:', error);
|
||
this.showNotification('主动监控失败', 'error');
|
||
}
|
||
}
|
||
|
||
async intelligentAnalysis() {
|
||
try {
|
||
const response = await fetch('/api/agent/intelligent-analysis', { method: 'POST' });
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
this.showNotification('智能分析完成', 'success');
|
||
// 更新分析图表
|
||
this.updateAnalyticsChart(data.analysis);
|
||
} else {
|
||
this.showNotification('智能分析失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('智能分析失败:', error);
|
||
this.showNotification('智能分析失败', 'error');
|
||
}
|
||
}
|
||
|
||
updateAnalyticsChart(analysis) {
|
||
if (!this.charts.analytics || !analysis) return;
|
||
|
||
// 更新分析图表数据
|
||
const labels = analysis.trends?.dates || [];
|
||
const satisfactionData = analysis.trends?.satisfaction || [];
|
||
const resolutionTimeData = analysis.trends?.resolution_time || [];
|
||
|
||
this.charts.analytics.data.labels = labels;
|
||
this.charts.analytics.data.datasets[0].data = satisfactionData;
|
||
this.charts.analytics.data.datasets[1].data = resolutionTimeData;
|
||
this.charts.analytics.update();
|
||
}
|
||
|
||
// 预警管理
|
||
async loadAlerts() {
|
||
try {
|
||
const response = await fetch('/api/alerts');
|
||
const alerts = await response.json();
|
||
this.updateAlertsDisplay(alerts);
|
||
this.updateAlertStatistics(alerts);
|
||
} catch (error) {
|
||
console.error('加载预警失败:', error);
|
||
}
|
||
}
|
||
|
||
updateAlertsDisplay(alerts) {
|
||
const container = document.getElementById('alerts-container');
|
||
|
||
if (alerts.length === 0) {
|
||
container.innerHTML = `
|
||
<div class="empty-state">
|
||
<i class="fas fa-check-circle"></i>
|
||
<h5>暂无活跃预警</h5>
|
||
<p>系统运行正常,没有需要处理的预警</p>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
const alertsHtml = alerts.map(alert => `
|
||
<div class="alert-item ${alert.level}">
|
||
<div class="d-flex justify-content-between align-items-start">
|
||
<div class="flex-grow-1">
|
||
<div class="d-flex align-items-center mb-2">
|
||
<span class="badge bg-${this.getAlertColor(alert.level)} me-2">${this.getLevelText(alert.level)}</span>
|
||
<span class="fw-bold">${alert.rule_name || '未知规则'}</span>
|
||
<span class="ms-auto text-muted small">${this.formatTime(alert.created_at)}</span>
|
||
</div>
|
||
<div class="alert-message mb-2">${alert.message}</div>
|
||
<div class="alert-meta text-muted small">
|
||
类型: ${this.getTypeText(alert.alert_type)} |
|
||
级别: ${this.getLevelText(alert.level)}
|
||
</div>
|
||
</div>
|
||
<div class="ms-3">
|
||
<button class="btn btn-sm btn-outline-success" onclick="dashboard.resolveAlert(${alert.id})">
|
||
<i class="fas fa-check me-1"></i>解决
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
|
||
container.innerHTML = alertsHtml;
|
||
}
|
||
|
||
updateAlertStatistics(alerts) {
|
||
const stats = alerts.reduce((acc, alert) => {
|
||
acc[alert.level] = (acc[alert.level] || 0) + 1;
|
||
acc.total = (acc.total || 0) + 1;
|
||
return acc;
|
||
}, {});
|
||
|
||
document.getElementById('critical-alerts').textContent = stats.critical || 0;
|
||
document.getElementById('warning-alerts').textContent = stats.warning || 0;
|
||
document.getElementById('info-alerts').textContent = stats.info || 0;
|
||
document.getElementById('total-alerts-count').textContent = stats.total || 0;
|
||
}
|
||
|
||
async resolveAlert(alertId) {
|
||
try {
|
||
const response = await fetch(`/api/alerts/${alertId}/resolve`, { method: 'POST' });
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
this.showNotification('预警已解决', 'success');
|
||
this.loadAlerts();
|
||
} else {
|
||
this.showNotification('解决预警失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('解决预警失败:', error);
|
||
this.showNotification('解决预警失败', 'error');
|
||
}
|
||
}
|
||
|
||
// 知识库管理
|
||
async loadKnowledge(page = 1) {
|
||
try {
|
||
const response = await fetch(`/api/knowledge?page=${page}&per_page=10`);
|
||
const data = await response.json();
|
||
|
||
if (data.knowledge) {
|
||
this.updateKnowledgeDisplay(data.knowledge);
|
||
this.updateKnowledgePagination(data);
|
||
} else {
|
||
// 兼容旧格式
|
||
this.updateKnowledgeDisplay(data);
|
||
}
|
||
} catch (error) {
|
||
console.error('加载知识库失败:', error);
|
||
}
|
||
}
|
||
|
||
updateKnowledgeDisplay(knowledge) {
|
||
const container = document.getElementById('knowledge-list');
|
||
|
||
if (knowledge.length === 0) {
|
||
container.innerHTML = '<div class="empty-state"><i class="fas fa-database"></i><p>暂无知识条目</p></div>';
|
||
return;
|
||
}
|
||
|
||
const knowledgeHtml = knowledge.map(item => `
|
||
<div class="knowledge-item">
|
||
<div class="d-flex justify-content-between align-items-start">
|
||
<div class="flex-grow-1">
|
||
<h6 class="mb-1">${item.question}</h6>
|
||
<p class="text-muted mb-2">${item.answer}</p>
|
||
<div class="d-flex gap-3">
|
||
<small class="text-muted">分类: ${item.category}</small>
|
||
<small class="text-muted">置信度: ${Math.round(item.confidence_score * 100)}%</small>
|
||
<small class="text-muted">使用次数: ${item.usage_count || 0}</small>
|
||
<span class="badge ${item.is_verified ? 'bg-success' : 'bg-warning'}">
|
||
${item.is_verified ? '已验证' : '未验证'}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
<div class="ms-3">
|
||
<div class="btn-group" role="group">
|
||
${item.is_verified ?
|
||
`<button class="btn btn-sm btn-outline-warning" onclick="dashboard.unverifyKnowledge(${item.id})" title="取消验证">
|
||
<i class="fas fa-times-circle"></i>
|
||
</button>` :
|
||
`<button class="btn btn-sm btn-outline-success" onclick="dashboard.verifyKnowledge(${item.id})" title="验证">
|
||
<i class="fas fa-check-circle"></i>
|
||
</button>`
|
||
}
|
||
<button class="btn btn-sm btn-outline-danger" onclick="dashboard.deleteKnowledge(${item.id})" title="删除">
|
||
<i class="fas fa-trash"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
|
||
container.innerHTML = knowledgeHtml;
|
||
}
|
||
|
||
updateKnowledgePagination(data) {
|
||
const paginationContainer = document.getElementById('knowledge-pagination');
|
||
if (!paginationContainer) return;
|
||
|
||
const { page, total_pages, total } = data;
|
||
|
||
if (total_pages <= 1) {
|
||
paginationContainer.innerHTML = '';
|
||
return;
|
||
}
|
||
|
||
let paginationHtml = `
|
||
<div class="d-flex justify-content-between align-items-center">
|
||
<div>
|
||
<small class="text-muted">共 ${total} 条知识,第 ${page} / ${total_pages} 页</small>
|
||
</div>
|
||
<nav>
|
||
<ul class="pagination pagination-sm mb-0">
|
||
`;
|
||
|
||
// 上一页
|
||
if (page > 1) {
|
||
paginationHtml += `<li class="page-item"><a class="page-link" href="#" onclick="dashboard.loadKnowledge(${page - 1})">上一页</a></li>`;
|
||
}
|
||
|
||
// 页码
|
||
const startPage = Math.max(1, page - 2);
|
||
const endPage = Math.min(total_pages, page + 2);
|
||
|
||
for (let i = startPage; i <= endPage; i++) {
|
||
const activeClass = i === page ? 'active' : '';
|
||
paginationHtml += `<li class="page-item ${activeClass}"><a class="page-link" href="#" onclick="dashboard.loadKnowledge(${i})">${i}</a></li>`;
|
||
}
|
||
|
||
// 下一页
|
||
if (page < total_pages) {
|
||
paginationHtml += `<li class="page-item"><a class="page-link" href="#" onclick="dashboard.loadKnowledge(${page + 1})">下一页</a></li>`;
|
||
}
|
||
|
||
paginationHtml += `
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
`;
|
||
|
||
paginationContainer.innerHTML = paginationHtml;
|
||
}
|
||
|
||
async searchKnowledge() {
|
||
const query = document.getElementById('knowledge-search').value.trim();
|
||
if (!query) {
|
||
this.loadKnowledge();
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch(`/api/knowledge/search?q=${encodeURIComponent(query)}`);
|
||
const results = await response.json();
|
||
this.updateKnowledgeDisplay(results);
|
||
} catch (error) {
|
||
console.error('搜索知识库失败:', error);
|
||
}
|
||
}
|
||
|
||
async addKnowledge() {
|
||
const question = document.getElementById('knowledge-question').value.trim();
|
||
const answer = document.getElementById('knowledge-answer').value.trim();
|
||
const category = document.getElementById('knowledge-category').value;
|
||
const confidence = parseFloat(document.getElementById('knowledge-confidence').value);
|
||
|
||
if (!question || !answer) {
|
||
this.showNotification('请填写完整信息', 'warning');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch('/api/knowledge', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
question,
|
||
answer,
|
||
category,
|
||
confidence_score: confidence
|
||
})
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
this.showNotification('知识添加成功', 'success');
|
||
bootstrap.Modal.getInstance(document.getElementById('addKnowledgeModal')).hide();
|
||
document.getElementById('knowledge-form').reset();
|
||
this.loadKnowledge();
|
||
} else {
|
||
this.showNotification('添加知识失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('添加知识失败:', error);
|
||
this.showNotification('添加知识失败', 'error');
|
||
}
|
||
}
|
||
|
||
async uploadFile() {
|
||
const fileInput = document.getElementById('file-input');
|
||
const processMethod = document.getElementById('process-method').value;
|
||
const category = document.getElementById('file-category').value;
|
||
const confidence = parseFloat(document.getElementById('file-confidence').value);
|
||
const manualQuestion = document.getElementById('manual-question').value.trim();
|
||
|
||
if (!fileInput.files[0]) {
|
||
this.showNotification('请选择文件', 'warning');
|
||
return;
|
||
}
|
||
|
||
if (processMethod === 'manual' && !manualQuestion) {
|
||
this.showNotification('请指定问题', 'warning');
|
||
return;
|
||
}
|
||
|
||
// 显示进度条
|
||
const progressDiv = document.getElementById('upload-progress');
|
||
const progressBar = progressDiv.querySelector('.progress-bar');
|
||
const statusText = document.getElementById('upload-status');
|
||
|
||
progressDiv.style.display = 'block';
|
||
progressBar.style.width = '0%';
|
||
statusText.textContent = '正在上传文件...';
|
||
|
||
try {
|
||
const formData = new FormData();
|
||
formData.append('file', fileInput.files[0]);
|
||
formData.append('process_method', processMethod);
|
||
formData.append('category', category);
|
||
formData.append('confidence_score', confidence);
|
||
if (manualQuestion) {
|
||
formData.append('manual_question', manualQuestion);
|
||
}
|
||
|
||
// 模拟进度更新
|
||
let progress = 0;
|
||
const progressInterval = setInterval(() => {
|
||
progress += Math.random() * 20;
|
||
if (progress > 90) progress = 90;
|
||
progressBar.style.width = progress + '%';
|
||
|
||
if (progress < 30) {
|
||
statusText.textContent = '正在上传文件...';
|
||
} else if (progress < 60) {
|
||
statusText.textContent = '正在解析文件内容...';
|
||
} else if (progress < 90) {
|
||
statusText.textContent = '正在生成知识库...';
|
||
}
|
||
}, 500);
|
||
|
||
const response = await fetch('/api/knowledge/upload', {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
|
||
clearInterval(progressInterval);
|
||
progressBar.style.width = '100%';
|
||
statusText.textContent = '处理完成!';
|
||
|
||
const data = await response.json();
|
||
|
||
setTimeout(() => {
|
||
progressDiv.style.display = 'none';
|
||
|
||
if (data.success) {
|
||
this.showNotification(`文件处理成功,生成了 ${data.knowledge_count || 0} 条知识`, 'success');
|
||
bootstrap.Modal.getInstance(document.getElementById('uploadFileModal')).hide();
|
||
document.getElementById('file-upload-form').reset();
|
||
this.loadKnowledge();
|
||
} else {
|
||
this.showNotification(data.error || '文件处理失败', 'error');
|
||
}
|
||
}, 1000);
|
||
|
||
} catch (error) {
|
||
console.error('文件上传失败:', error);
|
||
progressDiv.style.display = 'none';
|
||
this.showNotification('文件上传失败', 'error');
|
||
}
|
||
}
|
||
|
||
async verifyKnowledge(knowledgeId) {
|
||
try {
|
||
const response = await fetch(`/api/knowledge/verify/${knowledgeId}`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
verified_by: 'admin'
|
||
})
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
this.showNotification('知识库验证成功', 'success');
|
||
this.loadKnowledge();
|
||
} else {
|
||
this.showNotification('知识库验证失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('验证知识库失败:', error);
|
||
this.showNotification('验证知识库失败', 'error');
|
||
}
|
||
}
|
||
|
||
async unverifyKnowledge(knowledgeId) {
|
||
try {
|
||
const response = await fetch(`/api/knowledge/unverify/${knowledgeId}`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
this.showNotification('取消验证成功', 'success');
|
||
this.loadKnowledge();
|
||
} else {
|
||
this.showNotification('取消验证失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('取消验证失败:', error);
|
||
this.showNotification('取消验证失败', 'error');
|
||
}
|
||
}
|
||
|
||
async deleteKnowledge(knowledgeId) {
|
||
if (!confirm('确定要删除这条知识库条目吗?')) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch(`/api/knowledge/delete/${knowledgeId}`, {
|
||
method: 'DELETE'
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
this.showNotification('知识库删除成功', 'success');
|
||
this.loadKnowledge();
|
||
} else {
|
||
this.showNotification('知识库删除失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('删除知识库失败:', error);
|
||
this.showNotification('删除知识库失败', 'error');
|
||
}
|
||
}
|
||
|
||
// 工单管理
|
||
async loadWorkOrders() {
|
||
try {
|
||
const statusFilter = document.getElementById('workorder-status-filter').value;
|
||
const priorityFilter = document.getElementById('workorder-priority-filter').value;
|
||
|
||
let url = '/api/workorders';
|
||
const params = new URLSearchParams();
|
||
if (statusFilter !== 'all') params.append('status', statusFilter);
|
||
if (priorityFilter !== 'all') params.append('priority', priorityFilter);
|
||
if (params.toString()) url += '?' + params.toString();
|
||
|
||
const response = await fetch(url);
|
||
const workorders = await response.json();
|
||
this.updateWorkOrdersDisplay(workorders);
|
||
this.updateWorkOrderStatistics(workorders);
|
||
} catch (error) {
|
||
console.error('加载工单失败:', error);
|
||
}
|
||
}
|
||
|
||
updateWorkOrdersDisplay(workorders) {
|
||
const container = document.getElementById('workorders-list');
|
||
|
||
if (workorders.length === 0) {
|
||
container.innerHTML = '<div class="empty-state"><i class="fas fa-tasks"></i><p>暂无工单</p></div>';
|
||
return;
|
||
}
|
||
|
||
const workordersHtml = workorders.map(workorder => `
|
||
<div class="work-order-item">
|
||
<div class="d-flex justify-content-between align-items-start">
|
||
<div class="flex-grow-1">
|
||
<h6 class="mb-1">${workorder.title}</h6>
|
||
<p class="text-muted mb-2">${workorder.description}</p>
|
||
<div class="d-flex gap-3">
|
||
<span class="badge bg-${this.getPriorityColor(workorder.priority)}">${this.getPriorityText(workorder.priority)}</span>
|
||
<span class="badge bg-${this.getStatusColor(workorder.status)}">${this.getStatusText(workorder.status)}</span>
|
||
<small class="text-muted">分类: ${workorder.category}</small>
|
||
<small class="text-muted">创建时间: ${new Date(workorder.created_at).toLocaleString()}</small>
|
||
</div>
|
||
</div>
|
||
<div class="ms-3">
|
||
<button class="btn btn-sm btn-outline-primary" onclick="dashboard.updateWorkOrder(${workorder.id})">
|
||
<i class="fas fa-edit"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`).join('');
|
||
|
||
container.innerHTML = workordersHtml;
|
||
}
|
||
|
||
updateWorkOrderStatistics(workorders) {
|
||
const stats = workorders.reduce((acc, wo) => {
|
||
acc.total = (acc.total || 0) + 1;
|
||
acc[wo.status] = (acc[wo.status] || 0) + 1;
|
||
return acc;
|
||
}, {});
|
||
|
||
document.getElementById('workorders-total').textContent = stats.total || 0;
|
||
document.getElementById('workorders-open').textContent = stats.open || 0;
|
||
document.getElementById('workorders-progress').textContent = stats.in_progress || 0;
|
||
document.getElementById('workorders-resolved').textContent = stats.resolved || 0;
|
||
}
|
||
|
||
async createWorkOrder() {
|
||
const title = document.getElementById('wo-title').value.trim();
|
||
const description = document.getElementById('wo-description').value.trim();
|
||
const category = document.getElementById('wo-category').value;
|
||
const priority = document.getElementById('wo-priority').value;
|
||
|
||
if (!title || !description) {
|
||
this.showNotification('请填写完整信息', 'warning');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await fetch('/api/workorders', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
title,
|
||
description,
|
||
category,
|
||
priority
|
||
})
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
this.showNotification('工单创建成功', 'success');
|
||
bootstrap.Modal.getInstance(document.getElementById('createWorkOrderModal')).hide();
|
||
document.getElementById('work-order-form').reset();
|
||
this.loadWorkOrders();
|
||
} else {
|
||
this.showNotification('创建工单失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('创建工单失败:', error);
|
||
this.showNotification('创建工单失败', 'error');
|
||
}
|
||
}
|
||
|
||
// 数据分析
|
||
async loadAnalytics() {
|
||
try {
|
||
const response = await fetch('/api/analytics');
|
||
const analytics = await response.json();
|
||
this.updateAnalyticsDisplay(analytics);
|
||
} catch (error) {
|
||
console.error('加载分析数据失败:', error);
|
||
}
|
||
}
|
||
|
||
updateAnalyticsDisplay(analytics) {
|
||
// 更新分析报告
|
||
const reportContainer = document.getElementById('analytics-report');
|
||
if (analytics.summary) {
|
||
reportContainer.innerHTML = `
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<h6>性能指标</h6>
|
||
<ul class="list-unstyled">
|
||
<li>总工单数: ${analytics.summary.total_orders || 0}</li>
|
||
<li>解决率: ${Math.round((analytics.summary.resolution_rate || 0) * 100)}%</li>
|
||
<li>平均解决时间: ${analytics.summary.avg_resolution_time_hours || 0}小时</li>
|
||
<li>平均满意度: ${analytics.summary.avg_satisfaction || 0}</li>
|
||
</ul>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<h6>趋势分析</h6>
|
||
<ul class="list-unstyled">
|
||
<li>工单趋势: ${analytics.summary.trends?.orders_trend ? '上升' : '下降'}</li>
|
||
<li>满意度趋势: ${analytics.summary.trends?.satisfaction_trend ? '上升' : '下降'}</li>
|
||
<li>解决时间趋势: ${analytics.summary.trends?.resolution_time_trend ? '上升' : '下降'}</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// 更新类别分布图
|
||
if (analytics.category_distribution && this.charts.category) {
|
||
const labels = Object.keys(analytics.category_distribution);
|
||
const data = Object.values(analytics.category_distribution);
|
||
|
||
this.charts.category.data.labels = labels;
|
||
this.charts.category.data.datasets[0].data = data;
|
||
this.charts.category.update();
|
||
}
|
||
}
|
||
|
||
// 系统设置
|
||
async loadSettings() {
|
||
try {
|
||
const response = await fetch('/api/settings');
|
||
const settings = await response.json();
|
||
this.updateSettingsDisplay(settings);
|
||
} catch (error) {
|
||
console.error('加载设置失败:', error);
|
||
}
|
||
}
|
||
|
||
updateSettingsDisplay(settings) {
|
||
if (settings.api_timeout) document.getElementById('api-timeout').value = settings.api_timeout;
|
||
if (settings.max_history) document.getElementById('max-history').value = settings.max_history;
|
||
if (settings.refresh_interval) document.getElementById('refresh-interval').value = settings.refresh_interval;
|
||
if (settings.auto_monitoring !== undefined) document.getElementById('auto-monitoring').checked = settings.auto_monitoring;
|
||
if (settings.agent_mode !== undefined) document.getElementById('agent-mode').checked = settings.agent_mode;
|
||
}
|
||
|
||
async saveSystemSettings() {
|
||
const settings = {
|
||
api_timeout: parseInt(document.getElementById('api-timeout').value),
|
||
max_history: parseInt(document.getElementById('max-history').value),
|
||
refresh_interval: parseInt(document.getElementById('refresh-interval').value),
|
||
auto_monitoring: document.getElementById('auto-monitoring').checked,
|
||
agent_mode: document.getElementById('agent-mode').checked
|
||
};
|
||
|
||
try {
|
||
const response = await fetch('/api/settings', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify(settings)
|
||
});
|
||
|
||
const data = await response.json();
|
||
if (data.success) {
|
||
this.showNotification('设置保存成功', 'success');
|
||
} else {
|
||
this.showNotification('保存设置失败', 'error');
|
||
}
|
||
} catch (error) {
|
||
console.error('保存设置失败:', error);
|
||
this.showNotification('保存设置失败', 'error');
|
||
}
|
||
}
|
||
|
||
async loadSystemInfo() {
|
||
try {
|
||
const response = await fetch('/api/system/info');
|
||
const info = await response.json();
|
||
this.updateSystemInfoDisplay(info);
|
||
} catch (error) {
|
||
console.error('加载系统信息失败:', error);
|
||
}
|
||
}
|
||
|
||
updateSystemInfoDisplay(info) {
|
||
const container = document.getElementById('system-info');
|
||
container.innerHTML = `
|
||
<div class="mb-3">
|
||
<strong>系统版本:</strong> ${info.version || '1.0.0'}
|
||
</div>
|
||
<div class="mb-3">
|
||
<strong>Python版本:</strong> ${info.python_version || '未知'}
|
||
</div>
|
||
<div class="mb-3">
|
||
<strong>数据库:</strong> ${info.database || 'SQLite'}
|
||
</div>
|
||
<div class="mb-3">
|
||
<strong>运行时间:</strong> ${info.uptime || '未知'}
|
||
</div>
|
||
<div class="mb-3">
|
||
<strong>内存使用:</strong> ${info.memory_usage || '0'} MB
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// 工具函数
|
||
getLevelText(level) {
|
||
const levelMap = {
|
||
'critical': '严重',
|
||
'error': '错误',
|
||
'warning': '警告',
|
||
'info': '信息'
|
||
};
|
||
return levelMap[level] || level;
|
||
}
|
||
|
||
getTypeText(type) {
|
||
const typeMap = {
|
||
'performance': '性能',
|
||
'quality': '质量',
|
||
'volume': '量级',
|
||
'system': '系统',
|
||
'business': '业务'
|
||
};
|
||
return typeMap[type] || type;
|
||
}
|
||
|
||
getAlertColor(level) {
|
||
const colorMap = {
|
||
'critical': 'danger',
|
||
'error': 'danger',
|
||
'warning': 'warning',
|
||
'info': 'info'
|
||
};
|
||
return colorMap[level] || 'secondary';
|
||
}
|
||
|
||
getPriorityText(priority) {
|
||
const priorityMap = {
|
||
'low': '低',
|
||
'medium': '中',
|
||
'high': '高',
|
||
'urgent': '紧急'
|
||
};
|
||
return priorityMap[priority] || priority;
|
||
}
|
||
|
||
getPriorityColor(priority) {
|
||
const colorMap = {
|
||
'low': 'secondary',
|
||
'medium': 'primary',
|
||
'high': 'warning',
|
||
'urgent': 'danger'
|
||
};
|
||
return colorMap[priority] || 'secondary';
|
||
}
|
||
|
||
getStatusText(status) {
|
||
const statusMap = {
|
||
'open': '待处理',
|
||
'in_progress': '处理中',
|
||
'resolved': '已解决',
|
||
'closed': '已关闭'
|
||
};
|
||
return statusMap[status] || status;
|
||
}
|
||
|
||
getStatusColor(status) {
|
||
const colorMap = {
|
||
'open': 'warning',
|
||
'in_progress': 'info',
|
||
'resolved': 'success',
|
||
'closed': 'secondary'
|
||
};
|
||
return colorMap[status] || 'secondary';
|
||
}
|
||
|
||
formatTime(timestamp) {
|
||
const date = new Date(timestamp);
|
||
const now = new Date();
|
||
const diff = now - date;
|
||
|
||
if (diff < 60000) { // 1分钟内
|
||
return '刚刚';
|
||
} else if (diff < 3600000) { // 1小时内
|
||
return `${Math.floor(diff / 60000)}分钟前`;
|
||
} else if (diff < 86400000) { // 1天内
|
||
return `${Math.floor(diff / 3600000)}小时前`;
|
||
} else {
|
||
return date.toLocaleDateString();
|
||
}
|
||
}
|
||
|
||
showNotification(message, type = 'info') {
|
||
const notification = document.createElement('div');
|
||
notification.className = `notification alert alert-${type === 'error' ? 'danger' : type} alert-dismissible fade show`;
|
||
notification.innerHTML = `
|
||
${message}
|
||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||
`;
|
||
|
||
document.body.appendChild(notification);
|
||
|
||
setTimeout(() => {
|
||
if (notification.parentNode) {
|
||
notification.parentNode.removeChild(notification);
|
||
}
|
||
}, 3000);
|
||
}
|
||
|
||
showCreateWorkOrderModal() {
|
||
const modal = new bootstrap.Modal(document.getElementById('createWorkOrderModal'));
|
||
modal.show();
|
||
}
|
||
}
|
||
|
||
// 初始化应用
|
||
let dashboard;
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
dashboard = new TSPDashboard();
|
||
});
|