Vue 中使用 Echarts 实现项目进度甘特图
简易版
1. 项目中安装 echarts
npm i echarts
2. 实现甘特图
<template>
<div id="app">
<!-- Echarts 图表 -->
<div ref="progressChart" class="progressChart"></div>
</div>
</template>
<script>
import * as echarts from 'echarts'
export default {
name: 'App',
mounted() {
this.initChart()
},
methods: {
/* 初始化图表 */
initChart() {
const progressChart = echarts.init(this.$refs.progressChart)
const option = {
// 鼠标移入提示工具
tooltip: {
trigger: 'axis',
formatter(params) {
if (params[1].data && params[0].data) {
return `<div>开始时间:${params[1].data}</div>` + `<div>结束时间:${params[0].data}</div>`
} else {
return ''
}
},
axisPointer: {
type: 'shadow'
}
},
grid: {
containLabel: true,
show: false,
right: 80,
left: 40,
bottom: 40,
top: 20,
backgroundColor: '#fff'
},
legend: {
// 图例组件
data: ['持续时间'],
align: 'auto',
top: 'bottom'
},
xAxis: {
type: 'time',
position: 'top', // x 轴位置
axisTick: {
// 隐藏刻度
show: false
},
axisLine: {
// 隐藏轴线
show: false
},
splitLine: {
// 显示网格线
show: true
}
},
yAxis: {
inverse: true, // y 轴数据翻转,该操作是为了保证项目一放在最上面,项目七在最下面
axisTick: {
// 隐藏刻度
show: false
},
axisLine: {
// 隐藏轴线
show: false
},
data: ['项目一', '项目二', '项目三', '项目四', '项目五', '项目六', '项目七']
},
series: [
{
name: '持续时间',
type: 'bar',
stack: 'duration',
itemStyle: {
color: '#007acc',
borderColor: '#fff',
borderWidth: 1
},
zlevel: -1,
data: ['2021-01-31', '2021-02-25', '2021-03-25', '2021-04-01', '2021-04-10', '2021-05-25', '2021-07-25'] // 结束时间
},
{
name: '持续时间',
type: 'bar',
stack: 'duration', // 堆叠标识符,同个类目轴上系列配置相同的 stack 值可以堆叠放置
itemStyle: {
color: '#fff'
},
zlevel: -1, // zlevel 大的 Canvas 会放在 zlevel 小的 Canvas 的上面
z: 9, // z值小的图形会被z值大的图形覆盖,z相比zlevel优先级更低,而且不会创建新的 Canvas
data: ['2021-01-01', '2021-01-31', '2021-02-25', '2021-03-25', '2021-04-01', '2021-04-10', '2021-05-25'] // 开始时间
}
]
}
progressChart.setOption(option)
// 浏览器窗口大小变化,图表大小自适应
window.addEventListener('resize', () => {
progressChart.resize()
})
}
}
}
</script>
<style scoped>
#app {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.progressChart {
width: 60%;
height: 420px;
border: 1px solid #aaa;
}
</style>
3. 效果展示
附加效果
说明:
- 附加效果都是在简易版的基础上改进完成的。
- 具体修改位置需看代码前的
+
、-
号标识符。+
为新增的代码,-
为删除的代码。
效果一:各柱子指定不同颜色
const option = {
legend: {
- data: ['持续时间'],
+ data: [
+ {
+ name: '持续时间',
+ itemStyle: {
+ color: '#000'
+ }
+ }
+ ], // 给 legend 图例自定义颜色
align: 'auto',
top: 'bottom'
},
+ color: ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'purple'], // 自定义调色盘的颜色
series: [
{
name: '持续时间',
type: 'bar',
stack: 'duration',
+ colorBy: 'data', // 让数据项 data 每一项的颜色根据调色盘中的颜色按顺序进行分配
itemStyle: {
- // color: '#007acc', // 这一行固定的颜色值需要删除或注释
borderColor: '#fff',
borderWidth: 1
},
zlevel: -1,
data: ['2021-01-31', '2021-02-25', '2021-03-25', '2021-04-01', '2021-04-10', '2021-05-25', '2021-07-25']
}
]
}
效果展示
效果二:柱子右侧添加自定义文字
+ const startTimeData = ['2021-01-01', '2021-01-31', '2021-02-25', '2021-03-25', '2021-04-01', '2021-04-10', '2021-05-25']
+ const endTimeData = ['2021-01-31', '2021-02-25', '2021-03-25', '2021-04-01', '2021-04-10', '2021-05-25', '2021-07-25']
const option = {
grid: {
containLabel: true,
show: false,
- right: 80,
+ right: 140, // 增加 grid 距离右侧的距离以显示完整文字信息
left: 40,
bottom: 40,
top: 20,
backgroundColor: '#fff'
},
series: [
{
// ...
+ label: {
+ show: true,
+ position: 'right',
+ color: '#999',
+ formatter: params => {
+ const findIndex = endTimeData.findIndex(item => item === params.value)
+ const startTime = startTimeData[findIndex]
+ return `开始时间:${startTime}\n\n结束时间:${params.value}`
+ } // 格式化 label
+ },
zlevel: -1,
- data: ['2021-01-31', '2021-02-25', '2021-03-25', '2021-04-01', '2021-04-10', '2021-05-25', '2021-07-25'] // 结束时间
+ data: endTimeData // 结束时间
},
{
// ...
zlevel: -1, // zlevel 大的 Canvas 会放在 zlevel 小的 Canvas 的上面
z: 9, // z值小的图形会被z值大的图形覆盖,z相比zlevel优先级更低,而且不会创建新的 Canvas
- data: ['2021-01-01', '2021-01-31', '2021-02-25', '2021-03-25', '2021-04-01', '2021-04-10', '2021-05-25'] // 开始时间
+ data: startTimeData // 开始时间
}
]
}
效果展示
效果三:当开始时间与结束时间恰为同一天时,渲染柱子
当开始时间与结束时间恰好是同一天时,会出现柱子无法显示的问题,这是由于两个数据项重合导致的。我们可以将时间格式映射为带有时分秒的形式,假设开始时间与结束时间都是 2021-01-01,我们可以将开始时间修正为 2021-01-01 00:00:00,结束时间修正为 2021-01-01 23:59:59,这样就可以保证柱子正常显示了。同时为了确保鼠标移入时显示的时间不带时分秒,我们在 formatter 中将时分秒去除掉即可。
<template>
<div id="app">
<!-- Echarts 图表 -->
<div ref="progressChart" class="progressChart"></div>
</div>
</template>
<script>
import * as echarts from 'echarts'
export default {
name: 'App',
mounted() {
this.initChart()
},
methods: {
/* 初始化图表 */
initChart() {
const progressChart = echarts.init(this.$refs.progressChart)
+ const startTimeData = ['2021-01-01', '2021-01-31', '2021-02-25', '2021-03-25', '2021-04-01', '2021-04-10', '2021-05-25'].map(item => item + ' 00:00:00')
+ const endTimeData = ['2021-01-01', '2021-01-31', '2021-02-25', '2021-04-01', '2021-04-10', '2021-05-25', '2021-07-25'].map(item => item + ' 23:59:59')
const option = {
// 鼠标移入提示工具
tooltip: {
trigger: 'axis',
formatter(params) {
if (params[1].data && params[0].data) {
- return `<div>开始时间:${params[1].data}</div>` + `<div>结束时间:${params[0].data}</div>`
+ // 去除时分秒
+ return `<div>开始时间:${params[1].data.split(' ')[0]}</div>` + `<div>结束时间:${params[0].data.split(' ')[0]}</div>`
} else {
return ''
}
},
axisPointer: {
type: 'shadow'
}
},
grid: {
containLabel: true,
show: false,
right: 80,
left: 40,
bottom: 40,
top: 20,
backgroundColor: '#fff'
},
legend: {
// 图例组件
data: ['持续时间'],
align: 'auto',
top: 'bottom'
},
xAxis: {
type: 'time',
position: 'top', // x 轴位置
axisTick: {
// 隐藏刻度
show: false
},
axisLine: {
// 隐藏轴线
show: false
},
splitLine: {
// 显示网格线
show: true
}
},
yAxis: {
inverse: true, // y 轴数据翻转,该操作是为了保证项目一放在最上面,项目七在最下面
axisTick: {
// 隐藏刻度
show: false
},
axisLine: {
// 隐藏轴线
show: false
},
data: ['项目一', '项目二', '项目三', '项目四', '项目五', '项目六', '项目七']
},
series: [
{
name: '持续时间',
type: 'bar',
stack: 'duration',
itemStyle: {
color: '#007acc',
borderColor: '#fff',
borderWidth: 1
},
zlevel: -1,
- data: ['2021-01-31', '2021-02-25', '2021-03-25', '2021-04-01', '2021-04-10', '2021-05-25', '2021-07-25'] // 结束时间
+ data: endTimeData // 结束时间 // 结束时间
},
{
name: '持续时间',
type: 'bar',
stack: 'duration', // 堆叠标识符,同个类目轴上系列配置相同的 stack 值可以堆叠放置
itemStyle: {
color: '#fff'
},
zlevel: -1, // zlevel 大的 Canvas 会放在 zlevel 小的 Canvas 的上面
z: 9, // z值小的图形会被z值大的图形覆盖,z相比zlevel优先级更低,而且不会创建新的 Canvas
- data: ['2021-01-01', '2021-01-31', '2021-02-25', '2021-03-25', '2021-04-01', '2021-04-10', '2021-05-25'] // 开始时间
+ data: startTimeData // 开始时间
}
]
}
progressChart.setOption(option)
// 浏览器窗口大小变化,图表大小自适应
window.addEventListener('resize', () => {
progressChart.resize()
})
}
}
}
</script>
<style scoped>
#app {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.progressChart {
width: 60%;
height: 420px;
border: 1px solid #aaa;
}
</style>
效果展示