944 lines
31 KiB
PHP
944 lines
31 KiB
PHP
<?php
|
||
// 设置错误报告
|
||
ini_set('display_errors', 1);
|
||
error_reporting(E_ALL);
|
||
|
||
// 数据库连接信息
|
||
$db_host = '192.168.2.4';
|
||
$db_port = 3307;
|
||
$db_name = 'task_reporter';
|
||
$db_user = 'task_reporter';
|
||
$db_pass = 'Pass12349ers!';
|
||
|
||
// 创建数据库连接
|
||
try {
|
||
$pdo = new PDO("mysql:host=$db_host;port=$db_port;dbname=$db_name", $db_user, $db_pass);
|
||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||
$pdo->exec("SET NAMES utf8");
|
||
} catch (PDOException $e) {
|
||
die("数据库连接失败: " . $e->getMessage());
|
||
}
|
||
|
||
// 获取所有工具名称和任务名称(用于筛选)
|
||
$tools_stmt = $pdo->query("SELECT DISTINCT tool_name FROM task ORDER BY tool_name");
|
||
$tools = $tools_stmt->fetchAll(PDO::FETCH_COLUMN);
|
||
|
||
$tasks_stmt = $pdo->query("SELECT DISTINCT task_name FROM task ORDER BY task_name");
|
||
$task_names = $tasks_stmt->fetchAll(PDO::FETCH_COLUMN);
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<title>Task Report Data</title>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<style>
|
||
body {
|
||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||
margin: 0;
|
||
padding: 20px;
|
||
background-color: #f5f5f5;
|
||
color: #333;
|
||
}
|
||
|
||
h1 {
|
||
color: #ff8c00;
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
background-color: #fff;
|
||
padding: 20px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.filters {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||
gap: 15px;
|
||
margin-bottom: 20px;
|
||
padding: 15px;
|
||
background-color: #f9f9f9;
|
||
border-radius: 5px;
|
||
}
|
||
|
||
.filter-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.filter-group label {
|
||
margin-bottom: 5px;
|
||
font-weight: bold;
|
||
color: #555;
|
||
}
|
||
|
||
.filter-group select,
|
||
.filter-group input {
|
||
padding: 8px;
|
||
border: 1px solid #ddd;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.buttons {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
button {
|
||
padding: 10px 20px;
|
||
background-color: #ff8c00;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
transition: background-color 0.3s;
|
||
}
|
||
|
||
button:hover {
|
||
background-color: #e67e00;
|
||
}
|
||
|
||
.export-btn {
|
||
background-color: #4CAF50;
|
||
}
|
||
|
||
.export-btn:hover {
|
||
background-color: #45a049;
|
||
}
|
||
|
||
.back-btn {
|
||
background-color: #555;
|
||
}
|
||
|
||
.back-btn:hover {
|
||
background-color: #444;
|
||
}
|
||
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
th, td {
|
||
padding: 12px 15px;
|
||
text-align: left;
|
||
border-bottom: 1px solid #ddd;
|
||
}
|
||
|
||
th {
|
||
background-color: #f2f2f2;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
}
|
||
|
||
th:hover {
|
||
background-color: #e6e6e6;
|
||
}
|
||
|
||
tr:hover {
|
||
background-color: #f9f9f9;
|
||
}
|
||
|
||
.pagination {
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.pagination button {
|
||
margin: 0 5px;
|
||
padding: 8px 12px;
|
||
background-color: #f2f2f2;
|
||
color: #333;
|
||
border: 1px solid #ddd;
|
||
}
|
||
|
||
.pagination button.active {
|
||
background-color: #ff8c00;
|
||
color: white;
|
||
border-color: #ff8c00;
|
||
}
|
||
|
||
.pagination button:hover:not(.active) {
|
||
background-color: #ddd;
|
||
}
|
||
|
||
.stats-container {
|
||
margin-top: 30px;
|
||
padding: 20px;
|
||
background-color: #f9f9f9;
|
||
border-radius: 5px;
|
||
flex: 1;
|
||
}
|
||
|
||
.stats-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.stats-header h2 {
|
||
margin: 0;
|
||
color: #555;
|
||
}
|
||
|
||
/* 标签页样式 */
|
||
.tabs {
|
||
display: flex;
|
||
border-bottom: 1px solid #ddd;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.tab {
|
||
padding: 10px 20px;
|
||
cursor: pointer;
|
||
background-color: #f2f2f2;
|
||
border: 1px solid #ddd;
|
||
border-bottom: none;
|
||
margin-right: 5px;
|
||
border-top-left-radius: 5px;
|
||
border-top-right-radius: 5px;
|
||
}
|
||
|
||
.tab.active {
|
||
background-color: #fff;
|
||
border-bottom: 1px solid #fff;
|
||
margin-bottom: -1px;
|
||
font-weight: bold;
|
||
color: #ff8c00;
|
||
}
|
||
|
||
.tab-content {
|
||
display: none;
|
||
}
|
||
|
||
.tab-content.active {
|
||
display: block;
|
||
}
|
||
|
||
.chart-container {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 20px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.chart {
|
||
flex: 1;
|
||
min-width: 300px;
|
||
height: 300px;
|
||
background-color: #fff;
|
||
border-radius: 5px;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||
padding: 15px;
|
||
position: relative;
|
||
}
|
||
|
||
.tool-stats {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||
gap: 15px;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.tool-card {
|
||
background-color: #fff;
|
||
border-radius: 5px;
|
||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||
padding: 15px;
|
||
cursor: pointer;
|
||
transition: transform 0.2s, box-shadow 0.2s;
|
||
}
|
||
|
||
.tool-card:hover {
|
||
transform: translateY(-3px);
|
||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.tool-card h3 {
|
||
margin-top: 0;
|
||
color: #ff8c00;
|
||
}
|
||
|
||
.tool-card p {
|
||
margin: 5px 0;
|
||
color: #666;
|
||
}
|
||
|
||
.tool-card .count {
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.tool-card .time-saved {
|
||
color: #4CAF50;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.modal {
|
||
display: none;
|
||
position: fixed;
|
||
z-index: 1000;
|
||
left: 0;
|
||
top: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
}
|
||
|
||
.modal-content {
|
||
background-color: #fff;
|
||
margin: 10% auto;
|
||
padding: 20px;
|
||
width: 80%;
|
||
max-width: 800px;
|
||
border-radius: 5px;
|
||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.close {
|
||
color: #aaa;
|
||
float: right;
|
||
font-size: 28px;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.close:hover {
|
||
color: #555;
|
||
}
|
||
|
||
#loading {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: rgba(255, 255, 255, 0.8);
|
||
z-index: 9999;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.spinner {
|
||
border: 5px solid #f3f3f3;
|
||
border-top: 5px solid #ff8c00;
|
||
border-radius: 50%;
|
||
width: 50px;
|
||
height: 50px;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
|
||
@keyframes spin {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="loading">
|
||
<div class="spinner"></div>
|
||
</div>
|
||
|
||
<div class="container">
|
||
<h1>Task Report Data</h1>
|
||
|
||
<div class="filters">
|
||
<div class="filter-group">
|
||
<label for="start-date">开始日期:</label>
|
||
<input type="date" id="start-date">
|
||
</div>
|
||
|
||
<div class="filter-group">
|
||
<label for="end-date">结束日期:</label>
|
||
<input type="date" id="end-date">
|
||
</div>
|
||
|
||
<div class="filter-group">
|
||
<label for="tool-name">工具名称:</label>
|
||
<select id="tool-name">
|
||
<option value="">全部</option>
|
||
<?php foreach ($tools as $tool): ?>
|
||
<option value="<?php echo htmlspecialchars($tool); ?>"><?php echo htmlspecialchars($tool); ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="filter-group">
|
||
<label for="task-name">任务名称:</label>
|
||
<select id="task-name">
|
||
<option value="">全部</option>
|
||
<?php foreach ($task_names as $task_name): ?>
|
||
<option value="<?php echo htmlspecialchars($task_name); ?>"><?php echo htmlspecialchars($task_name); ?></option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="filter-group">
|
||
<label for="show-debug">显示调试工具:</label>
|
||
<select id="show-debug">
|
||
<option value="false">否</option>
|
||
<option value="true">是</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="filter-group">
|
||
<label for="sort-by">排序字段:</label>
|
||
<select id="sort-by">
|
||
<option value="timestamp">时间</option>
|
||
<option value="time_saved">节省时间</option>
|
||
<option value="time_cost">花费时间</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="filter-group">
|
||
<label for="sort-direction">排序方向:</label>
|
||
<select id="sort-direction">
|
||
<option value="desc">降序</option>
|
||
<option value="asc">升序</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="buttons">
|
||
<button id="filter-btn">应用筛选</button>
|
||
<button id="export-btn" class="export-btn">导出CSV</button>
|
||
<button class="report-btn" onclick="window.location.href='generate_report.php'">查看报告</button>
|
||
<button class="back-btn" onclick="window.location.href='index.php'">返回首页</button>
|
||
</div>
|
||
|
||
<div class="stats-container">
|
||
<div class="stats-header">
|
||
<h2>数据与统计</h2>
|
||
</div>
|
||
|
||
<div class="tabs">
|
||
<div class="tab active" data-tab="data">请求数据</div>
|
||
<div class="tab" data-tab="trends">趋势分析</div>
|
||
<div class="tab" data-tab="tools">工具统计</div>
|
||
</div>
|
||
|
||
<div id="data-tab" class="tab-content active">
|
||
<table id="task-table">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>用户名</th>
|
||
<th>工具名称</th>
|
||
<th>任务名称</th>
|
||
<th>节省时间</th>
|
||
<th>花费时间</th>
|
||
<th>时间戳</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="task-data">
|
||
<!-- 任务数据将通过JavaScript动态加载 -->
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="pagination" id="pagination">
|
||
<!-- 分页按钮将通过JavaScript动态生成 -->
|
||
</div>
|
||
</div>
|
||
|
||
<div id="trends-tab" class="tab-content">
|
||
<div class="chart-container">
|
||
<div class="chart" id="requests-chart">
|
||
<canvas id="requests-canvas"></canvas>
|
||
</div>
|
||
<div class="chart" id="time-saved-chart">
|
||
<canvas id="time-saved-canvas"></canvas>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tools-tab" class="tab-content">
|
||
<div class="tool-stats" id="tool-stats">
|
||
<!-- 工具统计数据将通过JavaScript动态加载 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="tool-modal" class="modal">
|
||
<div class="modal-content">
|
||
<span class="close">×</span>
|
||
<h2 id="modal-title">工具详情</h2>
|
||
<div id="modal-content">
|
||
<!-- 工具详情将通过JavaScript动态加载 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||
<script>
|
||
// 全局变量
|
||
let currentPage = 1;
|
||
let totalPages = 1;
|
||
let perPage = 50;
|
||
let requestsChart = null;
|
||
let timeSavedChart = null;
|
||
|
||
// DOM元素
|
||
const startDateInput = document.getElementById('start-date');
|
||
const endDateInput = document.getElementById('end-date');
|
||
const toolNameSelect = document.getElementById('tool-name');
|
||
const taskNameSelect = document.getElementById('task-name');
|
||
const showDebugSelect = document.getElementById('show-debug');
|
||
const sortBySelect = document.getElementById('sort-by');
|
||
const sortDirectionSelect = document.getElementById('sort-direction');
|
||
const filterBtn = document.getElementById('filter-btn');
|
||
const exportBtn = document.getElementById('export-btn');
|
||
const taskDataContainer = document.getElementById('task-data');
|
||
const paginationContainer = document.getElementById('pagination');
|
||
const toolStatsContainer = document.getElementById('tool-stats');
|
||
const modal = document.getElementById('tool-modal');
|
||
const modalTitle = document.getElementById('modal-title');
|
||
const modalContent = document.getElementById('modal-content');
|
||
const closeModal = document.getElementsByClassName('close')[0];
|
||
const loading = document.getElementById('loading');
|
||
|
||
// 设置默认日期范围(最近30天)
|
||
const today = new Date();
|
||
const thirtyDaysAgo = new Date();
|
||
thirtyDaysAgo.setDate(today.getDate() - 30);
|
||
|
||
startDateInput.value = formatDate(thirtyDaysAgo);
|
||
endDateInput.value = formatDate(today);
|
||
|
||
// 初始化页面
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
loadTaskData();
|
||
loadToolStatistics();
|
||
loadTrendData();
|
||
|
||
// 事件监听器
|
||
filterBtn.addEventListener('click', function() {
|
||
currentPage = 1;
|
||
loadTaskData();
|
||
loadToolStatistics();
|
||
loadTrendData();
|
||
});
|
||
|
||
exportBtn.addEventListener('click', exportCsv);
|
||
|
||
closeModal.addEventListener('click', function() {
|
||
modal.style.display = 'none';
|
||
});
|
||
|
||
window.addEventListener('click', function(event) {
|
||
if (event.target === modal) {
|
||
modal.style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// 标签页切换
|
||
document.querySelectorAll('.tab').forEach(tab => {
|
||
tab.addEventListener('click', function() {
|
||
// 移除所有标签页的active类
|
||
document.querySelectorAll('.tab').forEach(t => {
|
||
t.classList.remove('active');
|
||
});
|
||
|
||
// 移除所有内容区的active类
|
||
document.querySelectorAll('.tab-content').forEach(content => {
|
||
content.classList.remove('active');
|
||
});
|
||
|
||
// 添加当前标签页的active类
|
||
this.classList.add('active');
|
||
|
||
// 显示对应的内容区
|
||
const tabId = this.getAttribute('data-tab');
|
||
document.getElementById(tabId + '-tab').classList.add('active');
|
||
|
||
// 如果切换到趋势分析标签页,重新渲染图表
|
||
if (tabId === 'trends') {
|
||
setTimeout(() => {
|
||
if (requestsChart) requestsChart.resize();
|
||
if (timeSavedChart) timeSavedChart.resize();
|
||
}, 10);
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
// 加载任务数据
|
||
function loadTaskData() {
|
||
showLoading();
|
||
|
||
const params = new URLSearchParams({
|
||
action: 'query_task_data',
|
||
start_date: startDateInput.value,
|
||
end_date: endDateInput.value,
|
||
tool_name: toolNameSelect.value,
|
||
task_name: taskNameSelect.value,
|
||
show_debug: showDebugSelect.value,
|
||
sort_by: sortBySelect.value,
|
||
sort_direction: sortDirectionSelect.value,
|
||
page: currentPage,
|
||
per_page: perPage
|
||
});
|
||
|
||
fetch(`index.php?${params.toString()}`)
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.error) {
|
||
alert('Error: ' + data.error);
|
||
return;
|
||
}
|
||
|
||
renderTaskData(data.tasks);
|
||
renderPagination(data.page, data.total_pages);
|
||
totalPages = data.total_pages;
|
||
|
||
hideLoading();
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
alert('加载数据时出错');
|
||
hideLoading();
|
||
});
|
||
}
|
||
|
||
// 渲染任务数据
|
||
function renderTaskData(tasks) {
|
||
taskDataContainer.innerHTML = '';
|
||
|
||
if (tasks.length === 0) {
|
||
const row = document.createElement('tr');
|
||
row.innerHTML = '<td colspan="7" style="text-align: center;">没有找到匹配的数据</td>';
|
||
taskDataContainer.appendChild(row);
|
||
return;
|
||
}
|
||
|
||
tasks.forEach(task => {
|
||
const row = document.createElement('tr');
|
||
row.innerHTML = `
|
||
<td>${task.id}</td>
|
||
<td>${task.username}</td>
|
||
<td>${task.tool_name}</td>
|
||
<td>${task.task_name}</td>
|
||
<td>${task.time_saved}</td>
|
||
<td>${task.time_cost}</td>
|
||
<td>${task.timestamp}</td>
|
||
`;
|
||
taskDataContainer.appendChild(row);
|
||
});
|
||
}
|
||
|
||
// 渲染分页
|
||
function renderPagination(currentPage, totalPages) {
|
||
paginationContainer.innerHTML = '';
|
||
|
||
if (totalPages <= 1) {
|
||
return;
|
||
}
|
||
|
||
// 添加"上一页"按钮
|
||
if (currentPage > 1) {
|
||
const prevBtn = document.createElement('button');
|
||
prevBtn.textContent = '上一页';
|
||
prevBtn.addEventListener('click', () => {
|
||
goToPage(currentPage - 1);
|
||
});
|
||
paginationContainer.appendChild(prevBtn);
|
||
}
|
||
|
||
// 添加页码按钮
|
||
let startPage = Math.max(1, currentPage - 2);
|
||
let endPage = Math.min(totalPages, startPage + 4);
|
||
|
||
if (endPage - startPage < 4) {
|
||
startPage = Math.max(1, endPage - 4);
|
||
}
|
||
|
||
for (let i = startPage; i <= endPage; i++) {
|
||
const pageBtn = document.createElement('button');
|
||
pageBtn.textContent = i;
|
||
pageBtn.className = i === currentPage ? 'active' : '';
|
||
pageBtn.addEventListener('click', () => {
|
||
goToPage(i);
|
||
});
|
||
paginationContainer.appendChild(pageBtn);
|
||
}
|
||
|
||
// 添加"下一页"按钮
|
||
if (currentPage < totalPages) {
|
||
const nextBtn = document.createElement('button');
|
||
nextBtn.textContent = '下一页';
|
||
nextBtn.addEventListener('click', () => {
|
||
goToPage(currentPage + 1);
|
||
});
|
||
paginationContainer.appendChild(nextBtn);
|
||
}
|
||
}
|
||
|
||
// 跳转到指定页
|
||
function goToPage(page) {
|
||
currentPage = page;
|
||
loadTaskData();
|
||
window.scrollTo(0, 0);
|
||
}
|
||
|
||
// 加载工具统计数据
|
||
function loadToolStatistics() {
|
||
const params = new URLSearchParams({
|
||
action: 'get_tool_statistics',
|
||
start_date: startDateInput.value,
|
||
end_date: endDateInput.value
|
||
});
|
||
|
||
fetch(`index.php?${params.toString()}`)
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.error) {
|
||
alert('Error: ' + data.error);
|
||
return;
|
||
}
|
||
|
||
renderToolStatistics(data.tools, data.total_requests, data.total_time_saved);
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
});
|
||
}
|
||
|
||
// 渲染工具统计数据
|
||
function renderToolStatistics(tools, totalRequests, totalTimeSaved) {
|
||
toolStatsContainer.innerHTML = '';
|
||
|
||
if (tools.length === 0) {
|
||
toolStatsContainer.innerHTML = '<p>没有找到匹配的数据</p>';
|
||
return;
|
||
}
|
||
|
||
tools.forEach(tool => {
|
||
const card = document.createElement('div');
|
||
card.className = 'tool-card';
|
||
card.innerHTML = `
|
||
<h3>${tool.tool_name}</h3>
|
||
<p>请求次数: <span class="count">${tool.request_count}</span></p>
|
||
<p>节省时间: <span class="time-saved">${parseFloat(tool.time_saved).toFixed(2)} 小时</span></p>
|
||
`;
|
||
|
||
card.addEventListener('click', () => {
|
||
showToolDetails(tool.tool_name);
|
||
});
|
||
|
||
toolStatsContainer.appendChild(card);
|
||
});
|
||
}
|
||
|
||
// 显示工具详情
|
||
function showToolDetails(toolName) {
|
||
showLoading();
|
||
|
||
const params = new URLSearchParams({
|
||
action: 'get_tool_tasks',
|
||
tool_name: toolName
|
||
});
|
||
|
||
fetch(`index.php?${params.toString()}`)
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.error) {
|
||
alert('Error: ' + data.error);
|
||
return;
|
||
}
|
||
|
||
modalTitle.textContent = `${toolName} 详情`;
|
||
|
||
if (data.tasks.length === 0) {
|
||
modalContent.innerHTML = '<p>没有找到匹配的数据</p>';
|
||
} else {
|
||
let html = '<table style="width:100%"><thead><tr><th>任务名称</th><th>请求次数</th><th>节省时间</th></tr></thead><tbody>';
|
||
|
||
data.tasks.forEach(task => {
|
||
html += `
|
||
<tr>
|
||
<td>${task.task_name}</td>
|
||
<td>${task.total_requests}</td>
|
||
<td>${parseFloat(task.time_saved).toFixed(2)} 小时</td>
|
||
</tr>
|
||
`;
|
||
});
|
||
|
||
html += '</tbody></table>';
|
||
modalContent.innerHTML = html;
|
||
}
|
||
|
||
modal.style.display = 'block';
|
||
hideLoading();
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
hideLoading();
|
||
});
|
||
}
|
||
|
||
// 加载趋势数据
|
||
function loadTrendData() {
|
||
const params = new URLSearchParams({
|
||
action: 'get_trend_data',
|
||
start_date: startDateInput.value,
|
||
end_date: endDateInput.value
|
||
});
|
||
|
||
fetch(`index.php?${params.toString()}`)
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.error) {
|
||
alert('Error: ' + data.error);
|
||
return;
|
||
}
|
||
|
||
renderTrendCharts(data.dates, data.requests, data.timeSaved);
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
});
|
||
}
|
||
|
||
// 渲染趋势图表
|
||
function renderTrendCharts(dates, requests, timeSaved) {
|
||
// 请求次数趋势图
|
||
const requestsCtx = document.getElementById('requests-canvas').getContext('2d');
|
||
|
||
if (requestsChart) {
|
||
requestsChart.destroy();
|
||
}
|
||
|
||
requestsChart = new Chart(requestsCtx, {
|
||
type: 'line',
|
||
data: {
|
||
labels: dates,
|
||
datasets: [{
|
||
label: '每日请求次数',
|
||
data: requests,
|
||
backgroundColor: 'rgba(255, 140, 0, 0.2)',
|
||
borderColor: 'rgba(255, 140, 0, 1)',
|
||
borderWidth: 2,
|
||
pointBackgroundColor: 'rgba(255, 140, 0, 1)',
|
||
tension: 0.4
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
plugins: {
|
||
title: {
|
||
display: true,
|
||
text: '每日请求次数趋势',
|
||
font: {
|
||
size: 16
|
||
}
|
||
},
|
||
legend: {
|
||
display: true,
|
||
position: 'top'
|
||
}
|
||
},
|
||
scales: {
|
||
y: {
|
||
beginAtZero: true,
|
||
ticks: {
|
||
precision: 0
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
// 节省时间趋势图
|
||
const timeSavedCtx = document.getElementById('time-saved-canvas').getContext('2d');
|
||
|
||
if (timeSavedChart) {
|
||
timeSavedChart.destroy();
|
||
}
|
||
|
||
timeSavedChart = new Chart(timeSavedCtx, {
|
||
type: 'line',
|
||
data: {
|
||
labels: dates,
|
||
datasets: [{
|
||
label: '每日节省时间(小时)',
|
||
data: timeSaved,
|
||
backgroundColor: 'rgba(76, 175, 80, 0.2)',
|
||
borderColor: 'rgba(76, 175, 80, 1)',
|
||
borderWidth: 2,
|
||
pointBackgroundColor: 'rgba(76, 175, 80, 1)',
|
||
tension: 0.4
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
plugins: {
|
||
title: {
|
||
display: true,
|
||
text: '每日节省时间趋势',
|
||
font: {
|
||
size: 16
|
||
}
|
||
},
|
||
legend: {
|
||
display: true,
|
||
position: 'top'
|
||
}
|
||
},
|
||
scales: {
|
||
y: {
|
||
beginAtZero: true
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// 导出CSV
|
||
function exportCsv() {
|
||
const params = new URLSearchParams({
|
||
action: 'export_csv',
|
||
start_date: startDateInput.value,
|
||
end_date: endDateInput.value,
|
||
tool_name: toolNameSelect.value,
|
||
task_name: taskNameSelect.value,
|
||
show_debug: showDebugSelect.value
|
||
});
|
||
|
||
window.location.href = `index.php?${params.toString()}`;
|
||
}
|
||
|
||
// 格式化日期为YYYY-MM-DD
|
||
function formatDate(date) {
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
return `${year}-${month}-${day}`;
|
||
}
|
||
|
||
// 显示加载中
|
||
function showLoading() {
|
||
loading.style.display = 'flex';
|
||
}
|
||
|
||
// 隐藏加载中
|
||
function hideLoading() {
|
||
loading.style.display = 'none';
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|