feat: 自动提交 - 周一 2025/09/22 16:28:00.19
This commit is contained in:
@@ -1373,19 +1373,37 @@ class TSPDashboard {
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加预警列表的批量操作头部
|
||||
const headerHtml = `
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="checkbox" id="select-all-alerts" class="form-check-input me-2" onchange="dashboard.toggleSelectAllAlerts()">
|
||||
<label for="select-all-alerts" class="form-check-label">全选</label>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-danger" id="batch-delete-alerts" onclick="dashboard.batchDeleteAlerts()" disabled>
|
||||
<i class="fas fa-trash me-1"></i>批量删除
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const alertsHtml = alerts.map(alert => `
|
||||
<div class="alert-item ${alert.level}">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<span class="badge bg-${this.getAlertColor(alert.level)} me-2">${this.getLevelText(alert.level)}</span>
|
||||
<span class="fw-bold">${alert.rule_name || '未知规则'}</span>
|
||||
<span class="ms-auto text-muted small">${this.formatTime(alert.created_at)}</span>
|
||||
</div>
|
||||
<div class="alert-message mb-2">${alert.message}</div>
|
||||
<div class="alert-meta text-muted small">
|
||||
类型: ${this.getTypeText(alert.alert_type)} |
|
||||
级别: ${this.getLevelText(alert.level)}
|
||||
<div class="d-flex align-items-start">
|
||||
<input type="checkbox" class="form-check-input me-2 alert-checkbox" value="${alert.id}" onchange="dashboard.updateBatchDeleteAlertsButton()">
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<span class="badge bg-${this.getAlertColor(alert.level)} me-2">${this.getLevelText(alert.level)}</span>
|
||||
<span class="fw-bold">${alert.rule_name || '未知规则'}</span>
|
||||
<span class="ms-auto text-muted small">${this.formatTime(alert.created_at)}</span>
|
||||
</div>
|
||||
<div class="alert-message mb-2">${alert.message}</div>
|
||||
<div class="alert-meta text-muted small">
|
||||
类型: ${this.getTypeText(alert.alert_type)} |
|
||||
级别: ${this.getLevelText(alert.level)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ms-3">
|
||||
@@ -1397,7 +1415,7 @@ class TSPDashboard {
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
container.innerHTML = alertsHtml;
|
||||
container.innerHTML = headerHtml + alertsHtml;
|
||||
}
|
||||
|
||||
updateAlertStatistics(alerts) {
|
||||
@@ -1413,6 +1431,72 @@ class TSPDashboard {
|
||||
document.getElementById('total-alerts-count').textContent = stats.total || 0;
|
||||
}
|
||||
|
||||
// 预警批量删除功能
|
||||
toggleSelectAllAlerts() {
|
||||
const selectAllCheckbox = document.getElementById('select-all-alerts');
|
||||
const alertCheckboxes = document.querySelectorAll('.alert-checkbox');
|
||||
|
||||
alertCheckboxes.forEach(checkbox => {
|
||||
checkbox.checked = selectAllCheckbox.checked;
|
||||
});
|
||||
|
||||
this.updateBatchDeleteAlertsButton();
|
||||
}
|
||||
|
||||
updateBatchDeleteAlertsButton() {
|
||||
const selectedCheckboxes = document.querySelectorAll('.alert-checkbox:checked');
|
||||
const batchDeleteBtn = document.getElementById('batch-delete-alerts');
|
||||
|
||||
if (batchDeleteBtn) {
|
||||
batchDeleteBtn.disabled = selectedCheckboxes.length === 0;
|
||||
batchDeleteBtn.textContent = selectedCheckboxes.length > 0
|
||||
? `批量删除 (${selectedCheckboxes.length})`
|
||||
: '批量删除';
|
||||
}
|
||||
}
|
||||
|
||||
async batchDeleteAlerts() {
|
||||
const selectedCheckboxes = document.querySelectorAll('.alert-checkbox:checked');
|
||||
const selectedIds = Array.from(selectedCheckboxes).map(cb => parseInt(cb.value));
|
||||
|
||||
if (selectedIds.length === 0) {
|
||||
this.showNotification('请选择要删除的预警', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`确定要删除选中的 ${selectedIds.length} 个预警吗?此操作不可撤销。`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/batch-delete/alerts', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ ids: selectedIds })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.showNotification(data.message, 'success');
|
||||
|
||||
// 清除缓存并强制刷新
|
||||
this.cache.delete('alerts');
|
||||
await this.loadAlerts();
|
||||
|
||||
// 重置批量删除按钮状态
|
||||
this.updateBatchDeleteAlertsButton();
|
||||
} else {
|
||||
this.showNotification(data.error || '批量删除失败', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('批量删除预警失败:', error);
|
||||
this.showNotification('批量删除预警失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async resolveAlert(alertId) {
|
||||
try {
|
||||
const response = await fetch(`/api/alerts/${alertId}/resolve`, { method: 'POST' });
|
||||
@@ -1456,19 +1540,37 @@ class TSPDashboard {
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加知识库列表的批量操作头部
|
||||
const headerHtml = `
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="checkbox" id="select-all-knowledge" class="form-check-input me-2" onchange="dashboard.toggleSelectAllKnowledge()">
|
||||
<label for="select-all-knowledge" class="form-check-label">全选</label>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-danger" id="batch-delete-knowledge" onclick="dashboard.batchDeleteKnowledge()" disabled>
|
||||
<i class="fas fa-trash me-1"></i>批量删除
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const knowledgeHtml = knowledge.map(item => `
|
||||
<div class="knowledge-item">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-1">${item.question}</h6>
|
||||
<p class="text-muted mb-2">${item.answer}</p>
|
||||
<div class="d-flex gap-3">
|
||||
<small class="text-muted">分类: ${item.category}</small>
|
||||
<small class="text-muted">置信度: ${Math.round(item.confidence_score * 100)}%</small>
|
||||
<small class="text-muted">使用次数: ${item.usage_count || 0}</small>
|
||||
<span class="badge ${item.is_verified ? 'bg-success' : 'bg-warning'}">
|
||||
${item.is_verified ? '已验证' : '未验证'}
|
||||
</span>
|
||||
<div class="d-flex align-items-start">
|
||||
<input type="checkbox" class="form-check-input me-2 knowledge-checkbox" value="${item.id}" onchange="dashboard.updateBatchDeleteKnowledgeButton()">
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-1">${item.question}</h6>
|
||||
<p class="text-muted mb-2">${item.answer}</p>
|
||||
<div class="d-flex gap-3">
|
||||
<small class="text-muted">分类: ${item.category}</small>
|
||||
<small class="text-muted">置信度: ${Math.round(item.confidence_score * 100)}%</small>
|
||||
<small class="text-muted">使用次数: ${item.usage_count || 0}</small>
|
||||
<span class="badge ${item.is_verified ? 'bg-success' : 'bg-warning'}">
|
||||
${item.is_verified ? '已验证' : '未验证'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ms-3">
|
||||
@@ -1490,7 +1592,7 @@ class TSPDashboard {
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
container.innerHTML = knowledgeHtml;
|
||||
container.innerHTML = headerHtml + knowledgeHtml;
|
||||
}
|
||||
|
||||
updateKnowledgePagination(data) {
|
||||
@@ -1750,24 +1852,115 @@ class TSPDashboard {
|
||||
}
|
||||
}
|
||||
|
||||
// 工单管理
|
||||
async loadWorkOrders() {
|
||||
// 知识库批量删除功能
|
||||
toggleSelectAllKnowledge() {
|
||||
const selectAllCheckbox = document.getElementById('select-all-knowledge');
|
||||
const knowledgeCheckboxes = document.querySelectorAll('.knowledge-checkbox');
|
||||
|
||||
knowledgeCheckboxes.forEach(checkbox => {
|
||||
checkbox.checked = selectAllCheckbox.checked;
|
||||
});
|
||||
|
||||
this.updateBatchDeleteKnowledgeButton();
|
||||
}
|
||||
|
||||
updateBatchDeleteKnowledgeButton() {
|
||||
const selectedCheckboxes = document.querySelectorAll('.knowledge-checkbox:checked');
|
||||
const batchDeleteBtn = document.getElementById('batch-delete-knowledge');
|
||||
|
||||
if (batchDeleteBtn) {
|
||||
batchDeleteBtn.disabled = selectedCheckboxes.length === 0;
|
||||
batchDeleteBtn.textContent = selectedCheckboxes.length > 0
|
||||
? `批量删除 (${selectedCheckboxes.length})`
|
||||
: '批量删除';
|
||||
}
|
||||
}
|
||||
|
||||
async batchDeleteKnowledge() {
|
||||
const selectedCheckboxes = document.querySelectorAll('.knowledge-checkbox:checked');
|
||||
const selectedIds = Array.from(selectedCheckboxes).map(cb => parseInt(cb.value));
|
||||
|
||||
if (selectedIds.length === 0) {
|
||||
this.showNotification('请选择要删除的知识库条目', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`确定要删除选中的 ${selectedIds.length} 个知识库条目吗?此操作不可撤销。`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const statusFilter = document.getElementById('workorder-status-filter').value;
|
||||
const priorityFilter = document.getElementById('workorder-priority-filter').value;
|
||||
const response = await fetch('/api/batch-delete/knowledge', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ ids: selectedIds })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.showNotification(data.message, 'success');
|
||||
|
||||
// 清除缓存并强制刷新
|
||||
this.cache.delete('knowledge');
|
||||
await this.loadKnowledge();
|
||||
|
||||
// 重置批量删除按钮状态
|
||||
this.updateBatchDeleteKnowledgeButton();
|
||||
} else {
|
||||
this.showNotification(data.error || '批量删除失败', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('批量删除知识库条目失败:', error);
|
||||
this.showNotification('批量删除知识库条目失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 工单管理
|
||||
async loadWorkOrders(forceRefresh = false) {
|
||||
try {
|
||||
const statusFilter = document.getElementById('workorder-status-filter')?.value || 'all';
|
||||
const priorityFilter = document.getElementById('workorder-priority-filter')?.value || 'all';
|
||||
|
||||
let url = '/api/workorders';
|
||||
const params = new URLSearchParams();
|
||||
if (statusFilter !== 'all') params.append('status', statusFilter);
|
||||
if (priorityFilter !== 'all') params.append('priority', priorityFilter);
|
||||
|
||||
// 添加强制刷新参数
|
||||
if (forceRefresh) {
|
||||
params.append('_t', Date.now().toString());
|
||||
}
|
||||
|
||||
if (params.toString()) url += '?' + params.toString();
|
||||
|
||||
const response = await fetch(url);
|
||||
const response = await fetch(url, {
|
||||
cache: forceRefresh ? 'no-cache' : 'default',
|
||||
headers: forceRefresh ? {
|
||||
'Cache-Control': 'no-cache',
|
||||
'Pragma': 'no-cache'
|
||||
} : {}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const workorders = await response.json();
|
||||
this.updateWorkOrdersDisplay(workorders);
|
||||
this.updateWorkOrderStatistics(workorders);
|
||||
|
||||
// 更新缓存
|
||||
this.cache.set('workorders', {
|
||||
data: workorders,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载工单失败:', error);
|
||||
this.showNotification('加载工单失败: ' + error.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1779,17 +1972,35 @@ class TSPDashboard {
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加工单列表的批量操作头部
|
||||
const headerHtml = `
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="checkbox" id="select-all-workorders" class="form-check-input me-2" onchange="dashboard.toggleSelectAllWorkorders()">
|
||||
<label for="select-all-workorders" class="form-check-label">全选</label>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-danger" id="batch-delete-workorders" onclick="dashboard.batchDeleteWorkorders()" disabled>
|
||||
<i class="fas fa-trash me-1"></i>批量删除
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const workordersHtml = workorders.map(workorder => `
|
||||
<div class="work-order-item">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-1">${workorder.title}</h6>
|
||||
<p class="text-muted mb-2">${workorder.description ? workorder.description.substring(0, 100) + (workorder.description.length > 100 ? '...' : '') : '无处理过程'}</p>
|
||||
<div class="d-flex gap-3">
|
||||
<span class="badge bg-${this.getPriorityColor(workorder.priority)}">${this.getPriorityText(workorder.priority)}</span>
|
||||
<span class="badge bg-${this.getStatusColor(workorder.status)}">${this.getStatusText(workorder.status)}</span>
|
||||
<small class="text-muted">分类: ${workorder.category}</small>
|
||||
<small class="text-muted">创建时间: ${new Date(workorder.created_at).toLocaleString()}</small>
|
||||
<div class="d-flex align-items-start">
|
||||
<input type="checkbox" class="form-check-input me-2 workorder-checkbox" value="${workorder.id}" onchange="dashboard.updateBatchDeleteButton()">
|
||||
<div class="flex-grow-1">
|
||||
<h6 class="mb-1">${workorder.title}</h6>
|
||||
<p class="text-muted mb-2">${workorder.description ? workorder.description.substring(0, 100) + (workorder.description.length > 100 ? '...' : '') : '无处理过程'}</p>
|
||||
<div class="d-flex gap-3">
|
||||
<span class="badge bg-${this.getPriorityColor(workorder.priority)}">${this.getPriorityText(workorder.priority)}</span>
|
||||
<span class="badge bg-${this.getStatusColor(workorder.status)}">${this.getStatusText(workorder.status)}</span>
|
||||
<small class="text-muted">分类: ${workorder.category}</small>
|
||||
<small class="text-muted">创建时间: ${new Date(workorder.created_at).toLocaleString()}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ms-3">
|
||||
@@ -1809,7 +2020,7 @@ class TSPDashboard {
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
container.innerHTML = workordersHtml;
|
||||
container.innerHTML = headerHtml + workordersHtml;
|
||||
}
|
||||
|
||||
updateWorkOrderStatistics(workorders) {
|
||||
@@ -1825,6 +2036,73 @@ class TSPDashboard {
|
||||
document.getElementById('workorders-resolved').textContent = stats.resolved || 0;
|
||||
}
|
||||
|
||||
// 工单批量删除功能
|
||||
toggleSelectAllWorkorders() {
|
||||
const selectAllCheckbox = document.getElementById('select-all-workorders');
|
||||
const workorderCheckboxes = document.querySelectorAll('.workorder-checkbox');
|
||||
|
||||
workorderCheckboxes.forEach(checkbox => {
|
||||
checkbox.checked = selectAllCheckbox.checked;
|
||||
});
|
||||
|
||||
this.updateBatchDeleteButton();
|
||||
}
|
||||
|
||||
updateBatchDeleteButton() {
|
||||
const selectedCheckboxes = document.querySelectorAll('.workorder-checkbox:checked');
|
||||
const batchDeleteBtn = document.getElementById('batch-delete-workorders');
|
||||
|
||||
if (batchDeleteBtn) {
|
||||
batchDeleteBtn.disabled = selectedCheckboxes.length === 0;
|
||||
batchDeleteBtn.textContent = selectedCheckboxes.length > 0
|
||||
? `批量删除 (${selectedCheckboxes.length})`
|
||||
: '批量删除';
|
||||
}
|
||||
}
|
||||
|
||||
async batchDeleteWorkorders() {
|
||||
const selectedCheckboxes = document.querySelectorAll('.workorder-checkbox:checked');
|
||||
const selectedIds = Array.from(selectedCheckboxes).map(cb => parseInt(cb.value));
|
||||
|
||||
if (selectedIds.length === 0) {
|
||||
this.showNotification('请选择要删除的工单', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`确定要删除选中的 ${selectedIds.length} 个工单吗?此操作不可撤销。`)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/batch-delete/workorders', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ ids: selectedIds })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.showNotification(data.message, 'success');
|
||||
|
||||
// 清除缓存并强制刷新
|
||||
this.cache.delete('workorders');
|
||||
await this.loadWorkOrders(true); // 强制刷新
|
||||
await this.loadAnalytics();
|
||||
|
||||
// 重置批量删除按钮状态
|
||||
this.updateBatchDeleteButton();
|
||||
} else {
|
||||
this.showNotification(data.error || '批量删除失败', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('批量删除工单失败:', error);
|
||||
this.showNotification('批量删除工单失败', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async createWorkOrder() {
|
||||
const title = document.getElementById('wo-title').value.trim();
|
||||
const description = document.getElementById('wo-description').value.trim();
|
||||
@@ -3407,6 +3685,7 @@ class TSPDashboard {
|
||||
|
||||
// 更新统计卡片
|
||||
updateStatisticsCards(data) {
|
||||
// 更新工单统计
|
||||
const total = data.workorders?.total || 0;
|
||||
const open = data.workorders?.open || 0;
|
||||
const resolved = data.workorders?.resolved || 0;
|
||||
@@ -3417,6 +3696,49 @@ class TSPDashboard {
|
||||
document.getElementById('resolvedWorkorders').textContent = resolved;
|
||||
document.getElementById('avgSatisfaction').textContent = avgSatisfaction.toFixed(1);
|
||||
|
||||
// 更新预警统计
|
||||
const alertTotal = data.alerts?.total || 0;
|
||||
const alertActive = data.alerts?.active || 0;
|
||||
const alertCritical = data.alerts?.by_level?.critical || 0;
|
||||
const alertWarning = data.alerts?.by_level?.warning || 0;
|
||||
const alertError = data.alerts?.by_level?.error || 0;
|
||||
|
||||
// 更新预警统计显示
|
||||
if (document.getElementById('critical-alerts')) {
|
||||
document.getElementById('critical-alerts').textContent = alertCritical;
|
||||
}
|
||||
if (document.getElementById('warning-alerts')) {
|
||||
document.getElementById('warning-alerts').textContent = alertWarning;
|
||||
}
|
||||
if (document.getElementById('error-alerts')) {
|
||||
document.getElementById('error-alerts').textContent = alertError;
|
||||
}
|
||||
if (document.getElementById('total-alerts-count')) {
|
||||
document.getElementById('total-alerts-count').textContent = alertTotal;
|
||||
}
|
||||
|
||||
// 更新性能统计
|
||||
const performanceScore = data.performance?.score || 0;
|
||||
const performanceTrend = data.performance?.trend || 'stable';
|
||||
|
||||
if (document.getElementById('performance-score')) {
|
||||
document.getElementById('performance-score').textContent = performanceScore.toFixed(1);
|
||||
}
|
||||
if (document.getElementById('performance-trend')) {
|
||||
document.getElementById('performance-trend').textContent = this.getPerformanceTrendText(performanceTrend);
|
||||
}
|
||||
|
||||
// 更新满意度统计
|
||||
const satisfactionAvg = data.satisfaction?.average || 0;
|
||||
const satisfactionCount = data.satisfaction?.count || 0;
|
||||
|
||||
if (document.getElementById('satisfaction-avg')) {
|
||||
document.getElementById('satisfaction-avg').textContent = satisfactionAvg.toFixed(1);
|
||||
}
|
||||
if (document.getElementById('satisfaction-count')) {
|
||||
document.getElementById('satisfaction-count').textContent = satisfactionCount;
|
||||
}
|
||||
|
||||
// 更新进度条
|
||||
if (total > 0) {
|
||||
document.getElementById('openProgress').style.width = `${(open / total) * 100}%`;
|
||||
@@ -3469,9 +3791,62 @@ class TSPDashboard {
|
||||
|
||||
// 更新分布图表
|
||||
updateDistributionChart(data) {
|
||||
const categories = data.workorders?.by_category || {};
|
||||
const labels = Object.keys(categories);
|
||||
const values = Object.values(categories);
|
||||
const currentDimension = document.getElementById('dataDimension')?.value || 'workorders';
|
||||
|
||||
let labels, values, title, backgroundColor;
|
||||
|
||||
if (currentDimension === 'alerts') {
|
||||
// 预警级别分布
|
||||
const alertLevels = data.alerts?.by_level || {};
|
||||
labels = Object.keys(alertLevels);
|
||||
values = Object.values(alertLevels);
|
||||
title = '预警级别分布';
|
||||
backgroundColor = [
|
||||
'#FF6384', // critical - 红色
|
||||
'#FFCE56', // warning - 黄色
|
||||
'#36A2EB', // error - 蓝色
|
||||
'#4BC0C0', // info - 青色
|
||||
'#9966FF' // 其他
|
||||
];
|
||||
} else if (currentDimension === 'performance') {
|
||||
// 性能指标分布
|
||||
const performanceMetrics = data.performance?.by_level || {};
|
||||
labels = Object.keys(performanceMetrics);
|
||||
values = Object.values(performanceMetrics);
|
||||
title = '性能指标分布';
|
||||
backgroundColor = [
|
||||
'#28a745', // 优秀 - 绿色
|
||||
'#ffc107', // 良好 - 黄色
|
||||
'#fd7e14', // 一般 - 橙色
|
||||
'#dc3545' // 差 - 红色
|
||||
];
|
||||
} else if (currentDimension === 'satisfaction') {
|
||||
// 满意度分布
|
||||
const satisfactionLevels = data.satisfaction?.by_level || {};
|
||||
labels = Object.keys(satisfactionLevels);
|
||||
values = Object.values(satisfactionLevels);
|
||||
title = '满意度分布';
|
||||
backgroundColor = [
|
||||
'#28a745', // 非常满意 - 绿色
|
||||
'#ffc107', // 满意 - 黄色
|
||||
'#fd7e14', // 一般 - 橙色
|
||||
'#dc3545' // 不满意 - 红色
|
||||
];
|
||||
} else {
|
||||
// 工单分类分布
|
||||
const categories = data.workorders?.by_category || {};
|
||||
labels = Object.keys(categories);
|
||||
values = Object.values(categories);
|
||||
title = '工单分类分布';
|
||||
backgroundColor = [
|
||||
'#FF6384',
|
||||
'#36A2EB',
|
||||
'#FFCE56',
|
||||
'#4BC0C0',
|
||||
'#9966FF',
|
||||
'#FF9F40'
|
||||
];
|
||||
}
|
||||
|
||||
const chartConfig = {
|
||||
type: 'doughnut',
|
||||
@@ -3479,14 +3854,7 @@ class TSPDashboard {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
data: values,
|
||||
backgroundColor: [
|
||||
'#FF6384',
|
||||
'#36A2EB',
|
||||
'#FFCE56',
|
||||
'#4BC0C0',
|
||||
'#9966FF',
|
||||
'#FF9F40'
|
||||
]
|
||||
backgroundColor: backgroundColor
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
@@ -3495,7 +3863,7 @@ class TSPDashboard {
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '工单分类分布'
|
||||
text: title
|
||||
},
|
||||
legend: {
|
||||
display: true,
|
||||
@@ -3576,23 +3944,72 @@ class TSPDashboard {
|
||||
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);
|
||||
const currentDimension = document.getElementById('dataDimension')?.value || 'workorders';
|
||||
|
||||
let labels, values, title, backgroundColor, label;
|
||||
|
||||
if (currentDimension === 'alerts') {
|
||||
// 预警严重程度分布
|
||||
const alertSeverities = data.alerts?.by_severity || {};
|
||||
labels = Object.keys(alertSeverities).map(s => this.getSeverityText(s));
|
||||
values = Object.values(alertSeverities);
|
||||
title = '预警严重程度分布';
|
||||
label = '预警数量';
|
||||
backgroundColor = [
|
||||
'#28a745', // low - 绿色
|
||||
'#ffc107', // medium - 黄色
|
||||
'#fd7e14', // high - 橙色
|
||||
'#dc3545' // critical - 红色
|
||||
];
|
||||
} else if (currentDimension === 'performance') {
|
||||
// 性能指标分布
|
||||
const performanceMetrics = data.performance?.by_metric || {};
|
||||
labels = Object.keys(performanceMetrics);
|
||||
values = Object.values(performanceMetrics);
|
||||
title = '性能指标分布';
|
||||
label = '性能值';
|
||||
backgroundColor = [
|
||||
'#28a745', // 优秀 - 绿色
|
||||
'#ffc107', // 良好 - 黄色
|
||||
'#fd7e14', // 一般 - 橙色
|
||||
'#dc3545' // 差 - 红色
|
||||
];
|
||||
} else if (currentDimension === 'satisfaction') {
|
||||
// 满意度分布
|
||||
const satisfactionLevels = data.satisfaction?.by_level || {};
|
||||
labels = Object.keys(satisfactionLevels).map(s => this.getSatisfactionText(s));
|
||||
values = Object.values(satisfactionLevels);
|
||||
title = '满意度分布';
|
||||
label = '满意度数量';
|
||||
backgroundColor = [
|
||||
'#28a745', // 非常满意 - 绿色
|
||||
'#ffc107', // 满意 - 黄色
|
||||
'#fd7e14', // 一般 - 橙色
|
||||
'#dc3545' // 不满意 - 红色
|
||||
];
|
||||
} else {
|
||||
// 工单优先级分布
|
||||
const priorities = data.workorders?.by_priority || {};
|
||||
labels = Object.keys(priorities).map(p => this.getPriorityText(p));
|
||||
values = Object.values(priorities);
|
||||
title = '工单优先级分布';
|
||||
label = '工单数量';
|
||||
backgroundColor = [
|
||||
'#28a745', // 低 - 绿色
|
||||
'#ffc107', // 中 - 黄色
|
||||
'#fd7e14', // 高 - 橙色
|
||||
'#dc3545' // 紧急 - 红色
|
||||
];
|
||||
}
|
||||
|
||||
this.charts.priorityChart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: '工单数量',
|
||||
label: label,
|
||||
data: values,
|
||||
backgroundColor: [
|
||||
'#28a745', // 低 - 绿色
|
||||
'#ffc107', // 中 - 黄色
|
||||
'#fd7e14', // 高 - 橙色
|
||||
'#dc3545' // 紧急 - 红色
|
||||
]
|
||||
backgroundColor: backgroundColor
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
@@ -3601,7 +4018,7 @@ class TSPDashboard {
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: '优先级分布'
|
||||
text: title
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
@@ -3618,33 +4035,124 @@ class TSPDashboard {
|
||||
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);
|
||||
const performance = trendData.map(item => item.performance || 0);
|
||||
const satisfaction = trendData.map(item => item.satisfaction || 0);
|
||||
|
||||
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'
|
||||
]
|
||||
}]
|
||||
};
|
||||
// 根据数据维度选择显示内容
|
||||
const currentDimension = document.getElementById('dataDimension')?.value || 'workorders';
|
||||
|
||||
if (currentDimension === 'alerts') {
|
||||
const alertLevels = data.alerts?.by_level || {};
|
||||
return {
|
||||
labels: Object.keys(alertLevels),
|
||||
datasets: [{
|
||||
data: Object.values(alertLevels),
|
||||
backgroundColor: [
|
||||
'#FF6384', // critical - 红色
|
||||
'#FFCE56', // warning - 黄色
|
||||
'#36A2EB', // error - 蓝色
|
||||
'#4BC0C0', // info - 青色
|
||||
'#9966FF' // 其他
|
||||
]
|
||||
}]
|
||||
};
|
||||
} else if (currentDimension === 'performance') {
|
||||
// 性能指标分布
|
||||
const performanceMetrics = data.performance || {};
|
||||
return {
|
||||
labels: Object.keys(performanceMetrics),
|
||||
datasets: [{
|
||||
data: Object.values(performanceMetrics),
|
||||
backgroundColor: [
|
||||
'#28a745', // 优秀 - 绿色
|
||||
'#ffc107', // 良好 - 黄色
|
||||
'#fd7e14', // 一般 - 橙色
|
||||
'#dc3545' // 差 - 红色
|
||||
]
|
||||
}]
|
||||
};
|
||||
} else if (currentDimension === 'satisfaction') {
|
||||
// 满意度分布
|
||||
const satisfactionLevels = data.satisfaction?.by_level || {};
|
||||
return {
|
||||
labels: Object.keys(satisfactionLevels),
|
||||
datasets: [{
|
||||
data: Object.values(satisfactionLevels),
|
||||
backgroundColor: [
|
||||
'#28a745', // 非常满意 - 绿色
|
||||
'#ffc107', // 满意 - 黄色
|
||||
'#fd7e14', // 一般 - 橙色
|
||||
'#dc3545' // 不满意 - 红色
|
||||
]
|
||||
}]
|
||||
};
|
||||
} else {
|
||||
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: [{
|
||||
// 线图和柱状图根据数据维度显示不同内容
|
||||
const currentDimension = document.getElementById('dataDimension')?.value || 'workorders';
|
||||
const datasets = [];
|
||||
|
||||
if (currentDimension === 'performance') {
|
||||
// 性能指标图表
|
||||
datasets.push({
|
||||
label: '性能指标',
|
||||
data: performance,
|
||||
borderColor: '#28a745',
|
||||
backgroundColor: 'rgba(40, 167, 69, 0.1)',
|
||||
tension: chartType === 'line' ? 0.4 : 0
|
||||
});
|
||||
} else if (currentDimension === 'satisfaction') {
|
||||
// 满意度图表
|
||||
datasets.push({
|
||||
label: '满意度评分',
|
||||
data: satisfaction,
|
||||
borderColor: '#ffc107',
|
||||
backgroundColor: 'rgba(255, 193, 7, 0.1)',
|
||||
tension: chartType === 'line' ? 0.4 : 0
|
||||
});
|
||||
} else {
|
||||
// 默认显示工单和预警数据
|
||||
datasets.push({
|
||||
label: '工单数量',
|
||||
data: workorders,
|
||||
borderColor: '#36A2EB',
|
||||
backgroundColor: 'rgba(54, 162, 235, 0.1)',
|
||||
tension: chartType === 'line' ? 0.4 : 0
|
||||
}]
|
||||
});
|
||||
|
||||
// 如果有预警数据,添加预警数据集
|
||||
if (alerts.some(alert => alert > 0)) {
|
||||
datasets.push({
|
||||
label: '预警数量',
|
||||
data: alerts,
|
||||
borderColor: '#FF6384',
|
||||
backgroundColor: 'rgba(255, 99, 132, 0.1)',
|
||||
tension: chartType === 'line' ? 0.4 : 0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
labels: labels,
|
||||
datasets: datasets
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -4281,6 +4789,35 @@ class TSPDashboard {
|
||||
return priorityMap[priority] || priority;
|
||||
}
|
||||
|
||||
getSeverityText(severity) {
|
||||
const severityMap = {
|
||||
'low': '低',
|
||||
'medium': '中',
|
||||
'high': '高',
|
||||
'critical': '严重'
|
||||
};
|
||||
return severityMap[severity] || severity;
|
||||
}
|
||||
|
||||
getSatisfactionText(level) {
|
||||
const satisfactionMap = {
|
||||
'very_satisfied': '非常满意',
|
||||
'satisfied': '满意',
|
||||
'neutral': '一般',
|
||||
'dissatisfied': '不满意'
|
||||
};
|
||||
return satisfactionMap[level] || level;
|
||||
}
|
||||
|
||||
getPerformanceTrendText(trend) {
|
||||
const trendMap = {
|
||||
'up': '上升',
|
||||
'down': '下降',
|
||||
'stable': '稳定'
|
||||
};
|
||||
return trendMap[trend] || trend;
|
||||
}
|
||||
|
||||
getPriorityColor(priority) {
|
||||
const colorMap = {
|
||||
'low': 'secondary',
|
||||
|
||||
Reference in New Issue
Block a user