// 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 = `
基本信息
| 工单号: |
${workorder.order_id || workorder.id} |
| 标题: |
${workorder.title} |
| 分类: |
${workorder.category} |
| 优先级: |
${this.getPriorityText(workorder.priority)} |
| 状态: |
${this.getStatusText(workorder.status)} |
| 创建时间: |
${new Date(workorder.created_at).toLocaleString()} |
| 更新时间: |
${new Date(workorder.updated_at).toLocaleString()} |
问题描述
${workorder.description}
${workorder.resolution ? `
解决方案
${workorder.resolution}
` : ''}
${workorder.satisfaction_score ? `
满意度评分
${workorder.satisfaction_score}/5.0
` : ''}
${workorder.conversations && workorder.conversations.length > 0 ? `
对话记录
${workorder.conversations.map(conv => `
${new Date(conv.timestamp).toLocaleString()}
用户: ${conv.user_message}
助手: ${conv.assistant_response}
`).join('')}
` : ''}
`;
// 移除已存在的模态框
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 = `
性能指标
- 总工单数: ${analytics.summary.total_orders || 0}
- 解决率: ${Math.round((analytics.summary.resolution_rate || 0) * 100)}%
- 平均解决时间: ${analytics.summary.avg_resolution_time_hours || 0}小时
- 平均满意度: ${analytics.summary.avg_satisfaction || 0}
趋势分析
- 工单趋势: ${analytics.summary.trends?.orders_trend ? '上升' : '下降'}
- 满意度趋势: ${analytics.summary.trends?.satisfaction_trend ? '上升' : '下降'}
- 解决时间趋势: ${analytics.summary.trends?.resolution_time_trend ? '上升' : '下降'}
`;
}
// 更新类别分布图
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();
});