统计模块中任务分析静态页面
This commit is contained in:
parent
ea7c131298
commit
4dffec016a
633
src/views/service/task.html
Normal file
633
src/views/service/task.html
Normal 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>
|
Loading…
Reference in New Issue
Block a user