1.需求
最近有需求需要实现 <table> 图表与 eharts 柱状图的联动。
完整的效果图如下所示
这里时间轴要实现的效果要基本如下图所示
该时间轴并不是要实现选中单独的某一个月份并查看单月的数据,而是要将当前数据的时间跨度控制在时间轴的跨度之间,在点击后退一个月或前进一个月的时候,对应的图表数据需要同步过滤并更新。
2.分析
echarts 本身是有时间轴的,但是这个自带的 timeline 并不能实现 后退一个月、前进一个月 这样的类似固定时间跨度的数据切换,而我们需要的是一个可以改变时间区间的时间轴,并不是要切换到某个月份。其实 echarts 中的 datazoom 是可以实现这个效果的,但是 datazoom 控制条的样式又不能像 timeline 时间轴那样标识每个月,所以决定自己用 html 控件做一个简单的时间轴,然后每当前进或者后退月份的时候,自己再过滤图表数据并更新图表。
图表数据可以选择一次获取全部(如果你数据量很大还是建议分段下载数据),但是展示在图表上的数据必须在时间轴的可见时间区间之内。
时间轴将页面控制页面上下两个图表,上半部分的窗格图表(类似日历,但这个是月历)可以选择 <table> 来实现,每个td中其实有包含两种类型的事件:图片类型事件和纯文本类型事件,这里要求图片类型事件始终在纯文本事件之上。td 右下角虽然现实月份,但是事件必须是年月都匹配上才可以。可以在拿到事件数据的时候动态生成所有td,然后 append 到 table 的 tr 下,在点击时间轴的向前、向后 arrows 时,可以整体左右移动 table 以符合时间轴当前时间区间。至于下半部分的柱状图,还是选择用 echarts 来实现,时间轴变动时主要需要更新 y轴 类目 category 和 系列 series 数据来更新柱图。
3.时间轴具体实现
html
<!--时间轴容器-->
<div id="timeLineContainer">
<!--时间轴-->
<div id="timeline">
<!--后退一个月-->
<span id="prev_month" class="prev_enabled" title="后退一个月"></span>
<!--横向时间轴日期列表-->
<ul id="dates">
<!--<li>
<span >2019年1月</span>
</li>
<li>
<span >2019年2月</span>
</li>
<li>
<span >2019年3月</span>
</li>
<li>
<span >2019年4月</span>
</li>
<li>
<span >2019年5月</span>
</li>
<li>
<span >2019年6月</span>
</li>
<li>
<span >2019年7月</span>
</li>
<li>
<span >2019年8月</span>
</li>
<li>
<span >2019年9月</span>
</li>
<li>
<span >2019年10月</span>
</li>
<li>
<span >2019年11月</span>
</li>
<li>
<span >2019年12月</span>
</li>-->
</ul>
<!--前进一个月-->
<span id="next_month" class="next_enabled" title="前进一个月"></span>
</div>
</div>
css
/*时间轴相关*/
/*时间轴时间节点*/
.timeLineNode{
cursor: pointer;
}
/*回退一个月*/
#prev_month {
z-index: 5;
cursor: pointer;
left: 0px;
top: 15px;
position: absolute;
display: inline-block;
width: 29px;
height: 29px;
}
/*前进一个月*/
#next_month {
z-index: 5;
cursor: pointer;
top: 15px;
right: 0px;
position: absolute;
display: inline-block;
width: 29px;
height: 29px;
}
/*可以回退时(即除了当前界面上的所有月份,在不可见的左侧,仍有月份)*/
.prev_enabled {
background: url(../img/left-on.png) center no-repeat;
}
/*不可以回退时(即除了当前界面上的所有月份,在不可见的左侧,已无月份)*/
.prev_disabled {
background: url(../img/left-off.png) center no-repeat;
}
/*可以前进时(即除了当前界面上的所有月份,在不可见的右侧,仍有月份)*/
.next_enabled {
disabled: false;
background: url(../img/right-on.png) center no-repeat;
}
/*不可以前进时(即除了当前界面上的所有月份,在不可见的右侧,已无月份)*/
.next_disabled {
disabled: true;
background: url(../img/right-off.png) center no-repeat;
}
/*时间轴容器*/
#timeLineContainer {
margin-top: -8px;
margin-left: 2%;
width: 98%;
height: 65px;
}
/*时间轴*/
#timeline {
width: 100%;
height: 60px;
overflow: hidden;
margin: 0px auto;
position: relative;
/*background: url(../img/over_time_red_point.png) left center repeat-x;*/
}
/*时间轴上的横线*/
#timeline:before {
content: "";
width: 100%;
height: 1px;
background: #bfc7ce;
position: absolute;
top: 30px;
left: 0;
}
/*日期列表*/
#dates {
position: absolute;
left: 0;
top: 0;
height: 60px;
overflow: hidden;
}
/*日期节点*/
#dates li {
list-style: none;
float: left;
width: 152px;
height: 60px;
font-size: 16px;
text-align: center;
background: url(../img/icon_dot_new.png) center no-repeat;
position: relative;
z-index: 3;
}
/*日期文字*/
#dates li span {
padding-top: 35px;
display: inline-block;
width: 152px;
font-size: 16px;
}
js
/**
* 时间轴数据源
*/
var timeLineDataArr = generateTimeLineDataArr();
// 这两个索引用来记录当前显示在页面上的时间轴上的起始月和结束月
var nextIndex = 11;
var preIndex = 0;
/*初始化 向前、向后 的图标*/
$('#prev_month').removeClass("prev_enabled").addClass('prev_disabled');
if(nextIndex == timeLineDataArr.length - 1) {
// 不能再前进了
$('#next_month').removeClass("next_enabled").addClass('next_disabled');
}
$(document).ready(function() {
// 初始化顶部普通文本表格
inflateNormalTableData();
// 设置时间轴数据和点击事件
var stringContent = "";
for(var i = 0; i < timeLineDataArr.length; i++) {
stringContent += "<li class='timeLineNode' title='" + timeLineDataArr[i] + "'>" + "<span>" + timeLineDataArr[i] + "</span> " + "</li>";
}
var $lis = $(stringContent);
var $parent = $('#dates');
$parent.append($lis);
// 后退一个月
$('#prev_month').click(function() {
if(!$("#dates").is(":animated")) {
if(preIndex > 0) {
// 前进按钮置为可用的背景图片
if($("#next_month").prop("className") == "next_disabled") {
$('#next_month').removeClass('next_disabled').addClass("next_enabled");
}
var timeLineItemLength = parseInt($("#dates li").css('width'));
var currentDatesLeft = parseInt($("#dates").css('left'));
$("#dates").animate({
left: (currentDatesLeft + timeLineItemLength)
}, 200);
var topTableItemLength = parseInt($("#table_sjzxjd td").css('width'));
var currentTableLeft = parseInt($("#table_sjzxjd").css('left'));
$("#table_sjzxjd").animate({
left: (topTableItemLength + currentTableLeft)
}, 200);
preIndex--;
if(nextIndex > 0) {
nextIndex--
}
if(preIndex == 0) {
// 不能再后退了
$('#prev_month').removeClass("prev_enabled").addClass('prev_disabled');
}
// 刷新底部柱图
refreshBottomBarChart(timeLineDataArr[preIndex], timeLineDataArr[nextIndex]);
}
}
});
// 向前一个月
$('#next_month').click(function() {
if(!$("#dates").is(":animated")) {
if(nextIndex < timeLineDataArr.length - 1) {
// 后退按钮置为可用的背景图片
if($("#prev_month").prop("className") == "prev_disabled") {
$('#prev_month').removeClass('prev_disabled').addClass("prev_enabled");
}
var timeLineItemLength = parseInt($("#dates li").css('width'));
var currentDatesLeft = parseInt($("#dates").css('left'));
$("#dates").animate({
left: (-timeLineItemLength + currentDatesLeft)
}, 200);
var topTableItemLength = parseInt($("#table_sjzxjd td").css('width'));
var currentTableLeft = parseInt($("#table_sjzxjd").css('left'));
$("#table_sjzxjd").animate({
left: (-topTableItemLength + currentTableLeft)
}, 200);
nextIndex++;
preIndex++;
if(nextIndex == timeLineDataArr.length - 1) {
// 不能再前进了
$('#next_month').removeClass("next_enabled").addClass('next_disabled');
}
// 刷新底部柱图
refreshBottomBarChart(timeLineDataArr[preIndex], timeLineDataArr[nextIndex]);
}
}
});
因为页面默认只可以显示 12 个月份的数据,所以选择使用当前时间轴可见时间的最小和最大时间节点,在时间轴数组中对应数据的索引(preIndex、nextIndex)来控制前进、后退月份,这样做的好处是可以方便的获取当前的可见时间区间的最值,并以此来过滤图表数据。
4.上半部分 table 图表具体实现
html
<!--顶部图表容器-->
<div id="top_chart_container">
<!--顶部图表标题-->
<span id="top_chart_title">实<br/>际<br/>执<br/>行<br/>进<br/>度</span>
<div id="top_table_cantainer">
<!--顶部时间事件表-->
<table id="table_sjzxjd">
<tr id="tr_events">
</tr>
</table>
</div>
</div>
css
/*顶部图表容器*/
#top_chart_container {
overflow: hidden;
margin: 0 auto;
padding: 0;
width: 100%;
height: 300px;
border: solid #e9e9ea 1px;
border-right: none;
}
/*顶部图表左侧标题*/
#top_chart_title {
font-weight: bold;
background-color: #2F92FF;
color: #FFFFFF;
padding-top: 80px;
text-align: center;
width: 2%;
height: 100%;
float: left;
}
/*顶部table容器*/
#top_table_cantainer {
position: relative;
width: 98%;
height: 100%;
overflow: hidden;
}
/*顶部table*/
#table_sjzxjd {
position: absolute;
left: 0;
border-collapse: collapse;
border: none;
table-layout: fixed;
float: left;
margin: 0 auto;
padding: 0;
width: 100%;
height: 100%;
}
/*顶部表格中的单元格 td*/
#table_sjzxjd td {
width: 151px;
position: relative;
border: solid #e9e9ea 1px;
border-top: none;
vertical-align: top;
}
/*顶部某一时间内的图片类型事件列表*/
.img_list {
width: 100%%;
}
/*顶部表格中的图片条目*/
.img_list li {
width: 100%;
height: 70px;
}
/*图片 “完”*/
.img_wan {
height: 28px;
width: 28px;
}
/*cg图片类型条目*/
.img_item_cg {
background: url(../img/top_chart_img_list_item_cg_bg.png) no-repeat center;
}
.img_item_cg span.img_wan {
background: url(../img/text_cai_gou_wan.png) no-repeat center;
}
/*ht图片类型条目*/
.img_item_ht {
background: url(../img/top_chart_img_list_item_ht_bg.png) no-repeat center;
}
/*ht——完*/
.img_item_ht span.img_wan {
background: url(../img/text_he_tong_wan.png) no-repeat center;
}
/*phgg图片类型条目*/
.img_item_phgg {
background: url(../img/top_chart_img_list_item_phgg_bg.png) no-repeat center;
}
/*phgg——完*/
.img_item_phgg span.img_wan {
background: url(../img/text_phgg_wan.png) no-repeat center;
}
/*cggd图片类型条目*/
.img_item_cggd {
background: url(../img/top_chart_img_list_item_cggd_bg.png) no-repeat center;
}
/*cggd——完*/
.img_item_cggd span.img_wan {
background: url(../img/text_cggd_wan.png) no-repeat center;
}
/*tqps*/
.img_item_tqps {
background: url(../img/top_chart_img_list_item_tqps_bg.png) no-repeat center;
}
/*tqps——完*/
.img_item_tqps span.img_wan {
background: url(../img/text_ti_qian_pi_shi_wan.png) no-repeat center;
}
/*设置图片条目的文本weight 和 颜色*/
.img_list li p {
font-weight: 400;
color: #FFFFFF;
}
/*cg 图片上的事件时间*/
.img_list_item p.case_time {
padding-left: 15px;
}
/*cg 图片上的事件类型*/
.img_list_item p.case_type {
padding-top: 12px;
padding-left: 15px;
}
/*上半部分图表,”cg“、”ht“、”tqps等“*/
.img_list_item {
position: relative;
}
/*图片:cg-完*/
.img_list_item span.img_wan {
position: absolute;
right: 12px;
bottom: 12px;
}
/*顶部表格中的文本列表*/
.text_list {
width: 100%;
}
/*文本条目左侧的 圆点 图片*/
.dot_in_text_item {
display: inline-block;
height: 12px;
width: 12px;
background: url(../img/icon_dot_new.png) no-repeat center;
}
/*顶部表格中的文本列表条目*/
.text_list_item {
width: 100%;
font-weight: 600;
color: #1C243199;
}
/*文本条目内容*/
.text_list_item span {
text-align: center;
margin-left: 10px;
}
/*文本条目上的小圆点*/
.text_list_item img {
margin-left: 10px;
}
/*顶部表格中标识月份的文本区域*/
.month_num_text_area {
position: absolute;
bottom: 1px;
right: 10px;
color: #becbcd;
font-weight: bold;
}
.moth_number {
font-size: 30px;
}
/**
*顶部table的事件窗格td
*/
.month_td{
cursor: pointer;
}
js
/*顶部列表的数据源,和底部图表的数据源应分开,无论每个月份是否有事件,月份仍要显示,时间坐标轴也是如此*/
var topChartData = {
events: [{
/*事件时间*/
eventTime: '2019-01-01',
/*事件类型:
*因为现在有图片和文本两大类事件,
* 0:文本类型事件;
* 1:图片类型事件
*/
eventType: 1,
/*图片类型事件中的具体事件类型,包括
* 0:cgfssp
* 1:ht
* 2:tqps
* 3:phgg
* 4:cggd
*
* 文本类型事件可不传该字段
* */
specificEventType: 0,
/*事件标题
* 在事件为文本类型时,该值必填,
* 在事件为图片类型时,若传入空字符串,那么将自动按类型填充填充默认标题
*/
eventTitle: '采购方式审批',
},
{
eventTime: '2019-01-10',
eventType: 0,
eventTitle: 'zb',
},
{
eventTime: '2019-02-28',
eventType: 1,
specificEventType: 1,
eventTitle: 'ht',
},
{
eventTime: '2019-02-15',
eventType: 0,
eventTitle: 'zb',
},
]
};
/**
* 加载顶部的图表数据
*/
function inflateTopTableChart() {
// 获取事件数组
var events = topChartData.events;
for(var i = 0; i < timeLineDataArr.length; i++) {
// 获取时间轴节点的年月
var timeLineNodeItem = timeLineDataArr[i];
var timeLineNodeItemDate = convertHanziDateToJSDate(timeLineNodeItem);
var timeLineNodeMonth = timeLineNodeItemDate.getMonth() + 1;
var timeLineNodeYear = timeLineNodeItemDate.getFullYear();
// 将时间轴上的时间作为对应td的id,生成td时先查看该td是否已存在,不存在先生成
if($('#' + timeLineNodeItem).length == 0) {
// 需要先生成一个 td
$('#tr_events').append('<td id="' +
timeLineNodeItem +
'" class="month_td" title="' +
timeLineNodeYear + "年" + timeLineNodeMonth + "月事件" +
'"></td>');
}
// 顶部 table 中的 td右下角的月份
$('#' + timeLineNodeItem).append('<div class="month_num_text_area"><span class="moth_number">' +
timeLineNodeMonth +
'</span><span>月</span></div>');
// 获取事件的年月并对比
for(var j = 0; j < events.length; j++) {
var event = events[j];
var eventTimeStr = event.eventTime;
var eventTime = new Date(eventTimeStr);
var eventYear = eventTime.getFullYear();
var eventMonth = eventTime.getMonth() + 1;
var eventDay = eventTime.getDate();
// 同年同月
if(timeLineNodeYear == eventYear && timeLineNodeMonth == eventMonth) {
// 保证图片类型消息的的ul在上,文本类型的在下
// 先看该td下是否已存在对应类型的 ul
if($('#' + timeLineNodeItem + ' ul.img_list').length == 0) {
//不存在的话,需要先生成一个,
$('#' + timeLineNodeItem).append('<ul class="img_list"></ul>');
}
// 文本类型事件 ul
// 先看该td下是否已存在对应类型的额 ul
if($('#' + timeLineNodeItem + ' ul.text_list').length == 0) {
//不存在的话,需要先生成一个,
$('#' + timeLineNodeItem).append('<ul class="text_list"></ul>');
}
if(event.eventType == 1) {
// 图片类型事件
//在图片类型 ul 下拼接 图片类型消息li
$('#' + timeLineNodeItem + ' ul.img_list').append('<li title="' +
event.eventTitle +
'" class="img_list_item ' +
getTopTableImgTypeClassName(event.specificEventType) +
'"><p class="case_type">' +
event.eventTitle +
'</p><p class="case_time">' +
eventYear + '.' + eventMonth + '.' + eventDay +
'</p><span class="img_wan"></span></li>');
}
if(event.eventType == 0) {
// 文本类型事件
//在文本类型 ul 下拼接 文本类型
$('#' + timeLineNodeItem + ' ul.text_list').append('<li class="text_list_item" title="' +
event.eventTitle +
'"><span class="dot_in_text_item"></span><span>' +
eventMonth + '.' + eventDay + ' ' + event.eventTitle + '</span></li>');
}
}
}
}
}
顶部 table 与 时间轴的联动代码可在 时间轴的 js 中找到。
5.下半部分 echarts 柱图的具体实现
这里需要注意的一点是,echarts 在频繁的 给 chart setOption( ) 时,很有可能会残留数据,造成切换数据后显示混乱。
跟踪代码排除是数据传错的情况后,可以选择不通过 setOption() 来刷新柱图数据了,而是在重新 echarts.init() 后,构建好新数据(通过时间轴的最值时间过滤好的 series)setOption()之前,使用 echart.clear() 方法来清除缓存,最后再调用echart.setOption(newOption) 来初始化图表数据。
html
<!--底部图表容器-->
<div id="bottom_chart_container">
<span id="bottom_chart_title">计<br>划<br>时<br>间</span>
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div id="chart_bottom"></div>
</div>
css
/*底部图表*/
#chart_bottom {
float: left;
margin: 0 auto;
padding: 0;
width: 98%;
height: 100%;
}
#sjzxjd {
background-color: '#2f92ff';
}
/*底部图表标题*/
#bottom_chart_title {
font-weight: bold;
color: #FFFFFF;
background-color: #a384e5;
padding-top: 100px;
text-align: center;
width: 2%;
height: 100%;
float: left;
}
/*盛放底部图表的容器*/
#bottom_chart_container {
border: solid #e9e9ea 1px;
overflow: hidden;
height: 300px;
width: 100%;
}
js
// 传底部柱图阶段数据
var stages = [{
"stageName": 'ghqd阶段',
"stageTimes": ['2019-01-01', '2019-02-28']
},
{
"stageName": 'ghdyzb阶段',
"stageTimes": ['2019-02-28', '2019-05-01']
},
{
"stageName": 'ghbz阶段',
"stageTimes": ['2019-05-01', '2019-10-31']
},
{
"stageName": 'ghsw阶段',
"stageTimes": ['2019-10-31', '2020-03-31']
}
];
/**
* 阶段开始时间数组
*/
var stagesBeginTimeArr = new Array();
var stagesBeginTimeMillisArr = new Array();
/**
* 阶段结束时间数组
*/
var stagesEndTimeArr = new Array();
var stagesEndTimeMillisArr = new Array();
/**
* 阶段名称
*/
var stagesNameArr = new Array();
/**
* 填充柱图数据
* @param {Object} inputStages 输入阶段
*/
function fillBarChartData(inputStages) {
stagesNameArr = [];
stagesBeginTimeArr = [];
stagesBeginTimeMillisArr = [];
stagesEndTimeArr = [];
stagesEndTimeMillisArr = [];
for(var i = 0; i < inputStages.length; i++) {
var stage = inputStages[i];
stagesNameArr[i] = stage.stageName;
stagesBeginTimeArr[i] = stage.stageTimes[0];
stagesBeginTimeMillisArr[i] = getTimeMilliseconds(stage.stageTimes[0]);
stagesEndTimeArr[i] = stage.stageTimes[1];
stagesEndTimeMillisArr[i] = getTimeMilliseconds(stage.stageTimes[1]);
}
}
/**
* 获取随机16进制颜色
*/
function getRandomColor() {
var color = '#';
for(var i = 0; i < 6; i++) {
color += (Math.random() * 16 | 0).toString(16);
}
if(color.toUpperCase() != '#FFFFF') {
console.log(color);
return color;
} else {
return getRandomColor();
}
}
/**
* 获取柱图单独系列的数据
* @param {Object} inputStages 输入阶段
* @param {Object} seriesRank 阶段在阶段数组中的索引
*/
function getSingleSeriesData(inputStages, seriesRank) {
var dataArr = [];
for(var j = 0; j < inputStages.length; j++) {
if(seriesRank == j) {
dataArr[j] = getTimeMilliseconds(inputStages[j].stageTimes[1]);
} else {
dataArr[j] = "-";
}
}
return dataArr;
}
/**
* 生成系列数据
* @param {Object} inputStages 输入阶段
*/
var generatedSeries = function(inputStages) {
var series = new Array();
// 占位柱图1
series[0] = {
name: '开始时间',
type: 'bar',
stack: '时间',
itemStyle: {
normal: {
barBorderColor: 'rgba(0,0,0,0)',
color: 'rgba(0,0,0,0)'
},
emphasis: {
barBorderColor: 'rgba(0,0,0,0)',
color: 'rgba(0,0,0,0)'
}
},
label: {
normal: {
formatter: function(params) {
var stageName = "";
if(params != null) {
stageName = params.name;
}
if(stageName == stagesNameArr[0]) {
return getSeriesDateStr(params.value);
}
return "";
},
show: true,
position: 'insideRight',
fontSize: 16,
color: '#1c2431',
fontFamily: 'Microsoft YaHei',
offset: [65, -20],
}
},
data: stagesBeginTimeMillisArr,
};
// 占位柱图2
series[1] = {
name: '开始时间1',
type: 'bar',
stack: '时间',
itemStyle: {
normal: {
barBorderColor: 'rgba(0,0,0,0)',
color: 'rgba(0,0,0,0)'
},
emphasis: {
barBorderColor: 'rgba(0,0,0,0)',
color: 'rgba(0,0,0,0)'
}
},
label: {
normal: {
formatter: function(params) {
var stageName = "";
if(params != null) {
stageName = params.name;
}
return stageName;
},
show: true,
position: 'Right',
fontSize: 16,
color: '#1c2431',
fontFamily: 'Microsoft YaHei',
offset: [0, 25],
}
},
data: stagesBeginTimeMillisArr,
};
// 循环填充数据
for(var i = 0; i < inputStages.length; i++) {
series[i + 2] = {
name: stagesNameArr[i],
type: 'bar',
stack: '时间',
itemStyle: {
normal: {
color: getRandomColor(),
}
},
label: {
normal: {
formatter: function(params) {
return getSeriesDateStr(params.value);
},
show: true,
fontSize: 16,
color: '#1c2431',
position: 'insideRight',
fontFamily: 'Microsoft YaHei',
offset: [5, -20],
}
},
data: getSingleSeriesData(inputStages, i),
/**
* 柱状图宽度
*/
barWidth: 20,
};
}
return series;
};
/**
* 过滤底部柱图阶段数据
* @param {Object} currentBarMinDate
* @param {Object} currentBarMaxDate
*/
function filterStagesAccordingTimeLineMinAndMaxVisibleTimeNode(currentBarMinDate, currentBarMaxDate) {
// 过滤底部柱图的数据
var barCharMinTime = convertHanziDateToJSDate(currentBarMinDate);
var barCharMaxTime = convertHanziDateToJSDate(currentBarMaxDate);
// // 当前时间轴上显示的最大、最小年月
var barCharMinYear = barCharMinTime.getFullYear();
var barCharMinMonth = barCharMinTime.getMonth() + 1;
var barCharMaxYear = barCharMaxTime.getFullYear();
var barCharMaxMonth = barCharMaxTime.getMonth() + 1;
var newStages = new Array();
for(var i = 0; i < stages.length; i++) {
var stage = stages[i];
// 阶段开始时间年月
var stageBeginTimeStr = stage.stageTimes[0];
var stageEndTimeStr = stage.stageTimes[1];
var stageBeginTime = new Date(stageBeginTimeStr);
var stageEndTime = new Date(stageEndTimeStr);
// 阶段结束时间年月
var stageBeginYear = stageBeginTime.getFullYear();
var stageBeginMonth = stageBeginTime.getMonth() + 1;
var stageEndYear = stageEndTime.getFullYear();
var stageEndMonth = stageEndTime.getMonth() + 1;
if(stageBeginTime.getTime() >= barCharMinTime.getTime() &&
stageEndTime.getTime() <= barCharMaxTime.getTime() ||
(stageBeginYear == barCharMinYear && stageBeginMonth >= barCharMinMonth &&
stageEndYear == barCharMaxYear && stageEndMonth <= barCharMaxMonth) ||
(stageBeginYear > barCharMinYear && stageEndYear < barCharMaxYear)) {
newStages.push(stage);
}
}
console.log('newStages-->' + newStages);
return newStages;
}
/**
* 当后退或前进时刷新底部的柱图
* @param {Object} currentBarMinDate 当前时间轴可见最小年月
* @param {Object} currentBarMaxDate 当前时间轴可见最大年月
*/
function refreshBottomBarChart(currentBarMinDate, currentBarMaxDate) {
// $("#chart_bottom").html(' ');
var newStages = filterStagesAccordingTimeLineMinAndMaxVisibleTimeNode(currentBarMinDate, currentBarMaxDate);
// 重新填充柱图数据
fillBarChartData(newStages);
// 初始化底部柱图图表
var myBottomChart = echarts.init(document.getElementById('chart_bottom'));
// 构建图表配置项
bottomChartOption = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
/**
* 也可以使用 formatter: '{b0}:<br />{a0}: {c0}<br />{a1}: {c1}<br />{a2}: {c2}',
* 但是这样当鼠标指向纵坐标的三个阶段中的某一个时,即使该阶段 没有按时完成,或者 没有超时,
* 也会显示 按时 或 超时 的 tooltip
*/
formatter: function(params) {
console.log("params111" + JSON.stringify(params));
var info = params[0].axisValue + ":<br />";
info += params[0].seriesName + ":" + getSeriesDateStr(params[0].value) + "<br />";
for(var i = 0; i < params.length; i++) {
if(params[i].seriesName == params[0].axisValue) {
info += "结束时间:" + getSeriesDateStr(params[i].value);
}
}
return info;
},
},
/**
* 右上角工具栏
*/
toolbox: {
right: '3%',
show: false,
feature: {
saveAsImage: {
show: true
}
}
},
/**
* 直角坐标系内绘图网格
*/
grid: {
top: '0%',
left: '0%',
right: '3%',
bottom: '0%',
/**
* grid 区域是否包含坐标轴的刻度标签。
*/
containLabel: false,
},
/**
* 横坐标
*/
xAxis: {
/**
* 时间轴,适用于连续的时序数据,与数值轴相比时间轴带有时间的格式化,
* 在刻度计算上也有所不同,例如会根据跨度的范围来决定使用月,星期,日还是小时范围的刻度。
*/
type: 'time',
/**
* value 是一个包含 min 和 max 的对象,分别表示数据的最大最小值,这个函数应该返回坐标轴的最大值。
*
* 坐标轴刻度最小值。
*/
min: getTimeMilliseconds(getProperTimeAxisBeginAndEndTime()[0]),
/**
* value 是一个包含 min 和 max 的对象,分别表示数据的最大最小值,这个函数应该返回坐标轴的最大值。
*
* 坐标轴刻度最大值。
*/
max: getTimeMilliseconds(getProperTimeAxisBeginAndEndTime()[1]),
/**
* 设置坐标轴分割间隔
*/
interval: getProperTimeAxisInterval(),
axisLine: {
lineStyle: {
color: '#BDC8CD',
width: 1,
},
},
axisTick: {
show: false,
},
/**
* 坐标轴刻度标签的相关设置。
*/
axisLabel: {
show: false,
showMinLabel: true,
showMaxLabel: true,
margin: 12,
fontSize: 16,
color: '#1c2431',
formatter: function(value, index) {
var date = new Date(value);
// var time = date.getFullYear() + "." + (date.getMonth() + 1) + "." + date.getDate();
var time = date.getFullYear();
if(xAxisLabelUnit.month) {
time += "." + (date.getMonth() + 1);
}
if(xAxisLabelUnit.day) {
time += "." + (date.getMonth() + 1) + '.' + date.getDate();
}
return time;
},
},
/**
* 坐标轴刻度分割线
*/
splitLine: {
show: false,
},
},
/**
* 纵坐标
*/
yAxis: {
type: 'category',
data: stagesNameArr,
// show: false,
axisTick: {
show: false,
},
axisLine: {
lineStyle: {
color: '#e9e9ea',
width: 1,
},
},
axisLabel: {
show: false,
fontWeight: 'bold',
fontSize: 16,
color: '#1c2431',
fontFamily: 'Microsoft YaHei',
},
splitLine: {
show: false,
lineStyle: {
color: '#eaeae9',
width: 1,
},
},
},
series: generatedSeries(newStages),
};
// 清除一下 echart 的缓存数据
myBottomChart.clear();
// 将构建好的配置项传入echarts
myBottomChart.setOption(bottomChartOption);
}
6.完整js(较长)
最后贴上完整js
// 传底部柱图阶段数据
var stages = [{
"stageName": 'ghqd阶段',
"stageTimes": ['2019-01-01', '2019-02-28']
},
{
"stageName": 'ghdyzb阶段',
"stageTimes": ['2019-02-28', '2019-05-01']
},
{
"stageName": 'ghbz阶段',
"stageTimes": ['2019-05-01', '2019-10-31']
},
{
"stageName": 'ghsw阶段',
"stageTimes": ['2019-10-31', '2020-03-31']
}
];
/**
* 横坐标轴时间刻度可选值
* 这里 month和year 没有考虑平闰年之分
*/
var timeInterval = {
day: 3600 * 1000 * 24,
month: 3600 * 1000 * 24 * 31,
year: 3600 * 1000 * 24 * 31 * 12,
};
/**
* 阶段开始时间数组
*/
var stagesBeginTimeArr = new Array();
var stagesBeginTimeMillisArr = new Array();
/**
* 阶段结束时间数组
*/
var stagesEndTimeArr = new Array();
var stagesEndTimeMillisArr = new Array();
/**
* 阶段名称
*/
var stagesNameArr = new Array();
/**
* 填充柱图数据
* @param {Object} inputStages 输入阶段
*/
function fillBarChartData(inputStages) {
stagesNameArr = [];
stagesBeginTimeArr = [];
stagesBeginTimeMillisArr = [];
stagesEndTimeArr = [];
stagesEndTimeMillisArr = [];
for(var i = 0; i < inputStages.length; i++) {
var stage = inputStages[i];
stagesNameArr[i] = stage.stageName;
stagesBeginTimeArr[i] = stage.stageTimes[0];
stagesBeginTimeMillisArr[i] = getTimeMilliseconds(stage.stageTimes[0]);
stagesEndTimeArr[i] = stage.stageTimes[1];
stagesEndTimeMillisArr[i] = getTimeMilliseconds(stage.stageTimes[1]);
}
}
/**
* 时间对象转日期字符串 yyyy.MM.dd
* @param {Object} timeObject 毫秒值或时间字符串
*/
function getSeriesDateStr(timeObject) {
if(timeObject == "-") {
return timeObject;
}
var date = new Date(timeObject);
var dateStr = '';
dateStr += date.getFullYear() + '.';
dateStr += date.getMonth() + 1 + '.';
dateStr += date.getDate();
return dateStr;
};
/**
* 获取随机16进制颜色
*/
function getRandomColor() {
var color = '#';
for(var i = 0; i < 6; i++) {
color += (Math.random() * 16 | 0).toString(16);
}
if(color.toUpperCase() != '#FFFFF') {
console.log(color);
return color;
} else {
return getRandomColor();
}
}
/**
* 获取柱图单独系列的数据
* @param {Object} inputStages 输入阶段
* @param {Object} seriesRank 阶段在阶段数组中的索引
*/
function getSingleSeriesData(inputStages, seriesRank) {
var dataArr = [];
for(var j = 0; j < inputStages.length; j++) {
if(seriesRank == j) {
dataArr[j] = getTimeMilliseconds(inputStages[j].stageTimes[1]);
} else {
dataArr[j] = "-";
}
}
console.log("dataArr--> " + dataArr);
console.log("dataArr.length--> " + dataArr.length);
return dataArr;
}
/**
* 生成系列数据
* @param {Object} inputStages 输入阶段
*/
var generatedSeries = function(inputStages) {
var series = new Array();
// 占位白色柱图
series[0] = {
name: '开始时间',
type: 'bar',
stack: '时间',
itemStyle: {
normal: {
barBorderColor: 'rgba(0,0,0,0)',
color: 'rgba(0,0,0,0)'
},
emphasis: {
barBorderColor: 'rgba(0,0,0,0)',
color: 'rgba(0,0,0,0)'
}
},
label: {
normal: {
formatter: function(params) {
// console.log("占位:" + JSON.stringify(params));
var stageName = "";
if(params != null) {
stageName = params.name;
}
if(stageName == stagesNameArr[0]) {
return getSeriesDateStr(params.value);
}
return "";
},
show: true,
position: 'insideRight',
fontSize: 16,
color: '#1c2431',
fontFamily: 'Microsoft YaHei',
offset: [65, -20],
}
},
data: stagesBeginTimeMillisArr,
};
// 占位白色柱图
series[1] = {
name: '开始时间1',
type: 'bar',
stack: '时间',
itemStyle: {
normal: {
// barBorderColor: 'rgba(0,0,0,0)',
// color: 'rgba(0,0,0,0)'
color: '#4BB429'
},
emphasis: {
barBorderColor: 'rgba(0,0,0,0)',
color: 'rgba(0,0,0,0)'
}
},
label: {
normal: {
formatter: function(params) {
// console.log("占位:" + JSON.stringify(params));
var stageName = "";
if(params != null) {
stageName = params.name;
}
return stageName;
},
show: true,
position: 'Right',
fontSize: 16,
color: '#1c2431',
fontFamily: 'Microsoft YaHei',
offset: [0, 25],
}
},
data: stagesBeginTimeMillisArr,
};
// 循环填充数据
for(var i = 0; i < inputStages.length; i++) {
series[i + 2] = {
name: stagesNameArr[i],
type: 'bar',
stack: '时间',
itemStyle: {
normal: {
color: getRandomColor(),
}
},
label: {
normal: {
formatter: function(params) {
return getSeriesDateStr(params.value);
},
show: true,
fontSize: 16,
color: '#1c2431',
position: 'insideRight',
fontFamily: 'Microsoft YaHei',
offset: [5, -20],
}
},
data: getSingleSeriesData(inputStages, i),
/**
* 柱状图宽度
*/
barWidth: 20,
};
}
return series;
};
/**
* 时间坐标轴标签单位应该精确到哪一位
*/
var xAxisLabelUnit = {
year: false,
month: false,
day: false
}
/**
* 获取横轴坐标数据源,这里横坐标只显示年月
* 获取的是当前柱图横坐标轴上,对应时间轴上可见时间的数组
* 最小值取传入数据最小的时间再减小一个月
* 最大值取传入数据最小的时间再增加一个月
*/
function getXAxisData() {
var arr = new Array();
arr = arr.concat(stagesBeginTimeArr)
.concat(stagesEndTimeArr)
.filter(function(item) {
return item != "-";
}).sort();
console.log(arr);
return arr;
}
/**
* 获取所有阶段的时间数组,这是过滤之前的原始数据
*/
function getTotalStagesTimeArr() {
var arr = new Array();
for(var i = 0; i < stages.length; i++) {
arr.push(stages[i].stageTimes[0]);
arr.push(stages[i].stageTimes[1]);
}
return arr.filter(function(item) {
return item != "-";
}).sort();
}
/**
*获取时间坐标轴的起始和结束值
*/
function getProperTimeAxisBeginAndEndTime() {
var xAxis = getXAxisData();
var begin = xAxis[0];
var end = xAxis[xAxis.length - 1];
var beginDate = new Date(begin);
var endDate = new Date(end);
if(xAxisLabelUnit.month) {
// if(beginDate.getDate()<15){
// beginDate.setMonth(beginDate.getMonth()-1);
// }
beginDate.setDate(1);
endDate.setMonth(endDate.getMonth() + 1);
endDate.setDate(1);
} else {
var daysCount = getProperTimeAxisInterval() / timeInterval.day;
console.log("daysCount " + daysCount);
beginDate.setDate(beginDate.getDate() - daysCount);
endDate.setDate(endDate.getDate() + daysCount);
}
var beArr = [formatDateToStr(beginDate), formatDateToStr(endDate)];
console.log("beArr " + beArr);
return beArr;
}
/**
* 获取格式化的日期 YYYY-MM-dd
*/
function formatDateToStr(date) {
var inputMonth = date.getMonth();
var inputDate = date.getDate();
var result = date.getFullYear() +
"-" + (inputMonth >= 9 ? inputMonth + 1 : "0" + (inputMonth + 1)) +
"-" + (inputDate >= 9 ? inputDate : "0" + (inputDate));
return result;
}
/**
* 根据时间字符串获取对应的毫秒值
* @param {Object} timeStr 时间字符串
*/
function getTimeMilliseconds(timeStr) {
return(new Date(timeStr)).getTime();
}
/**
* 获取合适的横坐标时间刻度间隔
*/
function getProperTimeAxisInterval() {
xAxisLabelUnit.year = false;
xAxisLabelUnit.month = false;
xAxisLabelUnit.day = false;
var timeDataArray = getXAxisData();
var begin = getTimeMilliseconds(timeDataArray[timeDataArray.length - 1]);
console.log("begin " + begin);
var periodMillis = getTimeMilliseconds(timeDataArray[timeDataArray.length - 1]) - getTimeMilliseconds(timeDataArray[0]);
console.log("periodMillis " + periodMillis);
var years = periodMillis / timeInterval.year;
console.log("years " + years);
var months = periodMillis / timeInterval.month;
console.log("months " + months);
var days = periodMillis / timeInterval.day;
console.log("days " + days);
if(months <= 1) {
xAxisLabelUnit.day = true;
return timeInterval.day * 2;
} else if(months <= 16) {
xAxisLabelUnit.month = true;
return timeInterval.month;
} else if(months <= 24) {
xAxisLabelUnit.month = true;
return timeInterval.month * 2;
} else if(years <= 16) {
xAxisLabelUnit.year = true;
return timeInterval.year;
}
}
/*顶部列表的数据源,和底部图表的数据源应分开,无论每个月份是否有事件,月份仍要显示,时间坐标轴也是如此*/
var topChartData = {
events: [{
/*事件时间*/
eventTime: '2019-01-01',
/*事件类型:
*因为现在有图片和文本两大类事件,
* 0:文本类型事件;
* 1:图片类型事件
*/
eventType: 1,
/*图片类型事件中的具体事件类型,包括
* 0:cgfssp
* 1:ht
* 2:tqps
* 3:phgg
* 4:cggd
*
* 文本类型事件可不传该字段
* */
specificEventType: 0,
/*事件标题
* 在事件为文本类型时,该值必填,
* 在事件为图片类型时,若传入空字符串,那么将自动按类型填充填充默认标题
*/
eventTitle: 'cgfssp',
},
{
eventTime: '2019-01-10',
eventType: 0,
eventTitle: 'zb',
},
{
eventTime: '2019-02-28',
eventType: 1,
specificEventType: 1,
eventTitle: 'ht',
},
{
eventTime: '2019-02-15',
eventType: 0,
eventTitle: 'zb',
},
]
};
/**
* 获取 顶部图表月份数组
*/
function getTopCharMonthsTimeArr() {
var arr = new Array;
for(var i = 0; i < topChartData.events.length; i++) {
arr[i] = topChartData.events[i].eventTime;
}
arr = arr.sort();
console.log('topChartMonthsArr:' + arr);
return arr;
}
/**
* 获取顶部table中的图片类型消息的名称文本描述
*
* 0:cgffsp
* 1:ht
* 2:tqps
* 3:phgg
* 4:cggd
*
* @param {Object} specificEventType 图片类型事件中的具体事件类型
*/
function getTopTableImgTypeClassName(specificEventType) {
switch(specificEventType) {
case 0:
return 'img_item_cg';
case 1:
return 'img_item_ht';
case 2:
return 'img_item_tqps';
case 3:
return 'img_item_phgg';
case 4:
return 'img_item_cggd';
default:
return '';
}
}
/**
* 加载顶部的图表数据
*/
function inflateTopTableChart() {
// 获取事件数组
var events = topChartData.events;
for(var i = 0; i < timeLineDataArr.length; i++) {
// 获取时间轴节点的年月
var timeLineNodeItem = timeLineDataArr[i];
var timeLineNodeItemDate = convertHanziDateToJSDate(timeLineNodeItem);
var timeLineNodeMonth = timeLineNodeItemDate.getMonth() + 1;
var timeLineNodeYear = timeLineNodeItemDate.getFullYear();
// 将时间轴上的时间作为对应td的id,生成td时先查看该td是否已存在,不存在先生成
if($('#' + timeLineNodeItem).length == 0) {
// 需要先生成一个 td
$('#tr_events').append('<td id="' +
timeLineNodeItem +
'" class="month_td" title="' +
timeLineNodeYear + "年" + timeLineNodeMonth + "月事件" +
'"></td>');
}
// 顶部 table 中的 td右下角的月份
$('#' + timeLineNodeItem).append('<div class="month_num_text_area"><span class="moth_number">' +
timeLineNodeMonth +
'</span><span>月</span></div>');
// 获取事件的年月并对比
for(var j = 0; j < events.length; j++) {
var event = events[j];
var eventTimeStr = event.eventTime;
var eventTime = new Date(eventTimeStr);
var eventYear = eventTime.getFullYear();
var eventMonth = eventTime.getMonth() + 1;
var eventDay = eventTime.getDate();
// 同年同月
if(timeLineNodeYear == eventYear && timeLineNodeMonth == eventMonth) {
// 保证图片类型消息的的ul在上,文本类型的在下
// 先看该td下是否已存在对应类型的 ul
if($('#' + timeLineNodeItem + ' ul.img_list').length == 0) {
//不存在的话,需要先生成一个,
$('#' + timeLineNodeItem).append('<ul class="img_list"></ul>');
}
// 文本类型事件 ul
// 先看该td下是否已存在对应类型的额 ul
if($('#' + timeLineNodeItem + ' ul.text_list').length == 0) {
//不存在的话,需要先生成一个,
$('#' + timeLineNodeItem).append('<ul class="text_list"></ul>');
}
if(event.eventType == 1) {
// 图片类型事件
//在图片类型 ul 下拼接 图片类型消息li
$('#' + timeLineNodeItem + ' ul.img_list').append('<li title="' +
event.eventTitle +
'" class="img_list_item ' +
getTopTableImgTypeClassName(event.specificEventType) +
'"><p class="case_type">' +
event.eventTitle +
'</p><p class="case_time">' +
eventYear + '.' + eventMonth + '.' + eventDay +
'</p><span class="img_wan"></span></li>');
}
if(event.eventType == 0) {
// 文本类型事件
//在文本类型 ul 下拼接 文本类型
$('#' + timeLineNodeItem + ' ul.text_list').append('<li class="text_list_item" title="' +
event.eventTitle +
'"><span class="dot_in_text_item"></span><span>' +
eventMonth + '.' + eventDay + ' ' + event.eventTitle + '</span></li>');
}
}
}
}
}
/**
* 年月日中文格式日期转换成Date
* @param {Object} timeStr 日期字符串
*/
function convertHanziDateToJSDate(timeStr) {
return new Date(Date.parse(timeStr.replace('年', '-').replace('月', '-').replace('日', '')));
}
/**
* 获取 顶部图表月份数组
*/
var topChartMonthsArr = getTopCharMonthsTimeArr();
/**
* 获取指定时间的后十二个月份
* @param {Object} timeStr
*/
function generateTimeLineDataArr() {
// 底部所有阶段月份数组
var bottomCharMonthsArr = getTotalStagesTimeArr();
var arr = new Array();
arr = arr.concat(topChartMonthsArr)
.concat(bottomCharMonthsArr)
.filter(function(item) {
return item != "-";
}).sort();
console.log(arr);
// 最小日期
var minDate = new Date(arr[0]);
// 最大日期
var maxDate = new Date(arr[arr.length - 1]);
// 循环填充时间轴数据应该到多少月份
var loopEnd = 0;
var diffyear = maxDate.getFullYear() - minDate.getFullYear();
console.log("diffyear:" + diffyear);
var diffmonth = diffyear * 12 + maxDate.getMonth() - minDate.getMonth();
console.log("diffmonth->" + diffmonth);
if(diffmonth <= 11) {
loopEnd = 11;
} else {
loopEnd = diffmonth;
}
var result = [];
result.push(minDate.getFullYear() + "年" + (minDate.getMonth() + 1) + "月");
for(var i = 0; i < loopEnd; i++) {
minDate.setMonth(minDate.getMonth() + 1); //每次循环一次 月份值+1
var m = minDate.getMonth() + 1;
result.push(minDate.getFullYear() + "年" + (m) + "月");
}
console.log("result-->" + result);
return result;
}
/**
* 时间轴数据源
*/
var timeLineDataArr = generateTimeLineDataArr();
// 这两个索引用来记录当前显示在页面上的时间轴上的起始月和结束月
var nextIndex = 11;
var preIndex = 0;
/*初始化 向前、向后 的图标*/
$('#prev_month').removeClass("prev_enabled").addClass('prev_disabled');
if(nextIndex == timeLineDataArr.length - 1) {
// 不能再前进了
$('#next_month').removeClass("next_enabled").addClass('next_disabled');
}
$(document).ready(function() {
// 初始化顶部普通文本表格
inflateNormalTableData();
// 设置时间轴数据和点击事件
var stringContent = "";
for(var i = 0; i < timeLineDataArr.length; i++) {
stringContent += "<li class='timeLineNode' title='" + timeLineDataArr[i] + "'>" + "<span>" + timeLineDataArr[i] + "</span> " + "</li>";
}
var $lis = $(stringContent);
var $parent = $('#dates');
$parent.append($lis);
// 后退一个月
$('#prev_month').click(function() {
if(!$("#dates").is(":animated")) {
if(preIndex > 0) {
// 前进按钮置为可用的背景图片
if($("#next_month").prop("className") == "next_disabled") {
$('#next_month').removeClass('next_disabled').addClass("next_enabled");
}
var timeLineItemLength = parseInt($("#dates li").css('width'));
var currentDatesLeft = parseInt($("#dates").css('left'));
$("#dates").animate({
left: (currentDatesLeft + timeLineItemLength)
}, 200);
var topTableItemLength = parseInt($("#table_sjzxjd td").css('width'));
var currentTableLeft = parseInt($("#table_sjzxjd").css('left'));
$("#table_sjzxjd").animate({
left: (topTableItemLength + currentTableLeft)
}, 200);
preIndex--;
if(nextIndex > 0) {
nextIndex--
}
if(preIndex == 0) {
// 不能再后退了
$('#prev_month').removeClass("prev_enabled").addClass('prev_disabled');
}
// 刷新底部柱图
refreshBottomBarChart(timeLineDataArr[preIndex], timeLineDataArr[nextIndex]);
}
}
});
// 向前一个月
$('#next_month').click(function() {
if(!$("#dates").is(":animated")) {
if(nextIndex < timeLineDataArr.length - 1) {
// 后退按钮置为可用的背景图片
if($("#prev_month").prop("className") == "prev_disabled") {
$('#prev_month').removeClass('prev_disabled').addClass("prev_enabled");
}
var timeLineItemLength = parseInt($("#dates li").css('width'));
var currentDatesLeft = parseInt($("#dates").css('left'));
$("#dates").animate({
left: (-timeLineItemLength + currentDatesLeft)
}, 200);
var topTableItemLength = parseInt($("#table_sjzxjd td").css('width'));
var currentTableLeft = parseInt($("#table_sjzxjd").css('left'));
$("#table_sjzxjd").animate({
left: (-topTableItemLength + currentTableLeft)
}, 200);
nextIndex++;
preIndex++;
if(nextIndex == timeLineDataArr.length - 1) {
// 不能再前进了
$('#next_month').removeClass("next_enabled").addClass('next_disabled');
}
// 刷新底部柱图
refreshBottomBarChart(timeLineDataArr[preIndex], timeLineDataArr[nextIndex]);
}
}
});
/**
* 过滤底部柱图阶段数据
* @param {Object} currentBarMinDate
* @param {Object} currentBarMaxDate
*/
function filterStagesAccordingTimeLineMinAndMaxVisibleTimeNode(currentBarMinDate, currentBarMaxDate) {
// 过滤底部柱图的数据
var barCharMinTime = convertHanziDateToJSDate(currentBarMinDate);
var barCharMaxTime = convertHanziDateToJSDate(currentBarMaxDate);
// // 当前时间轴上显示的最大、最小年月
var barCharMinYear = barCharMinTime.getFullYear();
var barCharMinMonth = barCharMinTime.getMonth() + 1;
var barCharMaxYear = barCharMaxTime.getFullYear();
var barCharMaxMonth = barCharMaxTime.getMonth() + 1;
var newStages = new Array();
for(var i = 0; i < stages.length; i++) {
var stage = stages[i];
// 阶段开始时间年月
var stageBeginTimeStr = stage.stageTimes[0];
var stageEndTimeStr = stage.stageTimes[1];
var stageBeginTime = new Date(stageBeginTimeStr);
var stageEndTime = new Date(stageEndTimeStr);
// 阶段结束时间年月
var stageBeginYear = stageBeginTime.getFullYear();
var stageBeginMonth = stageBeginTime.getMonth() + 1;
var stageEndYear = stageEndTime.getFullYear();
var stageEndMonth = stageEndTime.getMonth() + 1;
if(stageBeginTime.getTime() >= barCharMinTime.getTime() &&
stageEndTime.getTime() <= barCharMaxTime.getTime() ||
(stageBeginYear == barCharMinYear && stageBeginMonth >= barCharMinMonth &&
stageEndYear == barCharMaxYear && stageEndMonth <= barCharMaxMonth) ||
(stageBeginYear > barCharMinYear && stageEndYear < barCharMaxYear)) {
newStages.push(stage);
}
}
console.log('newStages-->' + newStages);
return newStages;
}
/**
* 当后退或前进时刷新底部的柱图
* @param {Object} currentBarMinDate 当前时间轴可见最小年月
* @param {Object} currentBarMaxDate 当前时间轴可见最大年月
*/
function refreshBottomBarChart(currentBarMinDate, currentBarMaxDate) {
// $("#chart_bottom").html(' ');
var newStages = filterStagesAccordingTimeLineMinAndMaxVisibleTimeNode(currentBarMinDate, currentBarMaxDate);
// 重新填充柱图数据
fillBarChartData(newStages);
// 初始化底部柱图图表
var myBottomChart = echarts.init(document.getElementById('chart_bottom'));
// 构建图表配置项
bottomChartOption = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
/**
* 也可以使用 formatter: '{b0}:<br />{a0}: {c0}<br />{a1}: {c1}<br />{a2}: {c2}',
* 但是这样当鼠标指向纵坐标的三个阶段中的某一个时,即使该阶段 没有按时完成,或者 没有超时,
* 也会显示 按时 或 超时 的 tooltip
*/
formatter: function(params) {
console.log("params111" + JSON.stringify(params));
var info = params[0].axisValue + ":<br />";
info += params[0].seriesName + ":" + getSeriesDateStr(params[0].value) + "<br />";
for(var i = 0; i < params.length; i++) {
if(params[i].seriesName == params[0].axisValue) {
info += "结束时间:" + getSeriesDateStr(params[i].value);
}
}
return info;
},
},
/**
* 右上角工具栏
*/
toolbox: {
right: '3%',
show: false,
feature: {
/**
* 数据视图
*/
// dataView:{
// show:true,
// },
saveAsImage: {
show: true
}
}
},
/**
* 直角坐标系内绘图网格
*/
grid: {
top: '0%',
left: '0%',
right: '3%',
bottom: '0%',
/**
* grid 区域是否包含坐标轴的刻度标签。
*/
containLabel: false,
},
/**
* 横坐标
*/
xAxis: {
/**
* 时间轴,适用于连续的时序数据,与数值轴相比时间轴带有时间的格式化,
* 在刻度计算上也有所不同,例如会根据跨度的范围来决定使用月,星期,日还是小时范围的刻度。
*/
type: 'time',
/**
* value 是一个包含 min 和 max 的对象,分别表示数据的最大最小值,这个函数应该返回坐标轴的最大值。
*
* 坐标轴刻度最小值。
*/
min: getTimeMilliseconds(getProperTimeAxisBeginAndEndTime()[0]),
/**
* value 是一个包含 min 和 max 的对象,分别表示数据的最大最小值,这个函数应该返回坐标轴的最大值。
*
* 坐标轴刻度最大值。
*/
max: getTimeMilliseconds(getProperTimeAxisBeginAndEndTime()[1]),
/**
* 设置坐标轴分割间隔
*/
interval: getProperTimeAxisInterval(),
axisLine: {
lineStyle: {
color: '#BDC8CD',
width: 1,
},
},
axisTick: {
show: false,
},
/**
* 坐标轴刻度标签的相关设置。
*/
axisLabel: {
show: false,
showMinLabel: true,
showMaxLabel: true,
// rotate: 35,
margin: 12,
fontSize: 16,
color: '#1c2431',
formatter: function(value, index) {
var date = new Date(value);
// var time = date.getFullYear() + "." + (date.getMonth() + 1) + "." + date.getDate();
var time = date.getFullYear();
if(xAxisLabelUnit.month) {
time += "." + (date.getMonth() + 1);
}
if(xAxisLabelUnit.day) {
time += "." + (date.getMonth() + 1) + '.' + date.getDate();
}
return time;
},
},
/**
* 坐标轴刻度分割线
*/
splitLine: {
show: false,
},
},
/**
* 纵坐标
*/
yAxis: {
type: 'category',
data: stagesNameArr,
// show: false,
axisTick: {
show: false,
},
axisLine: {
lineStyle: {
color: '#e9e9ea',
width: 1,
},
},
axisLabel: {
show: false,
fontWeight: 'bold',
fontSize: 16,
color: '#1c2431',
fontFamily: 'Microsoft YaHei',
},
splitLine: {
show: false,
lineStyle: {
color: '#eaeae9',
width: 1,
},
},
},
series: generatedSeries(newStages),
};
// 清除一下 echart 的缓存数据
myBottomChart.clear();
// 将构建好的配置项传入echarts
myBottomChart.setOption(bottomChartOption);
}
// 初始化顶部table图表
inflateTopTableChart();
// 初始化时被过滤的stages,若stages时间跨度大于当前时间轴可见的时间区间,则会过滤掉区间之外的数据
var initialFilterdStages = filterStagesAccordingTimeLineMinAndMaxVisibleTimeNode(timeLineDataArr[preIndex], timeLineDataArr[nextIndex])
// 初始化柱图数据
var initBarData = fillBarChartData(initialFilterdStages);
// 初始化底部柱图图表
var myBottomChart = echarts.init(document.getElementById('chart_bottom'));
// 构建图表配置项
bottomChartOption = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
/**
* 也可以使用 formatter: '{b0}:<br />{a0}: {c0}<br />{a1}: {c1}<br />{a2}: {c2}',
* 但是这样当鼠标指向纵坐标的三个阶段中的某一个时,即使该阶段 没有按时完成,或者 没有超时,
* 也会显示 按时 或 超时 的 tooltip
*/
formatter: function(params) {
console.log("params111" + JSON.stringify(params));
var info = params[0].axisValue + ":<br />";
info += params[0].seriesName + ":" + getSeriesDateStr(params[0].value) + "<br />";
for(var i = 0; i < params.length; i++) {
if(params[i].seriesName == params[0].axisValue) {
info += "结束时间:" + getSeriesDateStr(params[i].value);
}
}
return info;
},
},
/**
* 右上角工具栏
*/
toolbox: {
right: '3%',
show: false,
feature: {
/**
* 数据视图
*/
// dataView:{
// show:true,
// },
saveAsImage: {
show: true
}
}
},
/**
* 直角坐标系内绘图网格
*/
grid: {
top: '0%',
left: '0%',
right: '3%',
bottom: '0%',
/**
* grid 区域是否包含坐标轴的刻度标签。
*/
containLabel: false,
},
/**
* 横坐标
*/
xAxis: {
/**
* 时间轴,适用于连续的时序数据,与数值轴相比时间轴带有时间的格式化,
* 在刻度计算上也有所不同,例如会根据跨度的范围来决定使用月,星期,日还是小时范围的刻度。
*/
type: 'time',
/**
* value 是一个包含 min 和 max 的对象,分别表示数据的最大最小值,这个函数应该返回坐标轴的最大值。
*
* 坐标轴刻度最小值。
*/
min: getTimeMilliseconds(getProperTimeAxisBeginAndEndTime()[0]),
/**
* value 是一个包含 min 和 max 的对象,分别表示数据的最大最小值,这个函数应该返回坐标轴的最大值。
*
* 坐标轴刻度最大值。
*/
max: getTimeMilliseconds(getProperTimeAxisBeginAndEndTime()[1]),
/**
* 设置坐标轴分割间隔
*/
interval: getProperTimeAxisInterval(),
axisLine: {
lineStyle: {
color: '#BDC8CD',
width: 1,
},
},
axisTick: {
show: false,
},
/**
* 坐标轴刻度标签的相关设置。
*/
axisLabel: {
show: true,
showMinLabel: true,
showMaxLabel: true,
// rotate: 35,
margin: 12,
fontSize: 16,
color: '#1c2431',
formatter: function(value, index) {
var date = new Date(value);
// var time = date.getFullYear() + "." + (date.getMonth() + 1) + "." + date.getDate();
var time = date.getFullYear();
if(xAxisLabelUnit.month) {
time += "." + (date.getMonth() + 1);
}
if(xAxisLabelUnit.day) {
time += "." + (date.getMonth() + 1) + '.' + date.getDate();
}
return time;
},
},
/**
* 坐标轴刻度分割线
*/
splitLine: {
show: false,
},
},
/**
* 纵坐标
*/
yAxis: {
type: 'category',
data: stagesNameArr,
// show: false,
axisTick: {
show: false,
},
axisLine: {
lineStyle: {
color: '#e9e9ea',
width: 1,
},
},
axisLabel: {
show: false,
fontWeight: 'bold',
fontSize: 16,
color: '#1c2431',
fontFamily: 'Microsoft YaHei',
},
splitLine: {
show: false,
lineStyle: {
color: '#eaeae9',
width: 1,
},
},
},
series: generatedSeries(initialFilterdStages),
};
// 将构建好的配置项传入echarts
myBottomChart.setOption(bottomChartOption);
});