统计模块中任务分析静态页面

This commit is contained in:
yis 2025-07-11 10:40:42 +08:00
parent ea7c131298
commit 4dffec016a

633
src/views/service/task.html Normal file
View File

@ -0,0 +1,633 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>任务监控数据可视化平台</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<style>
:root {
--primary-blue: #3A86FF;
--alert-red: #FF4D6D;
--success-green: #06D6A0;
--neutral-gray: #8E9AAF;
--light-gray: #F8F9FA;
--dark-gray: #343A40;
--border-radius: 8px;
--shadow: 0 3px 12px rgba(0, 0, 0, 0.05);
}
body {
background-color: #f0f2f5;
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
padding: 20px;
}
.dashboard-container {
max-width: 1440px;
margin: 0 auto;
background: white;
border-radius: var(--border-radius);
box-shadow: var(--shadow);
overflow: hidden;
}
.kpi-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
padding: 30px;
}
.kpi-card {
display: flex;
flex-direction: column;
padding: 24px;
background: white;
border-radius: var(--border-radius);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.04);
position: relative;
overflow: hidden;
transition: transform 0.3s ease;
}
.kpi-card:hover {
transform: translateY(-5px);
box-shadow: 0 6px 12px rgba(58, 134, 255, 0.15);
}
.kpi-card::after {
content: '';
position: absolute;
right: 24px;
top: 24px;
width: 48px;
height: 48px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
}
.kpi-card:nth-child(1)::after {
background: rgba(6, 214, 160, 0.1);
}
.kpi-card:nth-child(2)::after {
background: rgba(255, 77, 109, 0.1);
}
.kpi-card:nth-child(3)::after {
background: rgba(58, 134, 255, 0.1);
}
.kpi-card:nth-child(4)::after {
background: rgba(142, 154, 175, 0.1);
}
.kpi-value {
font-size: 32px;
font-weight: 700;
margin: 12px 0 8px;
}
.kpi-card:nth-child(1) .kpi-value {
color: var(--success-green);
}
.kpi-card:nth-child(2) .kpi-value {
color: var(--alert-red);
}
.kpi-card:nth-child(3) .kpi-value {
color: var(--primary-blue);
}
.kpi-card:nth-child(4) .kpi-value {
color: var(--neutral-gray);
}
.trend-change {
font-size: 14px;
display: flex;
align-items: center;
margin-top: 4px;
}
.trend-up {
color: var(--success-green);
}
.trend-down {
color: var(--alert-red);
}
.chart-container {
border-radius: var(--border-radius);
background: var(--light-gray);
padding: 24px;
margin: 0 30px 20px;
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.03);
}
.chart-title {
font-size: 18px;
font-weight: 600;
color: var(--dark-gray);
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.dual-column {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
padding: 0 30px 40px;
}
.date-range-picker {
display: flex;
align-items: center;
background: white;
border: 1px solid #e2e8f0;
border-radius: var(--border-radius);
padding: 8px 12px;
font-size: 14px;
color: var(--dark-gray);
cursor: pointer;
}
.day-selector {
border: 1px solid #e2e8f0;
border-radius: var(--border-radius);
padding: 8px 12px;
font-size: 14px;
color: var(--dark-gray);
background: white;
}
.chart-holder {
height: 350px;
}
/* Flatpickr 样式覆盖 */
.flatpickr-calendar {
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
box-shadow: var(--shadow);
border-radius: var(--border-radius);
}
.flatpickr-day.selected {
background: var(--primary-blue);
border-color: var(--primary-blue);
}
</style>
</head>
<body>
<div class="dashboard-container">
<!-- 顶部KPI区域 -->
<div class="kpi-grid">
<div class="kpi-card">
<div>实时任务完成率</div>
<div class="kpi-value">82.5%</div>
<div class="trend-change trend-up">
<iconify-icon icon="mingcute:arrow-up-line" style="margin-right: 4px;"></iconify-icon>
较昨日 ↑2.1%
</div>
<iconify-icon icon="material-symbols:data-usage"
style="position: absolute; right: 38px; top: 38px; color: var(--success-green); font-size: 24px;"></iconify-icon>
</div>
<div class="kpi-card">
<div>关键任务失败率</div>
<div class="kpi-value">5.2%</div>
<div class="trend-change trend-down">
<iconify-icon icon="mingcute:arrow-down-line" style="margin-right: 4px;"></iconify-icon>
同比 ↓1.3%
</div>
<iconify-icon icon="ic:outline-schedule"
style="position: absolute; right: 38px; top: 38px; color: var(--alert-red); font-size: 24px;"></iconify-icon>
</div>
<div class="kpi-card">
<div>平均任务周期</div>
<div class="kpi-value">4.8h</div>
<div class="trend-change">
<iconify-icon icon="mingcute:arrow-right-line"
style="margin-right: 4px; color: var(--neutral-gray);"></iconify-icon>
行业平均 5.2h
</div>
<iconify-icon icon="mdi:timer-outline"
style="position: absolute; right: 38px; top: 38px; color: var(--primary-blue); font-size: 24px;"></iconify-icon>
</div>
<div class="kpi-card">
<div>资源闲置率</div>
<div class="kpi-value">12.3%</div>
<div class="trend-change trend-down">
<iconify-icon icon="mingcute:arrow-down-line" style="margin-right: 4px;"></iconify-icon>
周同比 ↓3.5%
</div>
<iconify-icon icon="mdi:server-off"
style="position: absolute; right: 38px; top: 38px; color: var(--neutral-gray); font-size: 24px;"></iconify-icon>
</div>
</div>
<!-- 中部趋势图 -->
<div class="chart-container">
<div class="chart-title">
<span>任务完成趋势分析</span>
<div class="date-range-picker" id="dateRangePicker">
<iconify-icon icon="material-symbols:calendar-month"
style="color: var(--neutral-gray); margin-right: 8px;"></iconify-icon>
<span id="dateRangeDisplay">2023年7月1日 - 2023年7月31日</span>
</div>
</div>
<div id="trendChart" class="chart-holder"></div>
</div>
<!-- 底部双列图表 -->
<div class="dual-column">
<!-- 左侧饼图 -->
<div class="chart-container">
<div class="chart-title">任务状态分布</div>
<div id="pieChart" class="chart-holder"></div>
</div>
<!-- 右侧堆叠柱状图 -->
<div class="chart-container">
<div class="chart-title">
<span>本周24小时任务状态分布</span>
<select id="daySelector" class="day-selector">
<option value="0">周一</option>
<option value="1">周二</option>
<option value="2">周三</option>
<option value="3">周四</option>
<option value="4">周五</option>
</select>
</div>
<div id="stackedBarChart" class="chart-holder"></div>
</div>
</div>
</div>
<script>
// 模拟数据生成器
const generateTrendData = (startDate, endDate) => {
const start = new Date(startDate);
const end = new Date(endDate);
const days = Math.ceil((end - start) / (1000 * 60 * 60 * 24)) + 1;
const labels = [];
const completed = [];
const inProgress = [];
const notStarted = [];
const failed = [];
for (let i = 0; i < days; i++) {
const date = new Date(start);
date.setDate(start.getDate() + i);
labels.push(`${date.getMonth() + 1}/${date.getDate()}`);
// 生成有波动趋势的数据
const baseCompleted = 300 + Math.sin(i / 3) * 50 + Math.random() * 30;
const baseInProgress = 150 + Math.cos(i / 2) * 30 + Math.random() * 20;
const baseNotStarted = 80 + Math.sin(i / 4) * 20 + Math.random() * 15;
const baseFailed = 10 + Math.abs(Math.sin(i / 5)) * 8 + Math.random() * 5;
completed.push(Math.round(baseCompleted));
inProgress.push(Math.round(baseInProgress));
notStarted.push(Math.round(baseNotStarted));
failed.push(Math.round(baseFailed));
}
return { labels, completed, inProgress, notStarted, failed };
};
// 初始化日期选择器
const datePicker = flatpickr("#dateRangePicker", {
mode: "range",
dateFormat: "Y-m-d",
defaultDate: ["2023-07-01", "2023-07-31"],
locale: {
rangeSeparator: " 至 ",
monthNames: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
weekdays: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
},
onChange: function (selectedDates, dateStr, instance) {
if (selectedDates.length === 2) {
const startDate = selectedDates[0];
const endDate = selectedDates[1];
// 更新显示
const startStr = `${startDate.getFullYear()}年${startDate.getMonth() + 1}月${startDate.getDate()}日`;
const endStr = `${endDate.getFullYear()}年${endDate.getMonth() + 1}月${endDate.getDate()}日`;
document.getElementById('dateRangeDisplay').textContent = `${startStr} - ${endStr}`;
// 生成新数据并更新图表
const newData = generateTrendData(startDate, endDate);
renderTrendChart(newData);
}
}
});
// 初始化图表
document.addEventListener('DOMContentLoaded', function () {
// 1. 任务趋势折线图
const trendChart = echarts.init(document.getElementById('trendChart'));
let currentTrendData = generateTrendData(new Date(2023, 6, 1), new Date(2023, 6, 31));
const renderTrendChart = (data) => {
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: params => {
let result = `${params[0].axisValue}<br>`;
params.forEach(param => {
result += `${param.marker} ${param.seriesName}: <strong>${param.value}</strong><br>`;
});
return result;
}
},
legend: {
data: ['已完成', '进行中', '未进行', '任务失败'],
bottom: 0,
textStyle: { color: '#8E9AAF' }
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
top: '5%',
containLabel: true
},
xAxis: {
type: 'category',
data: data.labels,
axisLine: { lineStyle: { color: '#e2e8f0' } },
axisLabel: {
color: '#8E9AAF',
interval: Math.max(1, Math.floor(data.labels.length / 7) - 1) // 动态间隔
}
},
yAxis: {
type: 'value',
axisLine: { show: true, lineStyle: { color: '#e2e8f0' } },
splitLine: { lineStyle: { color: '#f1f3f5', type: 'dashed' } },
axisLabel: { color: '#8E9AAF' }
},
series: [
{
name: '已完成',
type: 'line',
data: data.completed,
lineStyle: { width: 3, color: '#06D6A0' },
symbol: 'circle',
symbolSize: 8,
smooth: true,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(6, 214, 160, 0.3)' },
{ offset: 1, color: 'rgba(6, 214, 160, 0.05)' }
])
}
},
{
name: '进行中',
type: 'line',
data: data.inProgress,
lineStyle: { width: 3, color: '#3A86FF' },
symbol: 'circle',
symbolSize: 8,
smooth: true,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(58, 134, 255, 0.3)' },
{ offset: 1, color: 'rgba(58, 134, 255, 0.05)' }
])
}
},
{
name: '未进行',
type: 'line',
data: data.notStarted,
lineStyle: { width: 3, color: '#8E9AAF' },
symbol: 'circle',
symbolSize: 8,
smooth: true,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(142, 154, 175, 0.3)' },
{ offset: 1, color: 'rgba(142, 154, 175, 0.05)' }
])
}
},
{
name: '任务失败',
type: 'line',
data: data.failed,
lineStyle: { width: 3, color: '#FF4D6D' },
symbol: 'circle',
symbolSize: 8,
smooth: true,
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(255, 77, 109, 0.3)' },
{ offset: 1, color: 'rgba(255, 77, 109, 0.05)' }
])
}
}
]
};
trendChart.setOption(option);
};
renderTrendChart(currentTrendData);
// 2. 饼图 - 当天任务状态分布
const pieChart = echarts.init(document.getElementById('pieChart'));
const pieOption = {
tooltip: {
trigger: 'item',
formatter: '{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
right: 10,
top: 'center',
textStyle: { color: '#8E9AAF' }
},
series: [{
type: 'pie',
radius: ['40%', '70%'],
center: ['40%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 8,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
formatter: '{b}: {c}'
},
emphasis: {
label: {
show: true,
fontWeight: 'bold'
}
},
labelLine: { show: false },
data: [
{ value: 420, name: '已完成', itemStyle: { color: '#06D6A0' } },
{ value: 185, name: '进行中', itemStyle: { color: '#3A86FF' } },
{ value: 98, name: '未进行', itemStyle: { color: '#8E9AAF' } },
{ value: 37, name: '任务失败', itemStyle: { color: '#FF4D6D' } }
]
}]
};
pieChart.setOption(pieOption);
// 3. 堆叠柱状图
const stackedBarChart = echarts.init(document.getElementById('stackedBarChart'));
const timeLabels = Array.from({ length: 24 }, (_, i) => `${i}:00`);
// 模拟24小时不同状态的任务数据
const generateDayData = () => {
return {
completed: Array.from({ length: 24 }, (_, i) => {
// 白天任务多,晚上任务少的模式
const base = 30 + Math.sin(i / 24 * Math.PI) * 25;
return Math.round(base + Math.random() * 15);
}),
inProgress: Array.from({ length: 24 }, (_, i) => {
const base = 20 + Math.abs(Math.cos(i / 12 * Math.PI)) * 15;
return Math.round(base + Math.random() * 10);
}),
notStarted: Array.from({ length: 24 }, (_, i) => {
return Math.round(10 + Math.random() * 8);
}),
failed: Array.from({ length: 24 }, (_, i) => {
// 凌晨时段失败率略高
const nightFactor = i < 6 || i > 22 ? 1.5 : 1;
return Math.round((Math.random() * 5 + 2) * nightFactor);
})
};
};
// 为一周的每一天生成数据
const weekData = Array.from({ length: 5 }, () => generateDayData());
const updateStackedBarChart = (dayIndex) => {
const dayData = weekData[dayIndex];
const stackedBarOption = {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: params => {
let total = 0;
params.forEach(item => total += item.value);
return params.map(item =>
`${item.marker} ${item.seriesName}: ${item.value} (${Math.round(item.value / total * 100)}%)`
).join('<br>');
}
},
legend: {
data: ['已完成', '进行中', '未进行', '任务失败'],
bottom: 0,
textStyle: { color: '#8E9AAF' }
},
grid: {
left: '3%',
right: '4%',
bottom: '15%',
top: '5%',
containLabel: true
},
xAxis: {
type: 'category',
data: timeLabels,
axisLabel: {
interval: 1,
rotate: 45,
color: '#8E9AAF'
},
axisLine: { lineStyle: { color: '#e2e8f0' } }
},
yAxis: {
type: 'value',
axisLine: { show: true, lineStyle: { color: '#e2e8f0' } },
splitLine: { lineStyle: { color: '#f1f3f5', type: 'dashed' } },
axisLabel: { color: '#8E9AAF' }
},
series: [
{
name: '已完成',
type: 'bar',
stack: '总量',
emphasis: { focus: 'series' },
data: dayData.completed,
itemStyle: { color: '#06D6A0' },
barWidth: '60%'
},
{
name: '进行中',
type: 'bar',
stack: '总量',
emphasis: { focus: 'series' },
data: dayData.inProgress,
itemStyle: { color: '#3A86FF' },
barWidth: '60%'
},
{
name: '未进行',
type: 'bar',
stack: '总量',
emphasis: { focus: 'series' },
data: dayData.notStarted,
itemStyle: { color: '#8E9AAF' },
barWidth: '60%'
},
{
name: '任务失败',
type: 'bar',
stack: '总量',
emphasis: { focus: 'series' },
data: dayData.failed,
itemStyle: { color: '#FF4D6D' },
barWidth: '60%'
}
]
};
stackedBarChart.setOption(stackedBarOption);
};
// 初始化显示周一的数据
updateStackedBarChart(0);
// 添加日期选择器事件监听
document.getElementById('daySelector').addEventListener('change', function (e) {
updateStackedBarChart(parseInt(e.target.value));
});
// 窗口大小变化时重绘图表
window.addEventListener('resize', function () {
trendChart.resize();
pieChart.resize();
stackedBarChart.resize();
});
});
</script>
</body>
</html>