精华内容
下载资源
问答
  • 软件开发-项目进度甘特图--模板;软件开发-项目进度甘特图--模板;软件开发-项目进度甘特图--模板;软件开发-项目进度甘特图--模板;
  • 客户投诉处理进度甘特图Excel模板.xlsx 工程施工时间进度表甘特图Excel模板.xls 招聘计划甘特图Excel模板.xlsx 甘特图-项目计划进度表Excel模板.xlsx 甘特图工作进度表Excel模板.xls 简洁风工作进度计划表甘特图...
  • 工作计划进度甘特图
  • 工作计划进度甘特图
  • 营销服务中心-百日大会战进度甘特图。甘特图(Gantt chart)又称为横道图、条状图(Bar chart)。其通过条状图来显示项目,进度,和其他时间相关的系统进展的内在关系随着时间进展的情况。以提出者亨利·L·甘特...
  • 工作计划进度甘特图
  • PMP学习进度甘特图

    2013-06-26 12:39:22
    PMP学习进度甘特图,有效安排PMP学习进度,供参考。
  • 施工进度甘特图5.xlsx

    2021-09-11 16:51:07
    施工进度甘特图5
  • 49 物流跟踪进度甘特图
  • 这是三张周工作进度PPT甘特图,.PPTX格式;
  • 这是一张蓝色半年工作进度PPT甘特图,.PPTX格式;
  • 这是一张彩色箭头5数据项,工作进度PPT甘特图,.PPTX格式;
  • 项目进度甘特图PPT模板素材
  • 月份时间进度甘特图PPT模板.pptx
  • 这是一张彩色扁平化工作进度PPT甘特图,.PPTX格式;
  • 使用 Echarts 实现项目进度甘特图

    万次阅读 2018-11-22 17:34:19
    Echarts 功能十分强大,可以实现多种图表效果,下面简单介绍下最近使用Eharts 实现的一个项目进度甘特图。 下面是实现的效果: 目录 一.概览 Echarts 基本内容 1.官网文档 2.查看示例 二.需求分析 1.数据...

     Echarts 功能十分强大,可以实现多种图表效果,下面简单介绍下最近使用 Eharts 实现的一个项目进度甘特图。

    下面是实现的效果:

    目录

     

    一.概览 Echarts 基本内容

    1.官网文档

    2.查看示例

    二.需求分析

    1.数据系列(series)

    2.标线 (markLine)

    3.数据对象结构

    4.“超时”、“按时”划分

    三.引入并配置 Echarts

    1.坑

    2.具体操作

    四.说明


    一.概览 Echarts 基本内容

    1.官网文档

    首先基本的前端搭建必须的,然后应该大致浏览一下 Echarts 官网 文档 专题下的 教程 和 配置项手册,如果不是要实现特别复杂的效果和功能,这些内容基本就足够了。

     

     

    2.查看示例

    只看文字内容比较抽象,因为要做甘特图,可以先去 实例 中找一个甘特图研究下,看看别人是怎么实现的。你会发现Echarts官网好像只能在 实例 下找到官方示例图,别急,操作一番就可以看其他无私的贡献者发布的图表了。

    点击官网页面右上角 “EN”,切换为英文版,

    在 Get Started 下 点击 Gallery,

    这样就能看到其他开发者贡献的 Demo 了,

    点击 展开筛选,筛选你要找的图表类型。

    下面这个图表(点击这里查看该图表Demo)和我们要实现的效果还是有几分类似的:

    当然细节还得自己研究修改一下。

     

    二.需求分析

    从文章开头的图表效果来看,横坐标刻度值为时间,纵坐标为 “方案”、“纲要”、“成果” 三个项目阶段类目,每个阶段类目中包含按时完成(蓝色柱状图)和超时完成(红色柱状图)两种系列的数据,另外还有垂直于 x 时间坐标轴的 “计划开始时间”和“有效期”两条时间标线。结合查阅官网文档和demo,应该有以下初步结论:

    1.数据系列(series)

    每个阶段类目中实际应该有三种系列(series)的数据的,即应该有 开始时间、按时完成时间和超时完成时间三种系列,且三种系列的柱状图是堆叠在一起的。

    2.标线 (markLine)

    “计划开始时间” markLine 可以放在在 开始时间series 中,“有效期”markLine可以在 按时完成 或 超时完成 series 中。

    3.数据对象结构

    这里比较容易理解的传值结构,应该是传入 三个时间段 和 两个刻度值,类似下面这种结构:

    /**
     * 存储阶段进度的对象
     */
    var stageProgress = {
    	// 方案 实际时间段
    	fangAnTimeBucket: ['2017-01-01', '2017-03-01'],
    	// 纲要 实际时间段
    	gangYaoTimeBucket: ['2017-02-26', '2017-08-29'],
    	// 成果 实际时间段
    	chengGuoTimeBucket: ['2017-08-29', '2017-12-06'],
    	// 计划开始时间
    	scheduledStartTime: '2016-12-22',
    	// 有效截止日期
    	validTime: '2017-05-10'
    };

     

    4.“超时”、“按时”划分

    这里要求三个阶段实际时间段的首尾可以不连续,即三个阶段之间没有影响,所以,是否超时就取决于 有效期 与 阶段实际时间段 之间的关系了。比如,有效期在阶段实际时间段开始和结束时间之间,那么该阶段是超时的,但是该阶段的柱状图应该是蓝色红色共存的;有效期在阶段实际时间段结束时间之后,那么该阶段按时完成,应该都是蓝色的;有效期在阶段实际时间段开始时间之前,那么该阶段超时完成,应该都是红色的。

    其实主要是第四点需要计算一下,其他 参考一下 配置项手册基本没有太大问题。

     

    三.引入并配置 Echarts

    1.坑

    用新不用旧,但是经过我的测试,Echarets4.x 透明堆叠不起效果,即设置了 series 为 bar ,且 开始时间、按时完成时间、按时完成时间 三个 series 设置了 相同的 stack 后,开始时间 柱状图并不能起到遮盖其他系列柱状图的效果,于是尝试使用3.x 版本,是可以起到柱条堆叠辅助隐藏的效果的。

    若果需要兼容 IE8 的话,那么你需要定制下载  Echarts(我在实际测试中发现,这个定制下载即使选中 兼容IE8 ,下载下来的js仍让无法兼容 IE8,真的是让人脑壳痛)。

     

     

     

    2.具体操作

    在你的页面中,放置一个 div,作为放置图表的容器,需要为它指定宽高。

    <!-- 为ECharts准备一个具备大小(宽高)的Dom -->
    <div id="content"></div>

     

    控制图表的 js 代码基本结构如下:

    // 基于准备好的dom,初始化echarts实例
            var myChart = echarts.init(document.getElementById('main'));
    
            // 指定图表的配置项和数据
            var option = {
                title: {
                    text: 'ECharts 入门示例'
                },
                tooltip: {},
                legend: {
                    data:['销量']
                },
                xAxis: {
                    data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
                },
                yAxis: {},
                series: [{
                    name: '销量',
                    type: 'bar',
                    data: [5, 20, 36, 10, 10, 20]
                }]
            };
    
            // 使用刚指定的配置项和数据显示图表。
            myChart.setOption(option);

     

    下面是实现本文开头图表效果的 js,方法说明和需要注意的点基本都写在注释里了:

    /**
     * 存储阶段进度时间段的对象
     */
    var stageProgress = {
    // 			// 方案 实际时间段
    // 			fangAnTimeBucket: ['2017-01-01', '2017-03-01'],
    // 			// 纲要 实际时间段
    // 			gangYaoTimeBucket: ['2017-02-26', '2017-08-29'],
    // 			// 成果 实际时间段
    // 			chengGuoTimeBucket: ['2017-08-29', '2017-12-06'],
    // 			// 计划开始时间
    // 			scheduledStartTime: '2016-12-22',
    // 			// 有效截止日期
    // 			validTime: '2017-05-10'
    
    	// 		// 方案 实际时间段
    	// 		fangAnTimeBucket: ['2017-01-01', '2017-01-20'],
    	// 		// 纲要 实际时间段
    	// 		gangYaoTimeBucket: ['2017-02-07', '2017-02-28'],
    	// 		// 成果 实际时间段
    	// 		chengGuoTimeBucket: ['2017-02-15', '2017-03-20'],
    	// 		// 计划开始时间
    	// 		scheduledStartTime: '2017-01-01',
    	// 		// 有效截止日期
    	// 		validTime: '2017-02-20'
    
    	// 方案 实际时间段
    	fangAnTimeBucket: ['2017-11-23', '2017-11-28'],
    	// 纲要 实际时间段
    	gangYaoTimeBucket: ['2017-11-25', '2017-11-30'],
    	// 成果 实际时间段
    	chengGuoTimeBucket: ['2017-12-06', '2017-12-12'],
    	// 计划开始时间
    	scheduledStartTime: '2017-11-23',
    	// 有效截止日期
    	validTime: '2017-12-02'
    };
    
    /**
     * 横坐标轴时间刻度可选值
     * 这里 month和year 没有考虑平闰年之分
     */
    var timeInterval = {
    	day: 3600 * 1000 * 24,
    	month: 3600 * 1000 * 24 * 31,
    	year: 3600 * 1000 * 24 * 31 * 12,
    };
    
    /**
     * 时间坐标轴标签单位应该精确到哪一位
     */
    var xAxisLabelUnit = {
    	year: false,
    	month: false,
    	day: false
    }
    
    /**
     * 获取合适的横坐标时间刻度间隔
     */
    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;
    	}
    }
    
    /**
     * 获取横轴坐标数据源,这里横坐标只显示年月
     * 最小值取传入数据最小的时间再减小一个月
     * 最大值取传入数据最小的时间再增加一个月
     */
    function getXAxisData() {
    	var arr = new Array();
    	arr = arr.concat(stageProgress.scheduledStartTime)
    		.concat(stageProgress.fangAnTimeBucket)
    		.concat(stageProgress.gangYaoTimeBucket)
    		.concat(stageProgress.chengGuoTimeBucket)
    		.concat(stageProgress.validTime).filter(function(item) {
    			return item != "-";
    		}).sort();
    	console.log(arr);
    	return arr;
    }
    
    /**
     * 更改日期字符串为相应月份的第一天
     * @param {Object} dateStr 日期字符串
     */
    function changeDateToMonthFirstDay(dateStr) {
    	var inputDate = new Date(dateStr);
    	inputDate.setDate(1);
    	var result = inputDate.getFullYear() + "-" +
    		(inputDate.getMonth() >= 9 ? inputDate.getMonth() + 1 : "0" +
    			(inputDate.getMonth() + 1)) + "-" + ("0" + 1);
    	return result;
    }
    
    /**
     * 获取格式化的日期 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;
    }
    
    var faOnTimeCompletionTime = getOnTimeCompletionTime('方案', stageProgress.fangAnTimeBucket[0], stageProgress.fangAnTimeBucket[
    	1]);
    var gyOnTimeCompletionTime = getOnTimeCompletionTime('纲要', stageProgress.gangYaoTimeBucket[0], stageProgress.gangYaoTimeBucket[
    	1]);
    var cgOnTimeCompletionTime = getOnTimeCompletionTime('成果', stageProgress.chengGuoTimeBucket[0], stageProgress.chengGuoTimeBucket[
    	1]);
    
    var faOverTimeCompletionTime = getOverTimeCompletionTime('方案', stageProgress.fangAnTimeBucket[0], stageProgress.fangAnTimeBucket[
    	1]);
    var gyOverTimeCompletionTime = getOverTimeCompletionTime('纲要', stageProgress.gangYaoTimeBucket[0], stageProgress.gangYaoTimeBucket[
    	1]);
    var cgOverTimeCompletionTime = getOverTimeCompletionTime('成果', stageProgress.chengGuoTimeBucket[0], stageProgress.chengGuoTimeBucket[
    	1]);
    
    /**
     * 时间数组
     */
    var timeArray = {
    	// 开始时间
    	beginTimeArr: [
    		getTimeMilliseconds(stageProgress.fangAnTimeBucket[0]),
    		getTimeMilliseconds(stageProgress.gangYaoTimeBucket[0]),
    		getTimeMilliseconds(stageProgress.chengGuoTimeBucket[0]),
    	],
    	// 按时完成时间
    	onTimeCompletionTimeArr: [
    		getTimeMilliseconds(faOnTimeCompletionTime),
    		getTimeMilliseconds(gyOnTimeCompletionTime),
    		getTimeMilliseconds(cgOnTimeCompletionTime),
    	],
    	// 超时完成时间
    	overTimeCompletionTimeArr: [
    		getTimeMilliseconds(faOverTimeCompletionTime),
    		getTimeMilliseconds(gyOverTimeCompletionTime),
    		getTimeMilliseconds(cgOverTimeCompletionTime),
    	],
    };
    
    // 初始化图表
    var myChart = echarts.init(document.getElementById('content'));
    
    // 构建图表配置项
    option = {
    	tooltip: {
    		trigger: 'axis',
    		axisPointer: {
    			type: 'shadow',
    		},
    
    		/**
    		 * 也可以使用 formatter: '{b0}:<br />{a0}: {c0}<br />{a1}: {c1}<br />{a2}: {c2}',
    		 * 但是这样当鼠标指向纵坐标的三个阶段中的某一个时,即使该阶段 没有按时完成,或者 没有超时,
    		 * 也会显示 按时 或 超时 的 tooltip
    		 */
    		formatter: function(params) {
    // 			console.log("params[0]" + JSON.stringify(params[0]));
    // 			console.log("params[1]" + JSON.stringify(params[1]));
    			console.log("params[2]" + JSON.stringify(params[2]));
    			var info = params[0].axisValue + ":<br />";
    			info += params[0].seriesName + ":" + getSeriesDateStr(params[0].data) + "<br />";
    			info += "结束时间:" + (params[2].data != "-" && params[2].data != undefined && params[2].data != null&&!isNaN(params[2].data) ?
    				getSeriesDateStr(params[2].data) :
    				(!isNaN(params[1].data)&&params[1].data != undefined && params[1].data != null ? getSeriesDateStr(params[1].data) : "-"));
    			return info;
    		},
    	},
    
    	/**
    	 * 右上角工具栏
    	 */
    	toolbox: {
    		right: '3%',
    		show: false,
    		feature: {
    			/**
    			 * 数据视图
    			 */
    			// 			dataView:{
    			// 				show:true,
    			// 			},
    			saveAsImage: {
    				show: true
    			}
    		}
    	},
    
    	/**
    	 * 图例
    	 */
    	legend: {
    		data: ['按时完成', '超时完成'],
    		tooltip: {
    			show: true,
    		},
    		itemWidth: 15,
    		itemHeight: 15,
    		textStyle: {
    			fontSize: 16,
    			fontFamily: 'Microsoft YaHei',
    		},
    		itemGap: 120,
    		padding: 10,
    	},
    
    	/**
    	 * 直角坐标系内绘图网格
    	 */
    	grid: {
    		left: '3%',
    		right: '3%',
    		bottom: '2%',
    		/**
    		 * grid 区域是否包含坐标轴的刻度标签。
    		 */
    		containLabel: true,
    	},
    
    	/**
    	 * 横坐标
    	 */
    	xAxis: {
    		// 		/**
    		// 		 * 坐标轴指示器
    		// 		 */
    		// 		axisPointer: {
    		// 			show: true,
    		// 		},
    
    		/**
    		 * 时间轴,适用于连续的时序数据,与数值轴相比时间轴带有时间的格式化,
    		 * 在刻度计算上也有所不同,例如会根据跨度的范围来决定使用月,星期,日还是小时范围的刻度。
    		 */
    		type: 'time',
    
    		/**
    		 * value 是一个包含 min 和 max 的对象,分别表示数据的最大最小值,这个函数应该返回坐标轴的最大值。
    		 *
    		 * 坐标轴刻度最小值。
    		 */
    		min: function(value) {
    			return value.min + (getTimeMilliseconds(getProperTimeAxisBeginAndEndTime()[0]) - value.min);
    		},
    
    		/**
    		 * value 是一个包含 min 和 max 的对象,分别表示数据的最大最小值,这个函数应该返回坐标轴的最大值。
    		 *
    		 * 坐标轴刻度最大值。
    		 */
    		max: function(value) {
    			return value.max + (getTimeMilliseconds(getProperTimeAxisBeginAndEndTime()[1]) - value.max);
    		},
    		// 
    		/**
    		 * 设置坐标轴分割间隔
    		 */
    		interval: getProperTimeAxisInterval(),
    		axisLine: {
    			lineStyle: {
    				color: '#BDC8CD',
    				width: 1,
    			},
    		},
    
    		/**
    		 * 坐标轴刻度标签的相关设置。
    		 */
    		axisLabel: {
    			showMinLabel: false,
    			showMaxLabel: false,
    			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: ['方\n案', '纲\n要', '成\n果'],
    		axisTick: {
    			show: false,
    		},
    		axisLine: {
    			lineStyle: {
    				color: '#e9e9ea',
    				width: 1,
    			},
    		},
    		axisLabel: {
    			fontWeight: 'bold',
    			fontSize: 16,
    			color: '#1c2431',
    			fontFamily: 'Microsoft YaHei',
    		},
    		splitLine: {
    			show: true,
    			lineStyle: {
    				color: '#eaeae9',
    				width: 1,
    			},
    		},
    	},
    
    	/**
    	 * 系列
    	 */
    	series: [{
    		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) {
    					return getSeriesDateStr(params.value);
    				},
    				show: true,
    				position: 'insideRight',
    				fontSize: 16,
    				color: '#1c2431',
    				fontFamily: 'Microsoft YaHei',
    				offset: [40, -20],
    			}
    		},
    		data: timeArray.beginTimeArr,
    
    		/**
    		 * 标注线
    		 */
    		markLine: {
    			lineStyle: {
    				normal: {
    					color: '#0f77e9',
    				},
    			},
    			label: {
    				normal: {
    					fontWeight: 'bold',
    					padding: 2,
    					fontSize: 14,
    					fontFamily: 'Microsoft YaHei',
    					formatter: function(params) {
    						return '计划开始时间 ' + getSeriesDateStr(params.value);
    					},
    				},
    			},
    			data: [{
    				name: '计划开始时间',
    				xAxis: getTimeMilliseconds(stageProgress.scheduledStartTime),
    			}, ]
    		},
    	}, {
    		name: '按时完成',
    		type: 'bar',
    		stack: '时间',
    		itemStyle: {
    			normal: {
    				color: '#0f77e9'
    			}
    		},
    		label: {
    			normal: {
    				formatter: function(params) {
    					return getSeriesDateStr(params.value);
    				},
    				show: true,
    				fontSize: 16,
    				color: '#1c2431',
    				position: 'right',
    				fontFamily: 'Microsoft YaHei',
    				offset: [-45, -20],
    			}
    		},
    		data: timeArray.onTimeCompletionTimeArr,
    		markLine: {
    			lineStyle: {
    				normal: {
    					color: '#ff4747',
    				},
    
    			},
    			label: {
    				normal: {
    					fontWeight: 'bold',
    					padding: 2,
    					fontSize: 14,
    					fontFamily: 'Microsoft YaHei',
    					formatter: function(params) {
    						return '计划完成时间 ' + getSeriesDateStr(params.value);
    					},
    				},
    			},
    			data: [{
    				name: '有效期',
    				xAxis: getTimeMilliseconds(stageProgress.validTime),
    			}, ]
    		},
    	}, {
    		name: '超时完成',
    		type: 'bar',
    		stack: '时间',
    		itemStyle: {
    			normal: {
    				color: '#ff4747'
    			}
    		},
    		label: {
    			normal: {
    				formatter: function(params) {
    					return getSeriesDateStr(params.value);
    				},
    				show: true,
    				fontSize: 16,
    				color: '#1c2431',
    				offset: [-45, -20],
    				position: 'right',
    				fontFamily: 'Microsoft YaHei',
    			}
    		},
    		data: timeArray.overTimeCompletionTimeArr,
    		/**
    		 * 柱状图宽度
    		 */
    		barWidth: 20,
    	}, ]
    };
    
    // 将构建好的配置项传入echarts
    myChart.setOption(option);
    
    /**
     * 时间对象转日期字符串 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;
    };
    
    /**
     * 获取阶段的计划内完成时间(蓝色柱状图值)
     * @param {Object} stage 阶段
     * @param {Object} stateBeginTime
     * @param {Object} stateCompletionTime
     */
    function getOnTimeCompletionTime(stage, stageBeginTimeStr, stageCompletionTimeStr) {
    	var validTimeMillis = getTimeMilliseconds(stageProgress.validTime);
    	var stageBeginTimeMillis = getTimeMilliseconds(stageBeginTimeStr);
    	var stageCompletionTimeMillis = getTimeMilliseconds(stageCompletionTimeStr);
    	if (validTimeMillis <= stageBeginTimeMillis) {
    		// 若阶段开始时间大于等于有效期,则项目超时完成,有效时间为'-'
    		return '-';
    	}
    	if (validTimeMillis > stageBeginTimeMillis && validTimeMillis < stageCompletionTimeMillis) {
    		// 若有效期介于阶段完成时间和阶段开始时间之间,则该阶段按时完后时间(实际该阶段是超时完成的)即蓝色柱状图的终值为有效期
    		return stageProgress.validTime;
    	}
    	if (validTimeMillis >= stageCompletionTimeMillis) {
    		// 若有效期大于等于阶段完成时间,则阶段按时完成
    		return stageCompletionTimeStr;
    	}
    }
    
    /**
     * 获取阶段内的超时完成时间(红色色柱状图值)
     * @param {Object} stage 阶段
     * @param {Object} stateBeginTime
     * @param {Object} stateCompletionTime
     */
    function getOverTimeCompletionTime(stage, stageBeginTimeStr, stageCompletionTimeStr) {
    	var validTimeMillis = getTimeMilliseconds(stageProgress.validTime);
    	var stageBeginTimeMillis = getTimeMilliseconds(stageBeginTimeStr);
    	var stageCompletionTimeMillis = getTimeMilliseconds(stageCompletionTimeStr);
    
    	if (validTimeMillis < stageCompletionTimeMillis) {
    		// 阶段完成时间大于有效期,则将阶段完成时间作为超时时间返回
    		return stageCompletionTimeStr;
    	}
    
    	if (validTimeMillis >= stageCompletionTimeMillis) {
    		// 阶段完成时间小于等于有效期,则阶段按时完成,超时时间应为'-'
    		return '-';
    	}
    }
    
    /**
     * 根据时间字符串获取对应的毫秒值
     * @param {Object} timeStr 时间字符串
     */
    function getTimeMilliseconds(timeStr) {
    	return (new Date(timeStr)).getTime();
    }
    
    /**
     *获取时间坐标轴的起始和结束值
     */
    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) {
    		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;
    }
    

     

    四.后续需求修改1

    最近需求有修改,需要在 “方案”、“纲要”和“成果” 三个阶段的每个阶段都显示两个柱条,来显示对应阶段的实际用时和计划用时,在每个阶段中,实际用时柱条在上,计划用时柱条在下,突出对比效果,并且实际用时的柱条要可以显示蓝色(计划内用时)和红色(超时用时)两种颜色柱条的堆叠,同时移除之前的贯穿三个阶段的 “计划开始时间”和“有效期” markLine,最后修改成这个效果

     

     

    这里需要修改的地方主要有:

    1. 去掉 markLine 配置。
    2. 在 series 中,新增另一个 stack: '计划'来显示计划用时柱条。
    3. 同时由于在一个阶段中已经有了一个新柱条可以显示某一阶段的计划完成时间,所以在实际完成时间的柱条上,不再需要显示计划完成时间了(对应之前进度图中的有效期),防止文字挤压。
    4. getProperTimeAxisInterval() 方法中 在总的时间跨度 <=3 时,return timeInterval.day * 5,这样横坐标显示的坐标数量可以多些。
    5. getOnTimeCompletionTime() 和 getOverTimeCompletionTime() 中新添加一个参数 stagePlanCompletionTimeStr 阶段计划完成时间,同时方法内的对比使用参数,不再使用之前的 “有效时间” 来对比。

    具体js代码如下:

    /**
     * 存储阶段进度时间段的对象
     */
    var stageProgress = {
    	// 			// 方案 实际时间段
    	// 			fangAnTimeBucket: ['2017-01-01', '2017-03-01'],
    	// 			// 纲要 实际时间段
    	// 			gangYaoTimeBucket: ['2017-02-26', '2017-08-29'],
    	// 			// 成果 实际时间段
    	// 			chengGuoTimeBucket: ['2017-08-29', '2017-12-06'],
    	// 			// 计划开始时间
    	// 			scheduledStartTime: '2016-12-22',
    	// 			// 有效截止日期
    	// 			validTime: '2017-05-10'
    
    	// 		// 方案 实际时间段
    	// 		fangAnTimeBucket: ['2017-01-01', '2017-01-20'],
    	// 		// 纲要 实际时间段
    	// 		gangYaoTimeBucket: ['2017-02-07', '2017-02-28'],
    	// 		// 成果 实际时间段
    	// 		chengGuoTimeBucket: ['2017-02-15', '2017-03-20'],
    	// 		// 计划开始时间
    	// 		scheduledStartTime: '2017-01-01',
    	// 		// 有效截止日期
    	// 		validTime: '2017-02-20'
    
    //	// 方案 实际时间段
    //	fangAnTimeBucket: ['2017-11-23', '2017-11-30'],
    //	// 方案 计划时间段
    //	fangAnPlanTimeBucket: ['2017-11-20', '2017-11-26'],
    //
    //	// 纲要 实际时间段
    //	gangYaoTimeBucket: ['2017-11-25', '2017-12-03'],
    //	// 纲要 计划时间段
    //	gangYaoPlanTimeBucket: ['2017-11-25', '2017-12-03'],
    //
    //	// 成果 实际时间段
    //	chengGuoTimeBucket: ['2017-12-06', '2017-12-12'],
    //	// 成果 计划时间段
    //	chengGuoPlanTimeBucket: ['2017-12-07', '2017-12-14']
    	
    	// 方案 实际时间段
    	fangAnTimeBucket: ['2019-02-04', '2019-02-27'],
    	// 方案 计划时间段
    	fangAnPlanTimeBucket: ['2019-02-05', '2019-02-26'],
    
    	// 纲要 实际时间段
    	gangYaoTimeBucket: ['-', '-'],
    	// 纲要 计划时间段
    	gangYaoPlanTimeBucket: ['2019-02-27', '2019-03-07'],
    
    	// 成果 实际时间段
    	chengGuoTimeBucket: ['-', '-'],
    	// 成果 计划时间段
    	chengGuoPlanTimeBucket: ['2019-03-08', '2019-04-17']
    	
    	//	// 计划开始时间
    	//	scheduledStartTime: '2017-11-23',
    	//	// 有效截止日期
    	//	validTime: '2017-11-30 '
    };
    
    /**
     * 横坐标轴时间刻度可选值
     * 这里 month和year 没有考虑平闰年之分
     */
    var timeInterval = {
    	day: 3600 * 1000 * 24,
    	month: 3600 * 1000 * 24 * 31,
    	year: 3600 * 1000 * 24 * 31 * 12,
    };
    
    /**
     * 时间坐标轴标签单位应该精确到哪一位
     */
    var xAxisLabelUnit = {
    	year: false,
    	month: false,
    	day: false
    }
    
    /**
     * 获取合适的横坐标时间刻度间隔
     */
    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 <= 3) {
    		xAxisLabelUnit.day = true;
    		return timeInterval.day * 5;
    	} 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;
    	}
    }
    
    /**
     * 获取横轴坐标数据源,这里横坐标只显示年月
     * 最小值取传入数据最小的时间再减小一个月
     * 最大值取传入数据最小的时间再增加一个月
     */
    function getXAxisData() {
    	var arr = new Array();
    	arr = arr
    		//	.concat(stageProgress.scheduledStartTime)
    		.concat(stageProgress.fangAnTimeBucket)
    		.concat(stageProgress.fangAnPlanTimeBucket)
    		.concat(stageProgress.gangYaoTimeBucket)
    		.concat(stageProgress.gangYaoPlanTimeBucket)
    		.concat(stageProgress.chengGuoTimeBucket)
    		.concat(stageProgress.chengGuoPlanTimeBucket)
    		//		.concat(stageProgress.validTime)
    		.filter(function(item) {
    			return item != "-";
    		}).sort();
    	console.log(arr);
    	return arr;
    }
    
    /**
     * 更改日期字符串为相应月份的第一天
     * @param {Object} dateStr 日期字符串
     */
    function changeDateToMonthFirstDay(dateStr) {
    	var inputDate = new Date(dateStr);
    	inputDate.setDate(1);
    	var result = inputDate.getFullYear() + "-" +
    		(inputDate.getMonth() >= 9 ? inputDate.getMonth() + 1 : "0" +
    			(inputDate.getMonth() + 1)) + "-" + ("0" + 1);
    	return result;
    }
    
    /**
     * 获取格式化的日期 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;
    }
    
    var faOnTimeCompletionTime = getOnTimeCompletionTime('方案', stageProgress.fangAnTimeBucket[0], stageProgress.fangAnTimeBucket[
    	1], stageProgress.fangAnPlanTimeBucket[1]);
    var gyOnTimeCompletionTime = getOnTimeCompletionTime('纲要', stageProgress.gangYaoTimeBucket[0], stageProgress.gangYaoTimeBucket[
    	1], stageProgress.gangYaoPlanTimeBucket[1]);
    var cgOnTimeCompletionTime = getOnTimeCompletionTime('成果', stageProgress.chengGuoTimeBucket[0], stageProgress.chengGuoTimeBucket[
    	1], stageProgress.chengGuoPlanTimeBucket[1]);
    
    var faOverTimeCompletionTime = getOverTimeCompletionTime('方案', stageProgress.fangAnTimeBucket[0], stageProgress.fangAnTimeBucket[
    	1], stageProgress.fangAnPlanTimeBucket[1]);
    var gyOverTimeCompletionTime = getOverTimeCompletionTime('纲要', stageProgress.gangYaoTimeBucket[0], stageProgress.gangYaoTimeBucket[
    	1], stageProgress.gangYaoPlanTimeBucket[1]);
    var cgOverTimeCompletionTime = getOverTimeCompletionTime('成果', stageProgress.chengGuoTimeBucket[0], stageProgress.chengGuoTimeBucket[
    	1], stageProgress.chengGuoPlanTimeBucket[1]);
    
    /**
     * 时间数组
     */
    var timeArray = {
    	// 实际开始时间
    	beginTimeArr: [
    		getTimeMilliseconds(stageProgress.fangAnTimeBucket[0]),
    		getTimeMilliseconds(stageProgress.gangYaoTimeBucket[0]),
    		getTimeMilliseconds(stageProgress.chengGuoTimeBucket[0]),
    	],
    	// 按时完成时间
    	onTimeCompletionTimeArr: [
    		getTimeMilliseconds(faOnTimeCompletionTime),
    		getTimeMilliseconds(gyOnTimeCompletionTime),
    		getTimeMilliseconds(cgOnTimeCompletionTime),
    	],
    	// 超时完成时间
    	overTimeCompletionTimeArr: [
    		getTimeMilliseconds(faOverTimeCompletionTime),
    		getTimeMilliseconds(gyOverTimeCompletionTime),
    		getTimeMilliseconds(cgOverTimeCompletionTime),
    	],
    
    	// 计划开始时间
    	planbeginTimeArr: [
    		getTimeMilliseconds(stageProgress.fangAnPlanTimeBucket[0]),
    		getTimeMilliseconds(stageProgress.gangYaoPlanTimeBucket[0]),
    		getTimeMilliseconds(stageProgress.chengGuoPlanTimeBucket[0]),
    	],
    	planCompletionTimeArr: [
    		getTimeMilliseconds(stageProgress.fangAnPlanTimeBucket[1]),
    		getTimeMilliseconds(stageProgress.gangYaoPlanTimeBucket[1]),
    		getTimeMilliseconds(stageProgress.chengGuoPlanTimeBucket[1]),
    	],
    };
    
    // 初始化图表
    var myChart = echarts.init(document.getElementById('content'));
    
    // 构建图表配置项
    option = {
    	tooltip: {
    		trigger: 'axis',
    		axisPointer: {
    			type: 'shadow',
    		},
    
    		/**
    		 * 也可以使用 formatter: '{b0}:<br />{a0}: {c0}<br />{a1}: {c1}<br />{a2}: {c2}',
    		 * 但是这样当鼠标指向纵坐标的三个阶段中的某一个时,即使该阶段 没有按时完成,或者 没有超时,
    		 * 也会显示 按时 或 超时 的 tooltip
    		 */
    		formatter: function(params) {
    			console.log("params" + JSON.stringify(params));
    //			 			console.log("params[0]" + JSON.stringify(params[0]));
    //			 			console.log("params[1]" + JSON.stringify(params[1]));
    //			console.log("params[2]" + JSON.stringify(params[2]));
    			var info = params[0].axisValue + ":<br />";
    			info += params[0].seriesName + ":" + getSeriesDateStr(params[0].data) + "<br />";
    			info += "结束时间:" + (params[2].data != "-" && params[2].data != undefined && params[2].data != null && !isNaN(params[2].data) ?
    				getSeriesDateStr(params[2].data) :
    				(!isNaN(params[1].data) && params[1].data != undefined && params[1].data != null ? getSeriesDateStr(params[1].data) : "-"))
    			+ "<br />";
    			info+=params[3].seriesName+":"+getSeriesDateStr(params[3].data) + "<br />";
    			info+="计划完成时间:"+getSeriesDateStr(params[4].data);
    			return info;
    		},
    	},
    
    	/**
    	 * 右上角工具栏
    	 */
    	toolbox: {
    		right: '3%',
    		show: false,
    		feature: {
    			/**
    			 * 数据视图
    			 */
    			// 			dataView:{
    			// 				show:true,
    			// 			},
    			saveAsImage: {
    				show: true
    			}
    		}
    	},
    
    	/**
    	 * 图例
    	 */
    	legend: {
    		data: ['按时', '超时', '计划'],
    		tooltip: {
    			show: true,
    		},
    		itemWidth: 15,
    		itemHeight: 15,
    		textStyle: {
    			fontSize: 16,
    			fontFamily: 'Microsoft YaHei',
    		},
    		itemGap: 80,
    		padding: 10,
    	},
    
    	/**
    	 * 直角坐标系内绘图网格
    	 */
    	grid: {
    		left: '3%',
    		right: '3%',
    		bottom: '2%',
    		/**
    		 * grid 区域是否包含坐标轴的刻度标签。
    		 */
    		containLabel: true,
    	},
    
    	/**
    	 * 横坐标
    	 */
    	xAxis: {
    		// 		/**
    		// 		 * 坐标轴指示器
    		// 		 */
    		// 		axisPointer: {
    		// 			show: true,
    		// 		},
    
    		/**
    		 * 时间轴,适用于连续的时序数据,与数值轴相比时间轴带有时间的格式化,
    		 * 在刻度计算上也有所不同,例如会根据跨度的范围来决定使用月,星期,日还是小时范围的刻度。
    		 */
    		type: 'time',
    
    		/**
    		 * value 是一个包含 min 和 max 的对象,分别表示数据的最大最小值,这个函数应该返回坐标轴的最大值。
    		 *
    		 * 坐标轴刻度最小值。
    		 */
    		min: function(value) {
    			return value.min + (getTimeMilliseconds(getProperTimeAxisBeginAndEndTime()[0]) - value.min);
    		},
    
    		/**
    		 * value 是一个包含 min 和 max 的对象,分别表示数据的最大最小值,这个函数应该返回坐标轴的最大值。
    		 *
    		 * 坐标轴刻度最大值。
    		 */
    		max: function(value) {
    			return value.max + (getTimeMilliseconds(getProperTimeAxisBeginAndEndTime()[1]) - value.max);
    		},
    		// 
    		/**
    		 * 设置坐标轴分割间隔
    		 */
    		interval: getProperTimeAxisInterval(),
    		axisLine: {
    			lineStyle: {
    				color: '#BDC8CD',
    				width: 1,
    			},
    		},
    
    		/**
    		 * 坐标轴刻度标签的相关设置。
    		 */
    		axisLabel: {
    			showMinLabel: false,
    			showMaxLabel: false,
    			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: ['方\n案', '纲\n要', '成\n果'],
    		axisTick: {
    			show: false,
    		},
    		axisLine: {
    			lineStyle: {
    				color: '#e9e9ea',
    				width: 1,
    			},
    		},
    		axisLabel: {
    			fontWeight: 'bold',
    			fontSize: 16,
    			color: '#1c2431',
    			fontFamily: 'Microsoft YaHei',
    		},
    		splitLine: {
    			show: true,
    			lineStyle: {
    				color: '#eaeae9',
    				width: 1,
    			},
    		},
    	},
    
    	/**
    	 * 系列
    	 */
    	series: [{
    			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) {
    						return getSeriesDateStr(params.value);
    					},
    					show: true,
    					position: 'insideRight',
    					fontSize: 16,
    					color: '#1c2431',
    					fontFamily: 'Microsoft YaHei',
    					offset: [40, -15],
    				}
    			},
    			data: timeArray.beginTimeArr,
    			/**
    			 * 柱状图宽度
    			 */
    			barWidth: 15,
    			barCategoryGap:'50%',
    		}, {
    			name: '按时',
    			type: 'bar',
    			stack: '时间',
    			itemStyle: {
    				normal: {
    					color: '#0f77e9'
    				}
    			},
    			label: {
    				normal: {
    					formatter: function(params) {
    						return getSeriesDateStr(params.value);
    					},
    					show: false,
    					fontSize: 16,
    					color: '#1c2431',
    					position: 'right',
    					fontFamily: 'Microsoft YaHei',
    					offset: [-45, -15],
    				}
    			},
    			data: timeArray.onTimeCompletionTimeArr,
    			/**
    			 * 柱状图宽度
    			 */
    			barWidth: 15,
    			barCategoryGap:'50%',
    		}, {
    			name: '超时',
    			type: 'bar',
    			stack: '时间',
    			itemStyle: {
    				normal: {
    					color: '#ff4747'
    				}
    			},
    			label: {
    				normal: {
    					formatter: function(params) {
    						return getSeriesDateStr(params.value);
    					},
    					show: true,
    					fontSize: 16,
    					color: '#1c2431',
    					offset: [-45, -15],
    					position: 'right',
    					fontFamily: 'Microsoft YaHei',
    				}
    			},
    			data: timeArray.overTimeCompletionTimeArr,
    			/**
    			 * 柱状图宽度
    			 */
    			barWidth: 15,
    			barCategoryGap:'50%',
    		},
    		{
    			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) {
    						return getSeriesDateStr(params.value);
    					},
    					show: true,
    					position: 'insideRight',
    					fontSize: 16,
    					color: '#1c2431',
    					fontFamily: 'Microsoft YaHei',
    					//					offset: [40, -20],
    					offset: [45, 18],
    				}
    			},
    			data: timeArray.planbeginTimeArr,
    			/**
    			 * 柱状图宽度
    			 */
    			barWidth: 15,
    			barCategoryGap:'50%',
    		},
    		{
    			name: '计划',
    			type: 'bar',
    			stack: '计划',
    			itemStyle: {
    				normal: {
    					color: '#FFA500'
    				}
    			},
    			label: {
    				normal: {
    					formatter: function(params) {
    						return getSeriesDateStr(params.value);
    					},
    					show: true,
    					fontSize: 16,
    					color: '#1c2431',
    					//					offset: [-45, -20],
    					position: 'right',
    					offset: [-45, 18],
    					fontFamily: 'Microsoft YaHei',
    				}
    			},
    			data: timeArray.planCompletionTimeArr,
    			/**
    			 * 柱状图宽度
    			 */
    			barWidth: 15,
    			barCategoryGap:'50%',
    		}
    	]
    };
    
    // 将构建好的配置项传入echarts
    myChart.setOption(option);
    
    /**
     * 时间对象转日期字符串 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;
    };
    
    /**
     * 获取阶段的计划内完成时间(蓝色柱状图值)
     * @param {Object} stage 阶段
     * @param {Object} stateBeginTime 阶段实际开始时间
     * @param {Object} stateCompletionTime 阶段实际完成时间
     * @param{Object} stagePlanCompletionTimeStr 阶段计划完成时间
     */
    function getOnTimeCompletionTime(stage, stageBeginTimeStr, stageCompletionTimeStr, stagePlanCompletionTimeStr) {
    	//	var validTimeMillis = getTimeMilliseconds(stageProgress.validTime);
    	var validTimeMillis = getTimeMilliseconds(stagePlanCompletionTimeStr);
    	var stageBeginTimeMillis = getTimeMilliseconds(stageBeginTimeStr);
    	var stageCompletionTimeMillis = getTimeMilliseconds(stageCompletionTimeStr);
    	if(validTimeMillis <= stageBeginTimeMillis) {
    		// 若阶段开始时间大于等于有效期,则项目超时完成,有效时间为'-'
    		return '-';
    	}
    	if(validTimeMillis > stageBeginTimeMillis && validTimeMillis < stageCompletionTimeMillis) {
    		// 若有效期介于阶段完成时间和阶段开始时间之间,则该阶段按时完后时间(实际该阶段是超时完成的)即蓝色柱状图的终值为有效期
    		return stagePlanCompletionTimeStr;
    	}
    	if(validTimeMillis >= stageCompletionTimeMillis) {
    		// 若有效期大于等于阶段完成时间,则阶段按时完成
    		return stageCompletionTimeStr;
    	}
    }
    
    /**
     * 获取阶段内的超时完成时间(红色色柱状图值)
     * @param {Object} stage 阶段
     * @param {Object} stateBeginTime
     * @param {Object} stateCompletionTime
     * @param{Object} stagePlanCompletionTimeStr 阶段计划完成时间
     */
    function getOverTimeCompletionTime(stage, stageBeginTimeStr, stageCompletionTimeStr, stagePlanCompletionTimeStr) {
    	//	var validTimeMillis = getTimeMilliseconds(stageProgress.validTime);
    	var validTimeMillis = getTimeMilliseconds(stagePlanCompletionTimeStr);
    	var stageBeginTimeMillis = getTimeMilliseconds(stageBeginTimeStr);
    	var stageCompletionTimeMillis = getTimeMilliseconds(stageCompletionTimeStr);
    
    	if(validTimeMillis < stageCompletionTimeMillis) {
    		// 阶段完成时间大于有效期,则将阶段完成时间作为超时时间返回
    		return stageCompletionTimeStr;
    	}
    
    	if(validTimeMillis >= stageCompletionTimeMillis) {
    		// 阶段完成时间小于等于有效期,则阶段按时完成,超时时间应为'-'
    		return '-';
    	}
    }
    
    /**
     * 根据时间字符串获取对应的毫秒值
     * @param {Object} timeStr 时间字符串
     */
    function getTimeMilliseconds(timeStr) {
    	return(new Date(timeStr)).getTime();
    }
    
    /**
     *获取时间坐标轴的起始和结束值
     */
    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) {
    		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;
    }

     

    展开全文
  • Excel图表模板-计划进度甘特图
  • 项目进度甘特图PPT模板素材下载。
  • 月份时间进度甘特图PPT模板下载。
  • 这是一张彩色英文12月份工作进度PPT甘特图,.PPTX格式;
  • 这是一张彩色线条一周工作进度PPT甘特图,.PPTX格式;
  • PMP备考进度甘特图,PMP学习备考计划 专业机构表,更好更合理的安排备考时间及学习计划
  • 工作计划进度甘特图
  • pmp甘特图
  • 【专享】《工作计划进度甘特图》.zip
  • 项目管理,进度计划,项目实施进度安排 自用的Excel格式实施计划,填入数据,自动生成甘特图,适合中小型项目管理使用
  • 使用jQueryGantt实现进度甘特图

    千次阅读 2018-11-27 15:39:02
      由于项目需要,需要一个进度甘特图。要求使用jQueryGantt来实现,在网上看了很多博客,终于找了几篇比较有用的。但是仅仅靠这几篇文章和一个demo无法满足项目的需求,所以研究了下jQueryGantt的源码,根据项目...

      由于项目需要,需要做一个进度甘特图。要求使用jQueryGantt来实现,在网上看了很多博客,终于找了几篇比较有用的。但是仅仅靠这几篇文章和一个demo无法满足项目的需求,所以研究了下jQueryGantt的源码,根据项目需求进行了一些改动。由于网上对于jQueryGantt的文章较少,很多都不完整,所以特此写下这篇文章进行记录,顺便分享有需要的人。

    1.jQueryGantt下载

    官方演示地址:taitems.github.io/jQuery.Gant… 我此次使用的是Github上一位网友发布的一个版本:github.com/ybx13579/jQ… 这是他写的一份指南:blog.csdn.net/yangbingx/a… 非常感谢,大家喜欢的记得给他一个star。

    2.运行

    这里的说明并不是很详细,具体参考test1文件夹中的index.html 将css,js,img放入工程项目中,并参考test1/index.html 中的引入。

    注:如果需要甘特图中显示中文,则需要在js文件引用中加上charset特性并设置为GB2312,否则中文内容将显示为乱码。
    <script src="js/jquery.fn.gantt.js" charset ="GB2312"></script>
    复制代码

    之后在html页面上写一个div

       <style type="text/css">
            body {
                font-family: Helvetica, Arial, sans-serif;
                font-size: 13px;
                padding: 0 0 50px 0;
            }
    
            .contain {
                width: 800px;
                margin: 0 auto;
            }
        </style>
    
    
        <div class="gantt_ot" style="width:800px; margin:100px auto;">
            <div class="gantt"></div>
        </div>
    复制代码

    复制相应的js方法

    <script type="text/javascript">
        $(function () {
            var sourceData;
            $.ajax({
                url: "/test/getGanttData", //请求后台,并返回甘特图需要的json数据
                type: "post",
                dataType: "json",
                cache: false, //关闭AJAX缓存
                data: {"id": 1}, //传入的查询参数
                success: function (data) {
    
                    //初始化gantt
                    $(".gantt").gantt({
                        source: data,
                        navigate: 'scroll', 底部使用拖拽的刻度尺还是按钮:scroll和buttons
                        scale: "weeks",// 这里是设置默认比例。这里总共有四个参数:months  weeks days  hours.
                        maxScale: "months", // 这里是设置最大比例。这里总共有四个参数:months  weeks days  hours.
                        minScale: "days", // 这里是设置最小比例。这里总共有四个参数:months  weeks days  hours.
                        itemsPerPage: 20, //设置甘特图每页显示多少条
                        onItemClick: function (data) {//有数据范围内的点击事件
                            // alert("Item clicked - show some details");
                        },
                        onAddClick: function (dt, rowId) {//无数据范围内的点击事件
                            // alert("Empty space clicked - add an item!");
                        },
                        onRender: function () {//渲染事件
                            if (window.console && typeof console.log === "function") {
                                // console.log("chart rendered");
                            }
                        }
                    });
                }
            });
    
            /*  //弹窗功能
              $(".gantt").popover({
                  selector: ".bar",
                  title: "I'm a popover",
                  content: "And I'm the content of said popover.",
                  trigger: "hover",
                  placement: "auto right"
              });*/
    
            //prettyPrint();
        });
    
    
    </script>
    复制代码

    JQueryGantt需要的Json数据格式

    [
    
                    {
                        name: "task1",
                        desc: "",
                        values: [{
                            from: "/Date(1320192000000)/", //这里需要的时间为毫秒
                //。计算方式为时间变量减去时间初始值(1970-1-1)的差值换算为毫秒
                              to: "/Date(1320592000000)/",
                            label: "这是label",
                            customClass: "ganttRed" //这里是这条记录的在甘特图上的颜色
                        }]
                    }, {
                        name: "task2",
                        desc: "这是描述",
                        values: [{
                            from: "/Date(1322611200000)/",
                            to: "/Date(1323302400000)/",
                            label: "",
                            customClass: "ganttGreen"
                        }]
                    }]
    复制代码

    如果你是使用的Java来编写的话,我这里提供一下后台处理json数据的思路。 首先,最外层的是一个List集合。然后里面的name,desc,values使用Map进行封装。而values里面又是一个集合,所以values整个呢诶容是一个List集合。而内部的from,to,label等参数又需要使用Map以[key,value]的形式保存。所以又要封装一个Map。

    灵魂画手已上线

    看了看自己画的图,还是贴代码吧。。。

    //SpringMvc Framwork
    @RequestMapping(value = "ganttData", method = RequestMethod.POST)
        @ResponseBody
        public String ganttData(String id) throws Exception {
              //查询出需要的多条进度数据
                List<Project> scheduleDataList = scheduleMgrService.selectScheduleDataListByPid(pt);
                //用于保存整个数据集
                List<Object> dataList = new ArrayList<>();
                //循环遍历,对数据进行重新封装
                for (ScheduleData data : scheduleDataList) {
                   //用于存放 name,desc,value三个对象
                    Map<String, Object> planItemMap = new HashMap<>();
                    planItemMap.put("name", data.getSd_process_name() + "(计划)");
                    planItemMap.put("desc", data.getSd_plan_remark());
                    //用于存放value中的from,to,label,customClass
                    Map<String, Object> valueMap = new HashMap<>();
                    valueMap.put("from", "/Date(" + data.getSd_plan_start_date().getTime() + ")/");
                    valueMap.put("to", "/Date(" + data.getSd_plan_end_date().getTime() + ")/");
                    valueMap.put("label", "");
                    valueMap.put("customClass", "ganttRed");
                    //将封装的value的map封装到一个List集合中去
                    List<Object> planValueList = new ArrayList<>();
                    planValueList.add(valueMap);
                    //将封装的value List集合添加到保存整个数据集的List里面去
                    planItemMap.put("values", planValueList);
                    dataList.add(planItemMap);
                    }
              //将封装的数据集转换为Json数组
               JSONArray array = JSONArray.fromObject(dataList);
                //将Json数组转换为字符串并返回给前端
                return array.toString();
    }
    复制代码

    3.修改jQueryGantt的核心文件来满足项目的需求

    问题:

    下载的Demo中的jQueryGantt是一个宽度为800px的甘特图,但是项目中需要将甘特图横向铺满,所以必须将宽度设置为100%。但是jQueryGantt顶部的时间刻度是根据数据的时间长度生成的,在数据时间比较短并且在横向铺满屏幕的情况下就会出现时间刻度不全的情况。

    当我只修改了div的宽度为自动或者100%的时候出现了上述情况。

        <div class="gantt_ot" style="width:auto; margin:100px auto;">
            <div class="gantt"></div>
        </div>
    复制代码

    解决方法:

    我不得不说,为了找到这个解决办法,花了点时间阅读源码,调试,修改这个jquery.fn.gantt.js来解决这个问题。(可能是自己太菜了 2333333)。

    (敲黑板) 重点来了~~ 思路:空出来的地方用一年的刻度时间应该能填满,所以只要将jQueryGantt根据数据中的结束时间计算刻度尺长度的时间追加一年,就会多出一年的刻度时间出来。 打开jquery.fn.gantt.js 找到getMaxDate这个function 在所有的getFullYear()方法后面都追加一个 +1 maxDate.getFullYear()+1

    贴出修改后的maxDate方法

    // Return the maximum available date in data depending on the scale
    //根据比例返回数据中的最大可用日期。
        getMaxDate: function (element) {
                    var maxDate = null;
                    $.each(element.data, function (i, entry) {
                        $.each(entry.values, function (i, date) {
                            maxDate = maxDate < tools.dateDeserialize(date.to) ? tools.dateDeserialize(date.to) : maxDate;
                        });
                    });
                    maxDate = maxDate || new Date();
                    var bd;
                    switch (settings.scale) {
                        case "hours":
                            maxDate.setHours(Math.ceil((maxDate.getHours()) / element.scaleStep) * element.scaleStep);
                            maxDate.setHours(maxDate.getHours() + element.scaleStep * 3);
                            break;
                        case "weeks":
                            // wtf is happening here?
                            bd = new Date(maxDate.getTime());
                            bd = new Date(bd.setDate(bd.getDate() + 3 * 7));
                            var md = Math.floor(bd.getDate() / 7) * 7;
                            maxDate = new Date(bd.getFullYear() + 1, bd.getMonth(), md === 0 ? 4 : md - 3);
                            break;
                        case "months":
                            bd = new Date(maxDate.getFullYear() + 1, maxDate.getMonth(), 1);
                            bd.setMonth(bd.getMonth() + 2);
                            maxDate = new Date(bd.getFullYear() + 1, bd.getMonth(), 1);
                            break;
                        case "days":
                        /* falls through */
                        default:
                            maxDate.setHours(0);
                            maxDate.setDate(maxDate.getDate() + 3);
                    }
                    return maxDate;
                },
    复制代码

    这时你再刷新一下页面就会发现空白部分的网格和时间刻度被填上了。像这个样子

    4.小优化

    虽然最麻烦的问题得以解决。但是用户体验上还存在一些可以优化的地方,例如:月份为英文,星期为英文,底部的控件没有居中,这样用户感觉不太好。接下来我们逐一解决这些问题。

    1.修改英文月份和周为中文。

    打开jquery.fn.gantt.js 找到var settings这个变量的定义的地方,这里是jQueryGantt初始化参数的位置。

    //Default settings 默认设置
    var settings = {
                source: [],
                holidays: [],
                // 默认的页数
                itemsPerPage: 7,
                // localisation
                // dow: ["S", "M", "T", "W", "T", "F", "S"],
                //面板上的星期,只能为一个字,两个字会被挤出来
                dow: ["日", "一", "二", "三", "四", "五", "六"],
                //面板上的月份
                months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
              //甘特图在渲染的时候显示的提示文字
                waitText: "加载中,请稍候...",
                // 底部使用拖拽的刻度尺还是按钮:scroll和buttons
                navigate: "buttons",
                //网格加载完毕后是否自动滚动到今天,true为是 false为不需要
                scrollToToday: false,
                // cookie options
                useCookie: false,
                cookieKey: "jquery.fn.gantt",
                // scale parameters
                scale: "days",
                maxScale: "months",
                minScale: "hours",
                // callbacks
                onItemClick: function (data) {
                    return;
                },
                onAddClick: function (dt, rowId) {
                    return;
                },
                onRender: $.noop
            };
    复制代码
    2.底部的控件居中

    打开jQueryGantt的style.css

    .gantt {
        width: 100%;
        margin: 0 auto; //清除掉上下间距
        border: 14px solid #ddd;
        position: relative;
        -webkit-border-radius: 6px;
       -moz-border-radius: 6px;
            border-radius: 6px;
      -webkit-box-sizing: border-box;
       -moz-box-sizing: border-box;
            box-sizing: border-box;
    }
    
    //设置底部控件位置(前端技术有限,就这样简单的处理了一下)
    .nav-slider {
        margin-left: 20%;
    }
    复制代码

    参考文章:

    blog.csdn.net/yangbingx/a… github.com/ybx13579/jQ… blog.csdn.net/weixin_4191… blog.csdn.net/weixin_4191… www.cnblogs.com/marksfly/p/… blog.csdn.net/l3922768721… taitems.github.io/jQuery.Gant… github.com/ybx13579/jQ…

    转载请注明出处及作者,谢谢。Create By Hiseico

    掘金个人主页

    展开全文
  • 在工作中,我经常需要对项目进度进行追踪,而在项目追踪中,我常用甘特图来进行追踪,以此来更好的展现工作完成情况的进度。 工作中,如果你列出这样的项目进度报告交给老板,不仅阅读起来不方便还会觉得视觉...

    图片

    作为一名新媒体运营者,与数据打交道可以说是家常便饭。

    在工作中,我经常需要对项目进度进行追踪,而在项目追踪中,我常用甘特图来进行追踪,以此来更好的展现工作完成情况的进度。

    图片

    工作中,如果你列出这样的项目进度报告交给老板,不仅阅读起来不方便还会觉得视觉疲惫,给老板留下一种不好印象。

    搞不好老板还会让你重新做一遍,别人准点下班,而你还在加班熬夜做表!

    图片

    但是如果你用甘特图来展示,看着不仅直观清晰,还让你的报告拉高N个档次。

    图片

    甘特图作为提升工作效率必备工具之一,我们如何快速制作出好看实用又不缺逼格的甘特图呢?

    为此,小编精心整理出这份《工作计划进度Excel甘特图模板》资源包送给大家,拿来即用,助你高效做出工作进度跟踪表!

    有了这套资源包,你也可以轻松做出让领导眼前一亮、同事羡慕的高逼格工作计划进度甘特图。

    图片

    下面,咱们一起来看看本期甘特图Excel模板福利预览吧~ 

    这份资源包都有什么?

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    图片

    以上就是本期《工作计划进度Excel甘特图》资源包部分福利展示,这只是冰山一角,文件过多,就不一一列举展示。


    制作整理不易,喜欢请支持,在领取福利学习 ~

    如何领取本期福利?

    关注[软考大讲堂]公众号

    进入公众号后台,回复 甘特图 

    领取甘特图Excel模板福利

    展开全文
  • 工作计划进度甘特图
  • 进度计划甘特图.xls

    2021-09-11 16:53:10
    进度计划甘特图

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,609
精华内容 2,643
关键字:

做活动进度甘特图