// 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.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 = '已连接'; 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 = '未连接'; 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' ? '' : ''; const contentDiv = document.createElement('div'); contentDiv.className = 'message-content'; contentDiv.innerHTML = `
${content}
${new Date().toLocaleTimeString()}
`; if (knowledgeUsed && knowledgeUsed.length > 0) { const knowledgeDiv = document.createElement('div'); knowledgeDiv.className = 'knowledge-info'; knowledgeDiv.innerHTML = ` 使用了知识库: ${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.updateAgentExecutionHistory(data.execution_history || []); } } catch (error) { console.error('加载Agent数据失败:', error); } } updateToolsList(tools) { const toolsList = document.getElementById('tools-list'); if (tools.length === 0) { toolsList.innerHTML = '

暂无工具

'; return; } const toolsHtml = tools.map(tool => `
${tool.name}
使用次数: ${tool.usage_count || 0}
${Math.round((tool.success_rate || 0) * 100)}%
`).join(''); toolsList.innerHTML = toolsHtml; } updateExecutionHistory(history) { const historyContainer = document.getElementById('agent-execution-history'); if (history.length === 0) { historyContainer.innerHTML = '

暂无执行历史

'; return; } const historyHtml = history.slice(-5).map(item => `
${item.type || '未知任务'} ${new Date(item.timestamp).toLocaleString()}
${item.description || '无描述'}
${item.success ? '成功' : '失败'}
`).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 = `
暂无活跃预警

系统运行正常,没有需要处理的预警

`; return; } const alertsHtml = alerts.map(alert => `
${this.getLevelText(alert.level)} ${alert.rule_name || '未知规则'} ${this.formatTime(alert.created_at)}
${alert.message}
类型: ${this.getTypeText(alert.alert_type)} | 级别: ${this.getLevelText(alert.level)}
`).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 = '

暂无知识条目

'; return; } const knowledgeHtml = knowledge.map(item => `
${item.question}

${item.answer}

分类: ${item.category} 置信度: ${Math.round(item.confidence_score * 100)}% 使用次数: ${item.usage_count || 0} ${item.is_verified ? '已验证' : '未验证'}
${item.is_verified ? `` : `` }
`).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 = `
共 ${total} 条知识,第 ${page} / ${total_pages} 页
`; 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 = '

暂无工单

'; return; } const workordersHtml = workorders.map(workorder => `
${workorder.title}

${workorder.description}

${this.getPriorityText(workorder.priority)} ${this.getStatusText(workorder.status)} 分类: ${workorder.category} 创建时间: ${new Date(workorder.created_at).toLocaleString()}
`).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 viewWorkOrderDetails(workorderId) { try { const response = await fetch(`/api/workorders/${workorderId}`); const workorder = await response.json(); if (workorder.error) { this.showNotification('获取工单详情失败', 'error'); return; } this.showWorkOrderDetailsModal(workorder); } catch (error) { console.error('获取工单详情失败:', error); this.showNotification('获取工单详情失败', 'error'); } } showWorkOrderDetailsModal(workorder) { // 创建模态框HTML const modalHtml = ` `; // 移除已存在的模态框 const existingModal = document.getElementById('workOrderDetailsModal'); if (existingModal) { existingModal.remove(); } // 添加新的模态框到页面 document.body.insertAdjacentHTML('beforeend', modalHtml); // 显示模态框 const modal = new bootstrap.Modal(document.getElementById('workOrderDetailsModal')); modal.show(); // 模态框关闭时移除DOM元素 document.getElementById('workOrderDetailsModal').addEventListener('hidden.bs.modal', function() { this.remove(); }); } async updateWorkOrder(workorderId) { try { // 获取工单详情 const response = await fetch(`/api/workorders/${workorderId}`); const workorder = await response.json(); if (response.ok) { this.showEditWorkOrderModal(workorder); } else { throw new Error(workorder.error || '获取工单详情失败'); } } catch (error) { console.error('获取工单详情失败:', error); this.showNotification('获取工单详情失败: ' + error.message, 'error'); } } showEditWorkOrderModal(workorder) { // 创建编辑工单模态框 const modalHtml = ` `; // 移除已存在的模态框 const existingModal = document.getElementById('editWorkOrderModal'); if (existingModal) { existingModal.remove(); } // 添加新模态框到页面 document.body.insertAdjacentHTML('beforeend', modalHtml); // 显示模态框 const modal = new bootstrap.Modal(document.getElementById('editWorkOrderModal')); modal.show(); // 模态框关闭时清理 document.getElementById('editWorkOrderModal').addEventListener('hidden.bs.modal', function() { this.remove(); }); } async saveWorkOrder(workorderId) { try { // 获取表单数据 const formData = { title: document.getElementById('editTitle').value, description: document.getElementById('editDescription').value, category: document.getElementById('editCategory').value, priority: document.getElementById('editPriority').value, status: document.getElementById('editStatus').value, resolution: document.getElementById('editResolution').value, satisfaction_score: parseInt(document.getElementById('editSatisfactionScore').value) || null }; // 验证必填字段 if (!formData.title.trim() || !formData.description.trim()) { this.showNotification('标题和描述不能为空', 'error'); return; } // 发送更新请求 const response = await fetch(`/api/workorders/${workorderId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formData) }); const result = await response.json(); if (response.ok) { this.showNotification('工单更新成功', 'success'); // 关闭模态框 const modal = bootstrap.Modal.getInstance(document.getElementById('editWorkOrderModal')); modal.hide(); // 刷新工单列表 this.loadWorkOrders(); } else { throw new Error(result.error || '更新工单失败'); } } catch (error) { console.error('更新工单失败:', error); this.showNotification('更新工单失败: ' + error.message, 'error'); } } // 工单导入功能 showImportModal() { // 显示导入模态框 const modal = new bootstrap.Modal(document.getElementById('importWorkOrderModal')); modal.show(); // 重置表单 document.getElementById('excel-file-input').value = ''; document.getElementById('import-progress').classList.add('d-none'); document.getElementById('import-result').classList.add('d-none'); } async downloadTemplate() { try { const response = await fetch('/api/workorders/import/template'); const result = await response.json(); if (result.success) { // 创建下载链接 const link = document.createElement('a'); link.href = result.template_url; link.download = '工单导入模板.xlsx'; document.body.appendChild(link); link.click(); document.body.removeChild(link); this.showNotification('模板下载成功', 'success'); } else { throw new Error(result.error || '下载模板失败'); } } catch (error) { console.error('下载模板失败:', error); this.showNotification('下载模板失败: ' + error.message, 'error'); } } async importWorkOrders() { const fileInput = document.getElementById('excel-file-input'); const file = fileInput.files[0]; if (!file) { this.showNotification('请选择要导入的Excel文件', 'error'); return; } // 验证文件类型 if (!file.name.match(/\.(xlsx|xls)$/)) { this.showNotification('只支持Excel文件(.xlsx, .xls)', 'error'); return; } // 验证文件大小 if (file.size > 16 * 1024 * 1024) { this.showNotification('文件大小不能超过16MB', 'error'); return; } // 显示进度条 document.getElementById('import-progress').classList.remove('d-none'); document.getElementById('import-result').classList.add('d-none'); try { // 创建FormData const formData = new FormData(); formData.append('file', file); // 发送导入请求 const response = await fetch('/api/workorders/import', { method: 'POST', body: formData }); const result = await response.json(); if (response.ok && result.success) { // 显示成功消息 document.getElementById('import-progress').classList.add('d-none'); document.getElementById('import-result').classList.remove('d-none'); document.getElementById('import-success-message').textContent = `成功导入 ${result.imported_count} 个工单`; this.showNotification(result.message, 'success'); // 刷新工单列表 this.loadWorkOrders(); // 3秒后关闭模态框 setTimeout(() => { const modal = bootstrap.Modal.getInstance(document.getElementById('importWorkOrderModal')); modal.hide(); }, 3000); } else { throw new Error(result.error || '导入工单失败'); } } catch (error) { console.error('导入工单失败:', error); document.getElementById('import-progress').classList.add('d-none'); this.showNotification('导入工单失败: ' + error.message, 'error'); } } // 数据分析 async loadAnalytics() { try { const response = await fetch('/api/analytics'); const analytics = await response.json(); this.updateAnalyticsDisplay(analytics); this.initializeCharts(); } catch (error) { console.error('加载分析数据失败:', error); } } // 初始化图表 initializeCharts() { this.charts = {}; this.updateCharts(); } // 更新所有图表 async updateCharts() { try { const timeRange = document.getElementById('timeRange').value; const chartType = document.getElementById('chartType').value; const dataDimension = document.getElementById('dataDimension').value; // 获取数据 const response = await fetch(`/api/analytics?timeRange=${timeRange}&dimension=${dataDimension}`); const data = await response.json(); // 更新统计卡片 this.updateStatisticsCards(data); // 更新图表 this.updateMainChart(data, chartType); this.updateDistributionChart(data); this.updateTrendChart(data); this.updatePriorityChart(data); // 更新分析报告 this.updateAnalyticsReport(data); } catch (error) { console.error('更新图表失败:', error); this.showNotification('更新图表失败: ' + error.message, 'error'); } } // 更新统计卡片 updateStatisticsCards(data) { const total = data.workorders?.total || 0; const open = data.workorders?.open || 0; const resolved = data.workorders?.resolved || 0; const avgSatisfaction = data.satisfaction?.average || 0; document.getElementById('totalWorkorders').textContent = total; document.getElementById('openWorkorders').textContent = open; document.getElementById('resolvedWorkorders').textContent = resolved; document.getElementById('avgSatisfaction').textContent = avgSatisfaction.toFixed(1); // 更新进度条 if (total > 0) { document.getElementById('openProgress').style.width = `${(open / total) * 100}%`; document.getElementById('resolvedProgress').style.width = `${(resolved / total) * 100}%`; document.getElementById('satisfactionProgress').style.width = `${(avgSatisfaction / 5) * 100}%`; } } // 更新主图表 updateMainChart(data, chartType) { const ctx = document.getElementById('mainChart').getContext('2d'); // 销毁现有图表 if (this.charts.mainChart) { this.charts.mainChart.destroy(); } const chartData = this.prepareChartData(data, chartType); this.charts.mainChart = new Chart(ctx, { type: chartType, data: chartData, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: '数据分析趋势' }, legend: { display: true, position: 'top' } }, scales: chartType === 'pie' || chartType === 'doughnut' ? {} : { x: { display: true, title: { display: true, text: '时间' } }, y: { display: true, title: { display: true, text: '数量' } } } } }); } // 更新分布图表 updateDistributionChart(data) { const ctx = document.getElementById('distributionChart').getContext('2d'); if (this.charts.distributionChart) { this.charts.distributionChart.destroy(); } const categories = data.workorders?.by_category || {}; const labels = Object.keys(categories); const values = Object.values(categories); this.charts.distributionChart = new Chart(ctx, { type: 'doughnut', data: { labels: labels, datasets: [{ data: values, backgroundColor: [ '#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40' ] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: '工单分类分布' }, legend: { display: true, position: 'bottom' } } } }); } // 更新趋势图表 updateTrendChart(data) { const ctx = document.getElementById('trendChart').getContext('2d'); if (this.charts.trendChart) { this.charts.trendChart.destroy(); } const trendData = data.trend || []; const labels = trendData.map(item => item.date); const workorders = trendData.map(item => item.workorders); const alerts = trendData.map(item => item.alerts); this.charts.trendChart = new Chart(ctx, { type: 'line', data: { labels: labels, datasets: [{ label: '工单数量', data: workorders, borderColor: '#36A2EB', backgroundColor: 'rgba(54, 162, 235, 0.1)', tension: 0.4 }, { label: '预警数量', data: alerts, borderColor: '#FF6384', backgroundColor: 'rgba(255, 99, 132, 0.1)', tension: 0.4 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: '时间趋势分析' } }, scales: { x: { display: true, title: { display: true, text: '日期' } }, y: { display: true, title: { display: true, text: '数量' } } } } }); } // 更新优先级图表 updatePriorityChart(data) { const ctx = document.getElementById('priorityChart').getContext('2d'); if (this.charts.priorityChart) { this.charts.priorityChart.destroy(); } const priorities = data.workorders?.by_priority || {}; const labels = Object.keys(priorities).map(p => this.getPriorityText(p)); const values = Object.values(priorities); this.charts.priorityChart = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: '工单数量', data: values, backgroundColor: [ '#28a745', // 低 - 绿色 '#ffc107', // 中 - 黄色 '#fd7e14', // 高 - 橙色 '#dc3545' // 紧急 - 红色 ] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { title: { display: true, text: '优先级分布' } }, scales: { y: { beginAtZero: true } } } }); } // 准备图表数据 prepareChartData(data, chartType) { const trendData = data.trend || []; const labels = trendData.map(item => item.date); const workorders = trendData.map(item => item.workorders); if (chartType === 'pie' || chartType === 'doughnut') { const categories = data.workorders?.by_category || {}; return { labels: Object.keys(categories), datasets: [{ data: Object.values(categories), backgroundColor: [ '#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40' ] }] }; } else { return { labels: labels, datasets: [{ label: '工单数量', data: workorders, borderColor: '#36A2EB', backgroundColor: 'rgba(54, 162, 235, 0.1)', tension: chartType === 'line' ? 0.4 : 0 }] }; } } // 导出图表 exportChart(chartId) { if (this.charts[chartId]) { const link = document.createElement('a'); link.download = `${chartId}_chart.png`; link.href = this.charts[chartId].toBase64Image(); link.click(); } } // 全屏图表 fullscreenChart(chartId) { // 这里可以实现全屏显示功能 this.showNotification('全屏功能开发中', 'info'); } // 导出报告 async exportReport() { try { const response = await fetch('/api/analytics/export'); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = 'analytics_report.xlsx'; link.click(); window.URL.revokeObjectURL(url); } catch (error) { console.error('导出报告失败:', error); this.showNotification('导出报告失败: ' + error.message, 'error'); } } // 打印报告 printReport() { window.print(); } // Agent执行历史相关功能 async refreshAgentHistory() { try { const response = await fetch('/api/agent/action-history?limit=20'); const data = await response.json(); if (data.success) { this.updateAgentExecutionHistory(data.history); this.showNotification(`已加载 ${data.count} 条执行历史`, 'success'); } else { throw new Error(data.error || '获取执行历史失败'); } } catch (error) { console.error('刷新Agent历史失败:', error); this.showNotification('刷新Agent历史失败: ' + error.message, 'error'); } } async triggerSampleAction() { try { const response = await fetch('/api/agent/trigger-sample', { method: 'POST', headers: { 'Content-Type': 'application/json', } }); const data = await response.json(); if (data.success) { this.showNotification(data.message, 'success'); // 刷新执行历史 await this.refreshAgentHistory(); } else { throw new Error(data.error || '触发示例动作失败'); } } catch (error) { console.error('触发示例动作失败:', error); this.showNotification('触发示例动作失败: ' + error.message, 'error'); } } async clearAgentHistory() { if (!confirm('确定要清空Agent执行历史吗?此操作不可恢复。')) { return; } try { const response = await fetch('/api/agent/clear-history', { method: 'POST', headers: { 'Content-Type': 'application/json', } }); const data = await response.json(); if (data.success) { this.showNotification(data.message, 'success'); // 清空显示 this.updateAgentExecutionHistory([]); } else { throw new Error(data.error || '清空历史失败'); } } catch (error) { console.error('清空Agent历史失败:', error); this.showNotification('清空Agent历史失败: ' + error.message, 'error'); } } updateAgentExecutionHistory(history) { const container = document.getElementById('agent-execution-history'); if (!history || history.length === 0) { container.innerHTML = `

暂无执行历史

`; return; } const historyHtml = history.map(record => { const startTime = new Date(record.start_time * 1000).toLocaleString(); const endTime = new Date(record.end_time * 1000).toLocaleString(); const duration = Math.round((record.end_time - record.start_time) * 100) / 100; const priorityColor = { 5: 'danger', 4: 'warning', 3: 'info', 2: 'secondary', 1: 'light' }[record.priority] || 'secondary'; const confidenceColor = record.confidence >= 0.8 ? 'success' : record.confidence >= 0.5 ? 'warning' : 'danger'; return `
${record.description}
优先级 ${record.priority} 置信度 ${(record.confidence * 100).toFixed(0)}% ${record.success ? '成功' : '失败'}
开始: ${startTime} | 耗时: ${duration}秒
${record.action_type}
${record.result && record.result.message ? `
结果: ${record.result.message}
` : ''}
`; }).join(''); container.innerHTML = historyHtml; } // 更新分析报告 updateAnalyticsReport(data) { const reportContainer = document.getElementById('analytics-report'); if (!reportContainer) return; const summary = data.summary || {}; const workorders = data.workorders || {}; const satisfaction = data.satisfaction || {}; const alerts = data.alerts || {}; const performance = data.performance || {}; const reportHtml = `
工单统计概览
总工单数 ${workorders.total || 0}
待处理 ${workorders.open || 0}
处理中 ${workorders.in_progress || 0}
已解决 ${workorders.resolved || 0}
已关闭 ${workorders.closed || 0}
满意度分析
平均满意度 ${satisfaction.average || 0}/5.0
5星评价 ${satisfaction.distribution?.['5'] || 0} 个
4星评价 ${satisfaction.distribution?.['4'] || 0} 个
3星评价 ${satisfaction.distribution?.['3'] || 0} 个
2星及以下 ${(satisfaction.distribution?.['2'] || 0) + (satisfaction.distribution?.['1'] || 0)} 个
预警统计
总预警数 ${alerts.total || 0}
活跃预警 ${alerts.active || 0}
已解决 ${alerts.resolved || 0}
性能指标
响应时间 ${performance.response_time || 0} 秒
系统可用性 ${performance.uptime || 0}%
错误率 ${performance.error_rate || 0}%
吞吐量 ${performance.throughput || 0} 请求/小时
关键指标总结
${summary.resolution_rate || 0}%

解决率

${summary.avg_satisfaction || 0}

平均满意度

${summary.active_alerts || 0}

活跃预警

${summary.total_workorders || 0}

总工单数

`; reportContainer.innerHTML = reportHtml; } updateAnalyticsDisplay(analytics) { // 更新分析报告 const reportContainer = document.getElementById('analytics-report'); if (analytics.summary) { reportContainer.innerHTML = `
性能指标
趋势分析
`; } // 更新类别分布图 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 = `
系统版本: ${info.version || '1.0.0'}
Python版本: ${info.python_version || '未知'}
数据库: ${info.database || 'SQLite'}
运行时间: ${info.uptime || '未知'}
内存使用: ${info.memory_usage || '0'} MB
`; } // 工具函数 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} `; 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(); });