NetEaseDSMonitor/templates/dashboard.html
2025-04-22 09:05:30 +08:00

2046 lines
82 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>网易大神审核数据监控看板</title>
<link rel="icon" href="/static/ds-favicon.ico" type="image/x-icon">
<script src="https://cdn.bootcdn.net/ajax/libs/socket.io/4.0.1/socket.io.min.js"></script>
<style>
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background-color: #f5f5f5;
margin: 0;
padding: 0;
color: #333;
}
.header {
background-color: #1890ff;
color: white;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
}
.header-title {
display: flex;
align-items: baseline;
gap: 0.5rem;
}
.header h1 {
margin: 0;
font-size: 1.5rem;
}
.version-display {
font-size: 0.8rem;
color: rgba(255, 255, 255, 0.8);
font-weight: normal;
}
.header-buttons {
display: flex;
gap: 1rem;
}
.header-buttons button {
background-color: #2196F3;
border: none;
color: white;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
font-weight: bold;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
.header-buttons button:hover {
background-color: #0d8bf2;
}
.container {
display: flex;
padding: 1rem;
flex-wrap: wrap;
gap: 1rem;
}
.panel {
flex: 1;
min-width: 500px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.panel-header {
background-color: #fafafa;
padding: 1rem;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.panel-header h2 {
margin: 0;
font-size: 1.2rem;
color: #333;
}
.panel-header .last-update {
font-size: 0.8rem;
color: #999;
}
.panel-body {
padding: 1rem;
}
.data-section {
margin-bottom: 1.5rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid #eee;
}
.data-section:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.data-section h3 {
margin-top: 0;
margin-bottom: 1rem;
color: #666;
font-size: 1rem;
}
.data-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
}
.data-card {
background-color: #f9f9f9;
border-radius: 4px;
padding: 1rem;
display: flex;
flex-direction: column;
align-items: center;
}
.data-card .value {
font-size: 1.8rem;
font-weight: bold;
color: #1890ff;
margin-bottom: 0.5rem;
}
.data-card .label {
font-size: 0.9rem;
color: #666;
text-align: center;
}
.data-total {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #f0f8ff;
border: 1px solid #d6e9ff;
border-radius: 4px;
padding: 1rem;
margin-top: 1rem;
}
.data-total .label {
font-weight: bold;
color: #0d6efd;
}
.data-total .value {
font-size: 1.5rem;
font-weight: bold;
color: #0d6efd;
}
.data-coefficient {
font-size: 0.8rem;
color: #999;
margin-top: 0.25rem;
}
.categories {
margin-top: 1rem;
}
.category-header {
display: flex;
justify-content: space-between;
padding: 0.5rem 0;
border-bottom: 2px solid #ddd;
font-weight: bold;
color: #666;
margin-bottom: 0.5rem;
}
.category-header .name {
flex: 2;
}
.category-header .count,
.category-header .weighted {
flex: 1;
text-align: right;
}
.category-item {
display: flex;
justify-content: space-between;
padding: 0.5rem 0;
border-bottom: 1px dashed #eee;
}
.category-item:last-child {
border-bottom: none;
}
.category-item .name {
flex: 2;
}
.category-item .count,
.category-item .weighted,
.category-item .coefficient {
flex: 1;
text-align: right;
}
.total-stats {
background-color: #fff7e6;
border: 1px solid #ffe7ba;
border-radius: 8px;
padding: 1rem;
margin: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.total-stats h2 {
margin: 0;
color: #d46b08;
}
.total-stats .total-value {
font-size: 2rem;
font-weight: bold;
color: #d46b08;
}
.alarm-banner {
background-color: #fff2f0;
border: 1px solid #ffccc7;
color: #cf1322;
padding: 1rem;
margin: 1rem;
border-radius: 8px;
display: none;
align-items: center;
justify-content: space-between;
}
.alarm-banner.active {
display: flex;
animation: pulse 2s infinite;
}
.alarm-message {
font-weight: bold;
}
.alarm-button {
background-color: #ff4d4f;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
}
@keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.8;
}
100% {
opacity: 1;
}
}
.loading {
text-align: center;
padding: 2rem;
color: #999;
}
@media (max-width: 1100px) {
.panel {
min-width: 100%;
}
}
.breeze-settings-button {
background-color: #4a6da7;
}
.form-content {
max-height: 400px;
overflow-y: auto;
margin-bottom: 15px;
}
.search-group {
position: sticky;
top: 0;
background-color: #fff;
padding: 5px 0;
z-index: 10;
margin-bottom: 10px;
}
#breeze-search {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
margin-bottom: 10px;
}
/* 对话框样式 */
.dialog {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
/* 版本检测对话框样式 */
.update-available {
color: #cf1322;
font-weight: bold;
}
.check-time {
font-size: 0.8rem;
color: #999;
margin-top: 1rem;
}
.version-info {
margin: 1rem 0;
line-height: 1.5;
}
.dialog-button {
padding: 8px 16px;
background-color: #2196F3;
border: none;
color: white;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
margin-top: 1rem;
margin-right: 10px;
}
.dialog-button.update {
background-color: #52c41a;
}
.dialog-button:hover {
background-color: #0d8bf2;
}
.dialog-button.update:hover {
background-color: #3daa08;
}
/* 对话框内容样式 */
.dialog-content {
width: 500px;
background-color: white;
margin: 100px auto;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
.dialog h2 {
margin-top: 0;
color: #1890ff;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close:hover {
color: black;
}
.settings-form {
margin-top: 20px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #333;
}
.form-group input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.settings-form button {
padding: 8px 16px;
background-color: #1890ff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 10px;
}
/* 工具按钮样式 */
.action-buttons {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
/* 移除这个通用样式 */
/*.action-button {
padding: 8px 16px;
background-color: #f0f0f0;
border: 1px solid #ddd;
color: #333;
border-radius: 4px;
cursor: pointer;
}*/
/* 所有按钮使用统一的蓝色背景 */
.refresh-button,
.check-now-button,
.restart-button,
.test-alarm-button,
.cms-settings-button,
.breeze-settings-button,
#logout-button {
padding: 8px 16px;
background-color: #2196F3;
border: none;
color: white;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
/* 悬停效果 */
.refresh-button:hover,
.check-now-button:hover,
.restart-button:hover,
.test-alarm-button:hover,
.cms-settings-button:hover,
.breeze-settings-button:hover,
#logout-button:hover {
background-color: #0d8bf2;
}
.welcome-message {
display: none;
}
.welcome-text {
display: none;
}
/* 添加消息提示样式 */
.message-container {
position: fixed;
top: 16px;
left: 50%;
transform: translateX(-50%);
z-index: 1010;
pointer-events: none;
}
.message {
padding: 8px 16px;
margin-bottom: 8px;
border-radius: 4px;
box-shadow: 0 3px 6px -4px rgba(0,0,0,.12), 0 6px 16px 0 rgba(0,0,0,.08);
background: #fff;
display: inline-flex;
align-items: center;
pointer-events: all;
font-size: 14px;
animation: messageSlideDown 0.3s ease-out, messageFadeOut 0.2s ease-out 2.8s forwards;
}
.message.success {
background: #f6ffed;
border: 1px solid #b7eb8f;
color: #52c41a;
}
.message.error {
background: #fff2f0;
border: 1px solid #ffccc7;
color: #ff4d4f;
}
.message.info {
background: #e6f7ff;
border: 1px solid #91d5ff;
color: #1890ff;
}
@keyframes messageSlideDown {
0% {
opacity: 0;
transform: translateY(-100%);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes messageFadeOut {
0% {
opacity: 1;
transform: translateY(0);
max-height: 150px;
}
100% {
opacity: 0;
transform: translateY(-100%);
max-height: 0;
margin: 0;
padding: 0;
}
}
/* 当前用户样式 */
.current-user {
padding: 8px 16px;
background-color: #2196F3;
border: none;
color: white;
border-radius: 4px;
font-weight: bold;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
margin-right: 15px;
cursor: default; /* 不显示手型光标 */
}
.current-user:hover {
background-color: #2196F3; /* 保持原色,不改变 */
}
.categories-container {
margin-top: 20px;
margin-bottom: 20px;
}
.category-item {
padding: 10px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.category-name {
font-weight: 500;
color: #333;
}
.category-stats {
display: flex;
gap: 20px;
}
.category-stats span {
color: #666;
}
.category-stats .count {
color: #1890ff;
font-weight: 500;
}
.category-stats .weighted {
color: #52c41a;
}
.weighted-total {
margin-top: 20px;
text-align: right;
}
.weighted-total h3 {
color: #52c41a;
margin: 0;
}
.user-info {
display: flex;
justify-content: space-between;
align-items: center;
}
.user-name {
font-size: 1rem;
font-weight: bold;
}
.logout-btn {
background-color: #ff4d4f;
border: none;
color: white;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
}
.stats-container {
display: flex;
flex-wrap: wrap;
gap: 1rem;
padding: 1rem;
background: white;
}
.stats-card {
flex: 1;
min-width: 250px;
background: white;
border: 1px solid #eee;
border-radius: 8px;
padding: 1rem;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.stats-card h3 {
margin: 0 0 1rem 0;
color: #333;
font-size: 1rem;
}
.stats-row {
display: flex;
justify-content: space-between;
gap: 1rem;
}
.stats-item {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.stats-label {
font-size: 0.9rem;
color: #666;
margin-bottom: 0.5rem;
}
.stats-value {
font-size: 1.8rem;
font-weight: bold;
color: #1890ff;
display: flex;
align-items: baseline;
gap: 8px;
}
.weighted-number {
font-size: 1.2rem;
color: #999;
font-weight: normal;
}
.stats-time {
font-size: 0.8rem;
color: #999;
margin-top: 0.3rem;
}
.stats-card.total {
background: #fff7e6;
border: 1px solid #ffe7ba;
}
.stats-card.total h3 {
color: #d46b08;
}
</style>
</head>
<body>
<div id="messageContainer" class="message-container"></div>
<div class="header">
<div class="header-title">
<h1>网易大神审核数据监控看板</h1>
<span id="version-display" class="version-display">加载中...</span>
</div>
<div class="header-buttons">
<button class="current-user">当前登录用户:{{ username }}{{ staff_name }}</button>
<button class="refresh-button">刷新数据</button>
<button class="check-now-button">更新当前小时数据</button>
<button class="test-alarm-button">测试告警</button>
<button class="restart-button">更新全天数据</button>
<button class="check-version-button">检测版本更新</button>
<button class="cms-settings-button">CMS系数设置</button>
<button class="breeze-settings-button">Breeze系数设置</button>
<button id="logout-button">登出</button>
</div>
</div>
<div class="stats-container">
<div class="stats-card">
<h3>清风审核</h3>
<div class="stats-row">
<div class="stats-item">
<span class="stats-label">当前小时</span>
<span class="stats-value" id="breeze-total">0</span>
<span class="stats-time" id="breeze-hourly-time">-</span>
</div>
<div class="stats-item">
<span class="stats-label">今日累计</span>
<span class="stats-value" id="breeze-daily-total">0</span>
<span class="stats-time" id="breeze-daily-time">-</span>
</div>
</div>
</div>
<div class="stats-card">
<h3>大神CMS</h3>
<div class="stats-row">
<div class="stats-item">
<span class="stats-label">当前小时</span>
<span class="stats-value" id="cms-total">0</span>
<span class="stats-time" id="cms-hourly-time">-</span>
</div>
<div class="stats-item">
<span class="stats-label">今日累计</span>
<span class="stats-value" id="cms-daily-total">0</span>
<span class="stats-time" id="cms-daily-time">-</span>
</div>
</div>
</div>
<div class="stats-card">
<h3>CC审核平台-论坛审核</h3>
<div class="stats-row">
<div class="stats-item">
<span class="stats-label">当前小时</span>
<div class="stats-value">
<span id="inspect-hourly-total">0</span>
<span class="weighted-number" id="inspect-hourly-weighted">(0)</span>
</div>
<span class="stats-time" id="inspect-hourly-time">-</span>
</div>
<div class="stats-item">
<span class="stats-label">今日累计</span>
<div class="stats-value">
<span id="inspect-daily-total">0</span>
<span class="weighted-number" id="inspect-daily-weighted">(0)</span>
</div>
<span class="stats-time" id="inspect-daily-time">-</span>
</div>
</div>
</div>
<div class="stats-card total">
<h3>总计(折算量)</h3>
<div class="stats-row">
<div class="stats-item">
<span class="stats-label">当前小时</span>
<span class="stats-value" id="total-weighted-hourly">0</span>
<span class="stats-time" id="total-hourly-time">-</span>
</div>
<div class="stats-item">
<span class="stats-label">今日累计</span>
<span class="stats-value" id="total-weighted-daily">0</span>
<span class="stats-time" id="total-daily-time">-</span>
</div>
</div>
</div>
</div>
<!-- 存储告警阈值的隐藏元素 -->
<div id="alarm-threshold" style="display:none"></div>
<div id="alarm-banner" class="alarm-banner">
<div class="alarm-message">
<span id="alarm-type"></span>:当前小时折算总量已超过阈值<span id="alarm-details"></span>
</div>
<button id="reset-alarm" class="alarm-button">知道了</button>
</div>
<div class="container">
<!-- Breeze工单系统 -->
<div class="panel" id="breeze-panel">
<div class="panel-header">
<h2>清风审核</h2>
<div class="last-update" id="breeze-last-update">最后更新: 暂无</div>
</div>
<div class="panel-body">
<div class="data-section">
<h3>当前小时数据</h3>
<div class="data-card">
<div class="value" id="breeze-hourly-count">-</div>
<div class="label">工单总数</div>
</div>
<div class="data-total">
<div class="label">折算总计</div>
<div class="value" id="breeze-hourly-weighted">-</div>
</div>
<div class="categories" id="breeze-hourly-categories">
<div class="loading">加载中...</div>
</div>
</div>
<div class="data-section">
<h3>今日全天数据</h3>
<div class="data-card">
<div class="value" id="breeze-daily-count">-</div>
<div class="label">工单总数</div>
</div>
<div class="data-total">
<div class="label">折算总计</div>
<div class="value" id="breeze-daily-weighted">-</div>
</div>
<div class="categories" id="breeze-daily-categories">
<div class="loading">加载中...</div>
</div>
</div>
</div>
</div>
<!-- CMS工单系统 -->
<div class="panel" id="cms-panel">
<div class="panel-header">
<h2>大神CMS</h2>
<div class="last-update" id="cms-last-update">最后更新: 暂无</div>
</div>
<div class="panel-body">
<div class="data-section">
<h3>当前小时数据</h3>
<div class="data-grid">
<div class="data-card">
<div class="value" id="cms-hourly-comment">-</div>
<div class="label">评论</div>
<div class="data-coefficient" id="cms-hourly-coefficient-comment">系数: -</div>
</div>
<div class="data-card">
<div class="value" id="cms-hourly-feed">-</div>
<div class="label">动态</div>
<div class="data-coefficient" id="cms-hourly-coefficient-feed">系数: -</div>
</div>
<div class="data-card">
<div class="value" id="cms-hourly-complaint">-</div>
<div class="label">举报处理</div>
<div class="data-coefficient" id="cms-hourly-coefficient-complaint">系数: -</div>
</div>
<div class="data-card">
<div class="value" id="cms-hourly-count">-</div>
<div class="label">总记录数</div>
</div>
</div>
<div class="data-total">
<div class="label">折算总计</div>
<div class="value" id="cms-hourly-weighted">-</div>
</div>
</div>
<div class="data-section">
<h3>今日全天数据</h3>
<div class="data-grid">
<div class="data-card">
<div class="value" id="cms-daily-comment">-</div>
<div class="label">评论</div>
<div class="data-coefficient" id="cms-daily-coefficient-comment">系数: -</div>
</div>
<div class="data-card">
<div class="value" id="cms-daily-feed">-</div>
<div class="label">动态</div>
<div class="data-coefficient" id="cms-daily-coefficient-feed">系数: -</div>
</div>
<div class="data-card">
<div class="value" id="cms-daily-complaint">-</div>
<div class="label">举报处理</div>
<div class="data-coefficient" id="cms-daily-coefficient-complaint">系数: -</div>
</div>
<div class="data-card">
<div class="value" id="cms-daily-count">-</div>
<div class="label">总记录数</div>
</div>
</div>
<div class="data-total">
<div class="label">折算总计</div>
<div class="value" id="cms-daily-weighted">-</div>
</div>
</div>
</div>
</div>
<!-- CMS系数设置对话框 -->
<div id="settings-dialog" class="dialog">
<div class="dialog-content">
<span class="close" onclick="document.getElementById('unrecognizedIssuesDialog').style.display='none'">&times;</span>
<h2>无法识别的工单</h2>
<div class="unrecognized-issues-list" style="max-height: 400px; overflow-y: auto;">
<div class="loading">加载中...</div>
</div>
<button class="dialog-button" onclick="document.getElementById('unrecognizedIssuesDialog').style.display='none'">关闭</button>
</div>
</div>
<script>
// 建立 WebSocket 连接
const socket = io();
// 监听模板文件变更事件
socket.on('template_changed', function(data) {
console.log('模板文件已更新:', data.file);
// 刷新页面
window.location.reload();
});
// 定义全局变量
let settingsDialog;
let breezeSettingsDialog;
let alarmBanner;
let alarmsInterval = null;
document.addEventListener('DOMContentLoaded', function () {
// 初始化变量
settingsDialog = document.getElementById('settings-dialog');
breezeSettingsDialog = document.getElementById('breeze-settings-dialog');
alarmBanner = document.getElementById('alarm-banner');
// 绑定所有按钮事件
bindEvents();
// 获取版本信息并检查更新
fetch('/api/get-version')
.then(response => response.json())
.then(data => {
if (data.success) {
// 显示时间戳版本和固定版本号
document.getElementById('version-display').textContent = data.data.current_version || 'v1.0.0';
// 如果有更新,自动显示版本检测对话框
if (data.data.has_update) {
document.getElementById('currentVersion').textContent = data.data.current_version || '-';
document.getElementById('onlineVersion').textContent = data.data.online_version || '-';
document.getElementById('lastCheckTime').textContent = data.data.last_check_time || '-';
document.getElementById('versionStatus').textContent = '有新版本可用!请更新系统以获得最新功能和修复。';
document.getElementById('updateButton').style.display = 'inline-block';
document.getElementById('versionDialog').style.display = 'block';
document.getElementById('versionDialogTitle').textContent = '检测到新版本更新';
}
}
})
.catch(console.error);
// 立即获取统计数据
fetchStats();
// 每 5 秒自动刷新一次数据
setInterval(fetchStats, 5000);
// 每 1 分钟检查一次告警状态
setInterval(checkAlarmStatus, 60000);
// 每 2 分钟刷新一次系数
setInterval(loadCoefficients, 120000);
// 初始检查告警状态
checkAlarmStatus();
// 加载系数
loadCoefficients();
// 初始化检查每日数据按钮
document.getElementById('check-daily-btn').addEventListener('click', function () {
checkCmsDaily();
});
// 修改按钮文本
document.querySelector('.check-now-button').textContent = '更新当前小时数据';
document.querySelector('.restart-button').textContent = '更新全天数据';
// 修改按钮事件绑定
document.querySelector('.check-now-button').addEventListener('click', function() {
if (confirm('确认要更新当前小时数据吗?')) {
checkNow();
}
});
document.querySelector('.restart-button').addEventListener('click', function() {
if (confirm('确认要更新全天数据吗?')) {
checkDaily();
}
});
});
// 获取统计数据
function fetchStats() {
fetch('/api/get-stats')
.then(response => response.json())
.then(data => {
if (data.success) {
updateDashboard(data.data);
// 如果数据中包含系数,更新系数显示
if (data.data.cms && data.data.cms.coefficients) {
updateCoefficientsDisplay(data.data.cms.coefficients);
}
} else {
console.error('获取数据失败:', data.message);
}
})
.catch(error => {
console.error('获取数据出错:', error);
});
}
// 检查告警状态
function checkAlarmStatus() {
fetch('/api/get-alarm-status')
.then(response => response.json())
.then(data => {
if (data.success && data.data.is_alarming) {
alarmBanner.classList.add('active');
// 从API返回数据中获取告警类型
const alarmType = data.data.alarm_type || "实时告警";
document.getElementById('alarm-type').textContent = alarmType;
// 更新告警详细信息
const total = parseFloat(document.getElementById('total-weighted-hourly').textContent);
const threshold = data.data.threshold;
if (!isNaN(total) && total > 0) {
const overPercentage = ((total - threshold) / threshold * 100).toFixed(1);
document.getElementById('alarm-details').textContent = ` (当前值:${total},超出${overPercentage}%)`;
}
} else {
alarmBanner.classList.remove('active');
}
})
.catch(error => {
console.error('检查告警状态出错:', error);
});
}
// 更新仪表盘
function updateDashboard(data) {
try {
// 获取平台可用性信息
const breezeAvailable = data.breeze_available !== undefined ? data.breeze_available : true;
const cmsAvailable = data.cms_available !== undefined ? data.cms_available : true;
const inspectAvailable = data.inspect_available !== undefined ? data.inspect_available : true;
// 控制面板显示
document.getElementById('breeze-panel').style.display = breezeAvailable ? 'block' : 'none';
document.getElementById('cms-panel').style.display = cmsAvailable ? 'block' : 'none';
document.getElementById('inspect-panel').style.display = inspectAvailable ? 'block' : 'none';
// 根据可用平台调整总量级显示标题
let totalPlatforms = [];
if (breezeAvailable) totalPlatforms.push("清风审核");
if (cmsAvailable) totalPlatforms.push("大神CMS");
if (inspectAvailable) totalPlatforms.push("CC审核平台");
const platformsText = totalPlatforms.join(" + ");
const totalCard = document.querySelector('.stats-card.total h3');
totalCard.textContent = `总计(折算量)- ${platformsText || "无可用平台"}`;
// 更新Breeze工单系统面板数据
// 即使breeze数据不存在也使用默认值而不是直接返回
const breezeHourly = data.breeze && data.breeze.hourly ? data.breeze.hourly : { total: 0, weighted_total: 0, categories: {} };
const breezeDaily = data.breeze && data.breeze.daily ? data.breeze.daily : { total: 0, weighted_total: 0, categories: {} };
// 更新顶部统计数据
document.getElementById('breeze-total').textContent = breezeHourly.total || '0';
document.getElementById('breeze-daily-total').textContent = breezeDaily.total || '0';
// 更新Breeze工单系统面板
document.getElementById('breeze-hourly-count').textContent = breezeHourly.total || '0';
document.getElementById('breeze-hourly-weighted').textContent = breezeHourly.weighted_total ? breezeHourly.weighted_total.toFixed(2) : '0.00';
document.getElementById('breeze-daily-count').textContent = breezeDaily.total || '0';
document.getElementById('breeze-daily-weighted').textContent = breezeDaily.weighted_total ? breezeDaily.weighted_total.toFixed(2) : '0.00';
// 更新小时类别数据
const breezeHourlyCategories = document.getElementById('breeze-hourly-categories');
if (breezeHourly.categories && Object.keys(breezeHourly.categories).length > 0) {
let categoriesHTML = `
<div class="category-header">
<div class="name">类别</div>
<div class="count">数量</div>
<div class="weighted">折算值</div>
</div>`;
for (const [name, info] of Object.entries(breezeHourly.categories)) {
if (info.count > 0) {
categoriesHTML += `
<div class="category-item">
<div class="name">
${name}
<div style="font-size: 12px; color: #999;">系数: ${info.coefficient.toFixed(2)}</div>
</div>
<div class="count">${info.count}</div>
<div class="weighted">${info.weighted.toFixed(2)}</div>
</div>`;
}
}
breezeHourlyCategories.innerHTML = categoriesHTML || '<div class="loading">暂无数据</div>';
} else {
breezeHourlyCategories.innerHTML = '<div class="loading">暂无可用数据</div>';
}
// 更新日类别数据
const breezeDailyCategories = document.getElementById('breeze-daily-categories');
if (breezeDaily.categories && Object.keys(breezeDaily.categories).length > 0) {
let categoriesHTML = `
<div class="category-header">
<div class="name">类别</div>
<div class="count">数量</div>
<div class="weighted">折算值</div>
</div>`;
for (const [name, info] of Object.entries(breezeDaily.categories)) {
if (info.count > 0) {
categoriesHTML += `
<div class="category-item">
<div class="name">
${name}
<div style="font-size: 12px; color: #999;">系数: ${info.coefficient.toFixed(2)}</div>
</div>
<div class="count">${info.count}</div>
<div class="weighted">${info.weighted.toFixed(2)}</div>
</div>`;
}
}
breezeDailyCategories.innerHTML = categoriesHTML || '<div class="loading">暂无数据</div>';
} else {
breezeDailyCategories.innerHTML = '<div class="loading">暂无可用数据</div>';
}
// 更新最后更新时间
if (data.breeze) {
document.getElementById('breeze-last-update').textContent = '最后更新: ' + (data.breeze.hourly_update || '未知');
// 更新时间戳
if (data.breeze.hourly_update) {
document.getElementById('breeze-hourly-time').textContent = data.breeze.hourly_update;
}
if (data.breeze.daily_update) {
document.getElementById('breeze-daily-time').textContent = data.breeze.daily_update;
}
}
// 更新CMS数据
// 即使cms数据不存在也使用默认值而不是直接返回
const cmsHourly = data.cms && data.cms.hourly ? data.cms.hourly : {
stats: { comment: 0, feed: 0, complaint: 0 },
weighted_total: 0,
total_count: 0
};
const cmsDaily = data.cms && data.cms.daily ? data.cms.daily : {
stats: { comment: 0, feed: 0, complaint: 0 },
weighted_total: 0,
total_count: 0
};
// 更新顶部统计栏
document.getElementById('cms-total').textContent = cmsHourly.total_count || '0';
document.getElementById('cms-daily-total').textContent = cmsDaily.total_count || '0';
// 更新CMS审核系统面板
document.getElementById('cms-hourly-comment').textContent = cmsHourly.stats ? cmsHourly.stats.comment : '0';
document.getElementById('cms-hourly-feed').textContent = cmsHourly.stats ? cmsHourly.stats.feed : '0';
document.getElementById('cms-hourly-complaint').textContent = cmsHourly.stats ? cmsHourly.stats.complaint : '0';
document.getElementById('cms-hourly-count').textContent = cmsHourly.total_count || '0';
document.getElementById('cms-hourly-weighted').textContent = cmsHourly.weighted_total ? cmsHourly.weighted_total.toFixed(2) : '0.00';
// 更新时间戳
if (data.cms) {
document.getElementById('cms-last-update').textContent = '最后更新: ' + (data.cms.hourly_update || '未知');
if (data.cms.hourly_update) {
document.getElementById('cms-hourly-time').textContent = data.cms.hourly_update;
}
if (data.cms.daily_update) {
document.getElementById('cms-daily-time').textContent = data.cms.daily_update;
}
}
// 更新CMS每日数据
document.getElementById('cms-daily-comment').textContent = cmsDaily.stats ? cmsDaily.stats.comment : '0';
document.getElementById('cms-daily-feed').textContent = cmsDaily.stats ? cmsDaily.stats.feed : '0';
document.getElementById('cms-daily-complaint').textContent = cmsDaily.stats ? cmsDaily.stats.complaint : '0';
document.getElementById('cms-daily-count').textContent = cmsDaily.total_count || '0';
document.getElementById('cms-daily-weighted').textContent = cmsDaily.weighted_total ? cmsDaily.weighted_total.toFixed(2) : '0.00';
// 更新CC审核平台数据
const inspectHourly = data.inspect && data.inspect.hourly ? data.inspect.hourly : { total: 0, weighted_total: 0 };
const inspectDaily = data.inspect && data.inspect.daily ? data.inspect.daily : { total: 0, weighted_total: 0 };
document.getElementById('inspect-hourly-total').textContent = inspectHourly.total || '0';
document.getElementById('inspect-hourly-weighted').textContent = `(${Math.round(inspectHourly.weighted_total || 0)})`;
document.getElementById('inspect-daily-total').textContent = inspectDaily.total || '0';
document.getElementById('inspect-daily-weighted').textContent = `(${Math.round(inspectDaily.weighted_total || 0)})`;
// 更新时间戳
if (data.inspect) {
if (data.inspect.hourly_update) {
document.getElementById('inspect-hourly-time').textContent = data.inspect.hourly_update;
}
if (data.inspect.daily_update) {
document.getElementById('inspect-daily-time').textContent = data.inspect.daily_update;
}
}
// 更新总计数据 - 即使某个业务无数据也能正确显示总量级
if (data.total) {
document.getElementById('total-weighted-hourly').textContent = Math.round(data.total.hourly || 0);
document.getElementById('total-weighted-daily').textContent = Math.round(data.total.daily || 0);
// 获取最新的时间戳
const hourlyUpdateTime = getLatestTimestamp([
data.breeze?.hourly_update,
data.cms?.hourly_update,
data.inspect?.hourly_update
]);
const dailyUpdateTime = getLatestTimestamp([
data.breeze?.daily_update,
data.cms?.daily_update,
data.inspect?.daily_update
]);
// 更新时间戳显示
if (hourlyUpdateTime) {
document.getElementById('total-hourly-time').textContent = hourlyUpdateTime;
}
if (dailyUpdateTime) {
document.getElementById('total-daily-time').textContent = dailyUpdateTime;
}
}
} catch (error) {
console.error('Error updating stats:', error);
}
}
// 加载系数
function loadCoefficients() {
// 加载CMS系数
fetch('/api/get-coefficients')
.then(response => response.json())
.then(data => {
if (data.success) {
updateCoefficientsDisplay(data.data);
}
})
.catch(error => {
console.error('获取CMS系数出错:', error);
});
}
// 更新系数显示
function updateCoefficientsDisplay(coefficients) {
// 更新CMS系数显示
document.getElementById('cms-hourly-coefficient-comment').textContent =
'系数: ' + (coefficients.comment || '-').toFixed(2);
document.getElementById('cms-hourly-coefficient-feed').textContent =
'系数: ' + (coefficients.feed || '-').toFixed(2);
document.getElementById('cms-hourly-coefficient-complaint').textContent =
'系数: ' + (coefficients.complaint || '-').toFixed(2);
document.getElementById('cms-daily-coefficient-comment').textContent =
'系数: ' + (coefficients.comment || '-').toFixed(2);
document.getElementById('cms-daily-coefficient-feed').textContent =
'系数: ' + (coefficients.feed || '-').toFixed(2);
document.getElementById('cms-daily-coefficient-complaint').textContent =
'系数: ' + (coefficients.complaint || '-').toFixed(2);
}
// 显示CMS系数设置对话框
function showCMSSettingsDialog() {
fetch('/api/get-coefficients')
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('cms-coefficient-comment').value = data.data.comment;
document.getElementById('cms-coefficient-feed').value = data.data.feed;
document.getElementById('cms-coefficient-complaint').value = data.data.complaint;
settingsDialog.style.display = 'block';
} else {
alert('获取系数失败: ' + data.message);
}
})
.catch(error => {
console.error('获取系数出错:', error);
alert('获取系数出错');
});
}
// 显示Breeze系数设置对话框
function showBreezeSettingsDialog() {
fetch('/api/get-breeze-coefficients')
.then(response => response.json())
.then(data => {
if (data.success) {
const breezeSettingsForm = document.getElementById('breeze-settings-form');
// 清空现有内容
breezeSettingsForm.innerHTML = '';
// 为每个系数创建输入字段
const coefficients = data.data;
const chineseNames = {
'NTES_GOD_IMAGES': '网易大神APP图片',
'NTES_GOD_VIDEOS': '网易大神APP视频',
'NTES_GOD_CHAT_IMAGES': '网易大神APP聊天图片',
'NTES_GOD_CHAT_VIDEOS': '网易大神APP聊天视频',
'NTES_DASONG': '大神大宋视频',
'SPIDER_VIDEO': '大神普通供给视频',
'SPIDER_VIDEO_SP': '大神高优供给视频',
'NTES_GOD_AI': '大神AI图片',
'NTES_GOD_TOP': '大神短视频',
'T_SPIDER_VIDEO': '大神tiktok普通视频',
'T_SPIDER_VIDEO_SP': '大神tiktok高优视频',
'V_SPIDER_VIDEO': '大神ins普通供给视频',
'V_SPIDER_VIDEO_SP': '大神ins高优供给视频',
'NTES_GOD_XHS': '大神小红书图片',
'XHS_SPIDER_VIDEO': '小红书供给视频',
'Cupid': '大神交友',
'CHAT_P2P': '大神聊天/风险用户_私聊/私聊频繁',
'CHAT_TEAM': '大神聊天/风险用户_群聊/群聊频繁',
'CHAT_ROOM': '大神聊天_聊天室',
'CHAT_ROOM_MSG': '风险用户_聊天室频繁'
};
// 创建搜索框
const searchDiv = document.createElement('div');
searchDiv.className = 'form-group search-group';
searchDiv.innerHTML = `
<label for="breeze-search">搜索系数:</label>
<input type="text" id="breeze-search" placeholder="输入关键词搜索系数" />
`;
breezeSettingsForm.appendChild(searchDiv);
// 创建表单内容容器
const formContentDiv = document.createElement('div');
formContentDiv.className = 'form-content';
breezeSettingsForm.appendChild(formContentDiv);
// 为每个系数创建输入字段
for (const [key, value] of Object.entries(coefficients)) {
const formGroup = document.createElement('div');
formGroup.className = 'form-group';
formGroup.dataset.name = key;
const label = document.createElement('label');
label.htmlFor = `breeze-coefficient-${key}`;
label.textContent = chineseNames[key] || key;
const input = document.createElement('input');
input.type = 'number';
input.id = `breeze-coefficient-${key}`;
input.className = 'breeze-coefficient';
input.dataset.key = key;
input.step = '0.01';
input.min = '0';
input.max = '200';
input.value = value;
formGroup.appendChild(label);
formGroup.appendChild(input);
formContentDiv.appendChild(formGroup);
}
// 添加搜索功能
const searchInput = document.getElementById('breeze-search');
searchInput.addEventListener('input', function () {
const searchText = this.value.toLowerCase();
const formGroups = formContentDiv.querySelectorAll('.form-group');
formGroups.forEach(group => {
const key = group.dataset.name;
const label = group.querySelector('label').textContent.toLowerCase();
if (key && key.toLowerCase().includes(searchText) || label.includes(searchText)) {
group.style.display = '';
} else {
group.style.display = 'none';
}
});
});
// 添加保存按钮
const saveButton = document.createElement('button');
saveButton.id = 'save-breeze-settings-button';
saveButton.textContent = '保存';
saveButton.addEventListener('click', saveBreezeCoefficients);
breezeSettingsForm.appendChild(saveButton);
// 显示对话框
breezeSettingsDialog.style.display = 'block';
} else {
alert('获取Breeze系数失败: ' + data.message);
}
})
.catch(error => {
console.error('获取Breeze系数出错:', error);
alert('获取Breeze系数出错');
});
}
// 保存Breeze系数设置
function saveBreezeCoefficients() {
const coefficientInputs = document.querySelectorAll('.breeze-coefficient');
const newCoefficients = {};
coefficientInputs.forEach(input => {
if (input.value) {
newCoefficients[input.dataset.key] = parseFloat(input.value);
}
});
fetch('/api/update-breeze-coefficients', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newCoefficients)
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Breeze系数更新成功');
breezeSettingsDialog.style.display = 'none';
// 刷新数据
setTimeout(fetchStats, 2000);
} else {
alert('更新Breeze系数失败: ' + data.message);
}
})
.catch(error => {
console.error('更新Breeze系数出错:', error);
alert('更新Breeze系数出错');
});
}
// 绑定按钮事件
function bindEvents() {
// 绑定刷新数据按钮事件
document.querySelector('.refresh-button').addEventListener('click', fetchStats);
// 绑定立即检查按钮事件
document.querySelector('.check-now-button').addEventListener('click', checkNow);
// 绑定测试告警按钮事件
document.querySelector('.test-alarm-button').addEventListener('click', testAlarm);
// 绑定重启监控按钮事件
document.querySelector('.restart-button').addEventListener('click', restartMonitor);
// 绑定版本检测更新按钮事件
document.querySelector('.check-version-button').addEventListener('click', checkVersion);
// 绑定CMS系数设置按钮事件
document.querySelector('.cms-settings-button').addEventListener('click', showCMSSettingsDialog);
// 绑定Breeze系数设置按钮事件
document.querySelector('.breeze-settings-button').addEventListener('click', showBreezeSettingsDialog);
// 绑定登出按钮事件
document.getElementById('logout-button').addEventListener('click', function () {
if (confirm('确认要登出吗?')) {
window.location.href = '/logout';
}
});
// 绑定版本对话框的关闭按钮事件
document.getElementById('close-version-dialog').addEventListener('click', function() {
document.getElementById('versionDialog').style.display = 'none';
});
// 绑定版本更新按钮事件
document.getElementById('updateButton').addEventListener('click', handleVersionUpdate);
// 绑定告警确认按钮事件
document.getElementById('reset-alarm').addEventListener('click', function () {
acknowledgeAlarm();
});
}
// 立即检查当前小时数据
function checkNow() {
// 禁用按钮,显示加载状态
const checkNowButton = document.querySelector('.check-now-button');
if (checkNowButton) {
checkNowButton.disabled = true;
checkNowButton.textContent = '正在更新...';
checkNowButton.style.opacity = '0.5';
checkNowButton.style.cursor = 'not-allowed';
}
fetch('/api/check-now')
.then(response => response.json())
.then(data => {
if (data.success) {
// 创建一个通知元素
const notification = document.createElement('div');
notification.className = 'check-notification';
notification.style.position = 'fixed';
notification.style.top = '50%';
notification.style.left = '50%';
notification.style.transform = 'translate(-50%, -50%)';
notification.style.padding = '20px';
notification.style.backgroundColor = '#2196F3';
notification.style.color = 'white';
notification.style.borderRadius = '8px';
notification.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)';
notification.style.zIndex = '9999';
notification.style.textAlign = 'center';
notification.style.maxWidth = '80%';
notification.innerHTML = `
<h3 style="margin-top:0;">数据更新进行中</h3>
<p>系统正在获取最新数据,请耐心等待...</p>
<div class="progress-bar" style="height:5px;background:#1565C0;width:0%;margin:10px 0;border-radius:3px;"></div>
<p style="font-size:0.9em;">更新过程需要10-30秒请勿频繁点击检查按钮</p>
`;
document.body.appendChild(notification);
// 开始进度条动画
let progress = 0;
const progressBar = notification.querySelector('.progress-bar');
const progressInterval = setInterval(() => {
progress += 2;
if (progress <= 100) {
progressBar.style.width = `${progress}%`;
} else {
clearInterval(progressInterval);
}
}, 300);
// 5秒后刷新数据此时数据可能还未更新完成
setTimeout(fetchStats, 5000);
// 15秒后再次刷新确保获取更新后的数据
setTimeout(fetchStats, 15000);
// 20秒后移除通知并恢复按钮
setTimeout(() => {
document.body.removeChild(notification);
clearInterval(progressInterval);
// 恢复按钮状态
if (checkNowButton) {
checkNowButton.disabled = false;
checkNowButton.textContent = '更新当前小时数据';
checkNowButton.style.opacity = '1';
checkNowButton.style.cursor = 'pointer';
}
}, 20000);
} else {
alert('数据更新失败: ' + data.message);
// 恢复按钮状态
if (checkNowButton) {
checkNowButton.disabled = false;
checkNowButton.textContent = '更新当前小时数据';
checkNowButton.style.opacity = '1';
checkNowButton.style.cursor = 'pointer';
}
}
})
.catch(error => {
console.error('数据更新出错,请查看控制台日志:', error);
alert('数据更新出错,请查看控制台日志');
// 恢复按钮状态
if (checkNowButton) {
checkNowButton.disabled = false;
checkNowButton.textContent = '更新当前小时数据';
checkNowButton.style.opacity = '1';
checkNowButton.style.cursor = 'pointer';
}
});
}
// 重启监控进程
function restartMonitor() {
if (!confirm('确认要立即更新全天数据吗?')) {
return;
}
fetch('/api/restart-monitoring')
.then(response => response.json())
.then(data => {
if (data.success) {
alert('全天数据正在更新,稍后将更新数据');
// 5秒后刷新数据
setTimeout(fetchStats, 5000);
} else {
alert('重启失败: ' + data.message);
}
})
.catch(error => {
console.error('数据更新出错,请查看控制台日志:', error);
alert('数据更新出错,请查看控制台日志');
});
}
// 测试告警功能
function testAlarm() {
if (!confirm('确认要测试告警功能吗?这将触发一次测试告警通知。')) {
return;
}
fetch('/api/test-alarm')
.then(response => response.json())
.then(data => {
if (data.success) {
// 测试告警成功,不需要额外提示
console.log('测试告警已触发');
} else {
alert('测试告警失败: ' + data.message);
}
})
.catch(error => {
console.error('测试告警出错:', error);
alert('测试告警出错');
});
}
// 确认并重置告警
function acknowledgeAlarm() {
fetch('/api/acknowledge-alarm')
.then(response => response.json())
.then(data => {
if (data.success) {
// 强制移除告警横幅显示,不管是什么类型的告警
document.getElementById('alarm-banner').classList.remove('active');
console.log('告警已确认并重置');
} else {
alert('确认告警失败: ' + data.message);
}
})
.catch(error => {
console.error('确认告警出错:', error);
alert('确认告警出错');
// 即使出错也尝试移除告警横幅
document.getElementById('alarm-banner').classList.remove('active');
});
}
// 版本更新处理函数
function handleVersionUpdate() {
// 显示确认对话框
if (confirm('确认要更新系统吗?这将会关闭当前系统并自动下载安装最新版本。')) {
// 1. 先登出系统
fetch('/logout')
.then(response => {
if (response.ok) {
// 2. 调用更新API
return fetch('/api/update-system', {
method: 'POST'
});
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('更新程序已启动,系统即将关闭。');
// 关闭当前窗口
window.close();
} else {
alert('更新失败:' + data.message);
}
})
.catch(error => {
console.error('更新失败:', error);
alert('系统更新失败,请稍后重试。');
});
}
}
// 检测版本更新
function checkVersion() {
// 显示加载中状态
const checkVersionButton = document.querySelector('.check-version-button');
if (checkVersionButton) {
checkVersionButton.disabled = true;
checkVersionButton.textContent = '检测中...';
checkVersionButton.style.opacity = '0.5';
checkVersionButton.style.cursor = 'not-allowed';
}
fetch('/api/check-version')
.then(response => response.json())
.then(data => {
if (data.success) {
// 更新弹窗信息
document.getElementById('currentVersion').textContent = data.data.current_version || '-';
document.getElementById('onlineVersion').textContent = data.data.online_version || '-';
document.getElementById('lastCheckTime').textContent = data.data.last_check_time || '-';
// 更新标题中的版本号
document.getElementById('version-display').textContent = data.data.current_version || 'v1.0.0';
// 显示正确的状态信息和更新按钮
const updateButton = document.getElementById('updateButton');
if (data.data.has_update) {
document.getElementById('versionDialogTitle').textContent = '检测到新版本更新';
document.getElementById('versionStatus').textContent = '有新版本可用!请更新系统以获得最新功能和修复。';
updateButton.style.display = 'inline-block'; // 显示更新按钮
} else {
document.getElementById('versionDialogTitle').textContent = '系统版本检测';
document.getElementById('versionStatus').textContent = '您当前使用的是最新版本。';
updateButton.style.display = 'none'; // 隐藏更新按钮
}
// 显示弹窗
document.getElementById('versionDialog').style.display = 'block';
} else {
// 显示错误信息
document.getElementById('versionDialogTitle').textContent = '系统版本检测';
document.getElementById('versionStatus').textContent = data.message || '无法获取版本信息,请稍后再试。';
// 更新弹窗信息 - 即便出错也尝试显示当前版本
if (data.data && data.data.current_version) {
document.getElementById('currentVersion').textContent = data.data.current_version;
document.getElementById('version-display').textContent = data.data.current_version;
}
// 显示弹窗
document.getElementById('versionDialog').style.display = 'block';
}
})
.catch(error => {
console.error('检测版本出错:', error);
// 显示错误信息
document.getElementById('versionDialogTitle').textContent = '系统版本检测';
document.getElementById('versionStatus').textContent = '检测版本出错,请稍后再试。';
// 显示弹窗
document.getElementById('versionDialog').style.display = 'block';
})
.finally(() => {
// 恢复按钮状态
if (checkVersionButton) {
checkVersionButton.disabled = false;
checkVersionButton.textContent = '检测版本更新';
checkVersionButton.style.opacity = '1';
checkVersionButton.style.cursor = 'pointer';
}
});
}
// 立即检查CMS每日数据
function checkCmsDaily() {
// 显示加载状态
const dailyBtn = document.getElementById('check-daily-btn');
const originalText = dailyBtn.innerHTML;
dailyBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 检查中...';
dailyBtn.disabled = true;
fetch('/api/check-cms-daily', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('检查成功', '已触发CMS每日数据检查数据将在约一分钟内更新。', 'success');
// 30秒后刷新页面以展示更新后的数据
setTimeout(() => {
location.reload();
}, 30000);
} else {
showNotification('检查失败', data.message, 'error');
}
})
.catch(error => {
console.error('检查CMS每日数据时出错:', error);
showNotification('检查失败', '请求发生错误,请查看控制台获取详情', 'error');
})
.finally(() => {
// 恢复按钮状态
setTimeout(() => {
dailyBtn.innerHTML = originalText;
dailyBtn.disabled = false;
}, 3000);
});
}
// 添加WebSocket监听代码
socket.on('version_update', function(data) {
if (data.type === 'show_version_dialog') {
// 更新对话框内容
document.getElementById('currentVersion').textContent = data.current_version;
document.getElementById('onlineVersion').textContent = data.online_version;
document.getElementById('versionDialogTitle').textContent = '检测到新版本更新';
document.getElementById('versionStatus').textContent = '有新版本可用!请更新系统以获得最新功能和修复。';
// 显示更新按钮
document.getElementById('updateButton').style.display = 'inline-block';
// 显示对话框
document.getElementById('versionDialog').style.display = 'block';
}
});
// 显示无法识别的工单
function showUnrecognizedIssues(issues) {
const dialog = document.getElementById('unrecognizedIssuesDialog');
const listContainer = dialog.querySelector('.unrecognized-issues-list');
let html = '<div class="issues-list">';
issues.forEach(issue => {
html += `
<div class="issue-item" style="margin-bottom: 10px; padding: 10px; border: 1px solid #ddd; border-radius: 4px;">
<div><strong>工单ID:</strong> ${issue.id}</div>
<div><strong>标题:</strong> ${issue.title}</div>
<div><strong>产品代码:</strong> ${issue.product || '未知'}</div>
<div><strong>时间:</strong> ${issue.time || '未知'}</div>
</div>
`;
});
html += '</div>';
listContainer.innerHTML = html;
dialog.style.display = 'block';
}
// 添加更新全天数据的函数
function checkDaily() {
const restartButton = document.querySelector('.restart-button');
if (restartButton) {
restartButton.disabled = true;
restartButton.textContent = '更新中...';
restartButton.style.opacity = '0.5';
restartButton.style.cursor = 'not-allowed';
}
fetch('/api/check-daily')
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('更新成功', '全天数据更新已触发,请稍后查看结果。', 'success');
setTimeout(fetchStats, 5000);
} else {
showNotification('更新失败', data.message, 'error');
}
})
.catch(error => {
console.error('更新全天数据出错:', error);
showNotification('更新失败', '请求发生错误,请查看控制台获取详情', 'error');
})
.finally(() => {
if (restartButton) {
setTimeout(() => {
restartButton.disabled = false;
restartButton.textContent = '更新全天数据';
restartButton.style.opacity = '1';
restartButton.style.cursor = 'pointer';
}, 3000);
}
});
}
// 消息提示函数
function showMessage(type, content) {
const container = document.getElementById('messageContainer');
const message = document.createElement('div');
message.className = `message ${type}`;
message.textContent = content;
container.appendChild(message);
// 3秒后自动移除消息
setTimeout(() => {
message.addEventListener('animationend', () => {
container.removeChild(message);
});
}, 3000);
}
// 添加全局消息对象
window.message = {
success: (content) => showMessage('success', content),
info: (content) => showMessage('info', content),
error: (content) => showMessage('error', content)
};
// 在页面加载完成后检查是否需要显示欢迎消息
document.addEventListener('DOMContentLoaded', function() {
const staff_name = localStorage.getItem('staff_name');
if (staff_name) {
showMessage('info', `你好,${staff_name}`);
localStorage.removeItem('staff_name'); // 清除,确保消息只显示一次
}
});
function updateStats(data) {
try {
// 更新统计栏数据 - 使用默认值处理缺失数据
const breezeHourly = data.breeze && data.breeze.hourly ? data.breeze.hourly : { total: 0, weighted_total: 0 };
const breezeDaily = data.breeze && data.breeze.daily ? data.breeze.daily : { total: 0, weighted_total: 0 };
document.getElementById('breeze-total').textContent = breezeHourly.total || '0';
document.getElementById('breeze-daily-total').textContent = breezeDaily.total || '0';
// 更新时间戳
if (data.breeze) {
if (data.breeze.hourly_update) {
document.getElementById('breeze-hourly-time').textContent = data.breeze.hourly_update;
}
if (data.breeze.daily_update) {
document.getElementById('breeze-daily-time').textContent = data.breeze.daily_update;
}
}
// 更新CMS数据 - 使用默认值处理缺失数据
const cmsHourly = data.cms && data.cms.hourly ? data.cms.hourly : { total_count: 0, weighted_total: 0 };
const cmsDaily = data.cms && data.cms.daily ? data.cms.daily : { total_count: 0, weighted_total: 0 };
// 更新顶部统计栏
document.getElementById('cms-total').textContent = cmsHourly.total_count || '0';
document.getElementById('cms-daily-total').textContent = cmsDaily.total_count || '0';
// 更新时间戳
if (data.cms) {
if (data.cms.hourly_update) {
document.getElementById('cms-hourly-time').textContent = data.cms.hourly_update;
}
if (data.cms.daily_update) {
document.getElementById('cms-daily-time').textContent = data.cms.daily_update;
}
}
// 更新CC审核平台数据 - 使用默认值处理缺失数据
const inspectHourly = data.inspect && data.inspect.hourly ? data.inspect.hourly : { total: 0, weighted_total: 0 };
const inspectDaily = data.inspect && data.inspect.daily ? data.inspect.daily : { total: 0, weighted_total: 0 };
document.getElementById('inspect-hourly-total').textContent = inspectHourly.total || '0';
document.getElementById('inspect-hourly-weighted').textContent = `(${Math.round(inspectHourly.weighted_total || 0)})`;
document.getElementById('inspect-daily-total').textContent = inspectDaily.total || '0';
document.getElementById('inspect-daily-weighted').textContent = `(${Math.round(inspectDaily.weighted_total || 0)})`;
// 更新时间戳
if (data.inspect) {
if (data.inspect.hourly_update) {
document.getElementById('inspect-hourly-time').textContent = data.inspect.hourly_update;
}
if (data.inspect.daily_update) {
document.getElementById('inspect-daily-time').textContent = data.inspect.daily_update;
}
}
// 更新总计数据 - 确保即使某个业务无数据也能正确显示总量级
if (data.total) {
document.getElementById('total-weighted-hourly').textContent = Math.round(data.total.hourly || 0);
document.getElementById('total-weighted-daily').textContent = Math.round(data.total.daily || 0);
// 获取最新的时间戳
const hourlyUpdateTime = getLatestTimestamp([
data.breeze?.hourly_update,
data.cms?.hourly_update,
data.inspect?.hourly_update
]);
const dailyUpdateTime = getLatestTimestamp([
data.breeze?.daily_update,
data.cms?.daily_update,
data.inspect?.daily_update
]);
// 更新时间戳显示
if (hourlyUpdateTime) {
document.getElementById('total-hourly-time').textContent = hourlyUpdateTime;
}
if (dailyUpdateTime) {
document.getElementById('total-daily-time').textContent = dailyUpdateTime;
}
}
} catch (error) {
console.error('Error updating stats:', error);
}
}
function updateInspectStats(inspectData) {
if (inspectData.hourly) {
document.getElementById('inspect-hourly-total').textContent = inspectData.hourly.weighted_total.toFixed(2);
document.getElementById('inspect-hourly-time').textContent = formatTimestamp(inspectData.hourly_update);
}
}
// 添加一个辅助函数来获取最新的时间戳
function getLatestTimestamp(timestamps) {
const validTimestamps = timestamps.filter(t => t);
if (validTimestamps.length === 0) return null;
// 假设时间戳格式为 "YYYY-MM-DD HH:mm:ss"
return validTimestamps.reduce((latest, current) => {
if (!latest) return current;
return new Date(current) > new Date(latest) ? current : latest;
}, null);
}
</script>
</body>
</html>