前言
最近工作中需要做一个自动配置报表的功能,需求目的是可以通过配置来适应产品对于报表需求的变更,不用重复更改代码。
配置实现流程如下:配置报表所需的数据源
选择筛选报表数据的参数
生成报表页面
生成展示页面(生成的报表通过选择放入展示页面)
项目是通过前后端分离实现的,前端框架是Vue,配合Echart绘制相关图表,因为我也是第一次尝试写前端组件,所以这里写下文章记录下。
特别提示:这里的组件不是通过webpack打包生成的插件,而是放在src/components文件夹下的组件
生成vue项目
这里通过vue-cli工具初始化一个项目,开始这里,默认你都已经安装好vue相关环境以及工具,如果没有,请自行百度vue环境搭建教程
初始化项目
新建一个存放新项目的文件夹,例如我这里在d盘下建了一个名为VueAndEcharts的文件夹
在上图中的地址框中直接输入cmd,则在当前路径下打开cmd.exe
在上图中输入初始化项目的命令:vue init webpack vue_project(最后这个是我创建的项目文件夹的名字),具体操作如下图:
上面依次的步骤会让你输入:项目名称:我这里输入echartsdemo
项目描述:this is a vue/echart demo
项目作者:martin
安装vue-route:选yes即可
安装eslint到你的代码,这个是检查代码格式的,选yes即可
后面的几个我暂时没管他,都选了no,不停回车,项目就创建成功了
生成成功后项目结构如下:
切换到项目根目录下,我这里是cd vueEchartsForReport 然后在在命令提示符中输入:npm i 此步骤安装vue项目需要的相关依赖包,(这一步也可以省略,直接输入npm run dev,也会安装相关依赖包)这部分依赖包在项目根目录下的package.json中可以看到,
再输入:npm run dev,就可以启动项目了,
上图代表已经启动项目成功,在浏览器输入http://localhost:8080就可以看到项目helloworld页面了
组件开发
在项目src/components文件夹下新建一个vue文件,
因为是一个demo,所以我这里命名为bardemo.vue,
1.模板文件代码
以上代码是为绘制图表准备一个具备高宽的 DOM 容器,具体明细可参考echarts官方文档,传送门:echarts官方文档
2.安装echarts插件
在项目根目录下,输入命令:npm install echarts --save,安装成功后,在bardemo.vue文件中引入
let echarts = require('echarts')
import _ from 'lodash'
我这里因为要使用lodash工具库的方法,所以这里也安装了lodash工具库,输入命令: npm install lodash --save即可安装,导入loadsh如上图:import _ from 'lodash'
export default {
name: 'bar-report',
props: {
data: {
required: true, // 若是横轴,则此部分为x轴"数值"数据,若为纵轴,则此部分为y轴"数值"数据,此部分数据必传 type: Object
},
title: {
type: Object | String // 标题,可以只传入标题,其他属性使用默认值,也可自定义title属性,默认无标题 },
theme: {
type: String // dom参数属性,传入theme字符串,theme暂时支持dark和light两种,默认light },
width: String, // dom的宽度,默认600 height: String, // dom的高度,默认400 barType: {
type: String // 柱状图类型/值为xAxis则显示横轴柱状图,值为yAxis则为纵轴柱状图,默认是yAxis },
category: {
required: true, // 图表分类,如是纵轴,则是x轴的值 type: Object
},
legend: {
required: true, // 必传,图表上方标识每个颜色柱/线代表什么意思 type: Array
},
dataView: {
type: Boolean // 是否显示数据视图功能,默认不开启 },
magicType: {
type: Array | String // 是否显示图表之间切换显示功能,默认不开启 },
restore: {
type: Boolean // 是否显示图表还原功能,默认不开启 },
saveAsImage: {
type: Boolean // 是否显示图表图表保存为图片功能,默认不开启 },
stack: {
type: String // 柱状图是否堆叠属性,默认不堆叠 },
seriesType: {
type: String // 控制是绘制柱状图,还是绘制折线图,”bar“:柱状图,”line“:折线图 }
},
data () {
// 默认title属性 let baseTitle = {
text: '', // 主标题 subtext: '', // 副标题 left: 'left', // 标题位置:left,center,right textStyle: {
// 文字颜色 color: '#000000',
// 字体风格,'normal','italic','oblique' fontStyle: 'normal',
// 字体粗细 'normal','bold','bolder','lighter',100 | 200 | 300 | 400... fontWeight: 'bold',
// 字体系列 fontFamily: 'sans-serif',
// 字体大小 fontSize: 18
}
}
// 如果传入了title的值,则判断处理后重新赋值给baseTitle const tempTitle = this.title
if (tempTitle) {
debugger
if (typeof (tempTitle) === 'string') {
baseTitle.text = tempTitle
} else if (typeof (tempTitle) === 'object') {
if (tempTitle.text) {
baseTitle.text = tempTitle.text
}
if (tempTitle.subtext) {
baseTitle.subtext = tempTitle.subtext
}
if (tempTitle.left) {
baseTitle.left = tempTitle.left
}
if (tempTitle.textStyle) {
// 标题字体颜色 if (tempTitle.textStyle.color) {
baseTitle.textStyle.color = tempTitle.textStyle.color
}
// 字体风格,'normal','italic','oblique' if (tempTitle.textStyle.fontStyle) {
baseTitle.textStyle.fontStyle = tempTitle.textStyle.fontStyle
}
// 字体粗细 'normal','bold','bolder','lighter',100 | 200 | 300 | 400... if (tempTitle.textStyle.fontWeight) {
baseTitle.textStyle.fontWeight = tempTitle.textStyle.fontWeight
}
// 字体系列 if (tempTitle.textStyle.fontFamily) {
baseTitle.textStyle.fontFamily = tempTitle.textStyle.fontFamily
}
// 字体大小 if (tempTitle.textStyle.fontSize) {
baseTitle.textStyle.fontSize = tempTitle.textStyle.fontSize
}
}
}
}
// 默认dom属性 let baseDom = {
theme: 'light',
renderer: 'canvas',
opts: {
width: 600,
height: 400
}
}
// 判断处理是否传入dom属性值 if (this.theme) {
baseDom.theme = this.theme
}
if (this.width) {
baseDom.opts.width = parseInt(this.width)
}
if (this.height) {
baseDom.opts.height = parseInt(this.height)
}
let baseType = 'yAxis'
if (this.barType) {
baseType = this.barType
}
// 默认toolbox值 var baseToolbox = {
show: false,
feature: {
mark: {show: true},
dataView: {show: false, readOnly: false}, // 数据视图 magicType: {show: false, type: []}, // 切换为折线图/柱状图 restore: {show: false}, // 还原 saveAsImage: {show: false} // 保存为图片 }
}
// 判断处理传入toolbox属性值 if (this.dataView) {
baseToolbox.show = true
baseToolbox.feature.dataView.show = true
}
if (this.magicType) {
baseToolbox.show = true
baseToolbox.feature.magicType.show = true
if (typeof (this.magicType) === 'string') {
baseToolbox.feature.magicType.type = [this.magicType]
} else {
baseToolbox.feature.magicType.type = this.magicType
}
}
if (this.restore) {
baseToolbox.show = true
baseToolbox.feature.restore.show = true
}
if (this.saveAsImage) {
baseToolbox.show = true
baseToolbox.feature.saveAsImage.show = true
}
return {
titleProperty: baseTitle, // title属性值 domProperty: baseDom, // dom属性值 type: baseType, // 图表横纵类型 toolboxProperty: baseToolbox// toolbox属性 }
},
// 页面加载 mounted () {
this.setEchart()
},
methods: {
// 绘制图表方法 setEchart () {
const _this = this
let domInit = this.$refs.mychart
this.myChart = echarts.init(domInit, _this.domProperty.theme, {width: _this.domProperty.opts.width, height: _this.domProperty.opts.height})
// 指定图表的配置项和数据 let option = {
title: {
text: _this.titleProperty.text,
subtext: _this.titleProperty.subtext,
left: _this.titleProperty.left,
textStyle: _this.titleProperty.textStyle
},
tooltip: {
trigger: 'axis'
},
toolbox: this.toolboxProperty,
legend: {
data: this.legend
},
series: [
]
}
// 判断传入图表类型是横轴图表还是纵轴图表 if (this.type === 'xAxis') {
const tempx = {
type: 'value'
}
option.xAxis = tempx
option.yAxis = this.category
} else if (this.type === 'yAxis') {
const tempy = {
type: 'value'
}
option.yAxis = tempy
option.xAxis = this.category
}
// series数据赋值并判断是否是堆叠图表 const seriesData = this.data
if (seriesData) {
_.forEach(this.legend, function (value) {
var myBarData = _.get(seriesData, value)
console.log(value)
if (_.hasIn(seriesData, value)) {
var tempSeries = {
name: value,
type: _this.seriesType,
data: myBarData
}
if (_this.stack) {
tempSeries.stack = _this.stack
}
option.series.push(tempSeries)
}
})
}
this.myChart.setOption(option)
}
}
}
上面三个代码片段在一起,就是这个测试demo的全部组件,具体实现,请看代码注释,下面说说在其他页面如何使用该组件
4.组件的使用
打开hellowork.vue文件,删除
内的所有代码,在其中加入下面代码:
:data="barData"
:category="categoryData"
:legend="legendData">
import barEcharts from './bardemo' //导入组件export default {
name: 'HelloWorld',
data () {
return {
// 图表数据 barData: {
'2011': [18203, 23489, 29034, 104970, 131744, 630230],
'2012': [19325, 23438, 31000, 121594, 134141, 681807]
},
focusType: 1,
//定义组件,在上面用:is使用,不需要components注册 currentView: barEcharts,
categoryData: {
data: ['巴西', '印尼', '美国', '印度', '中国', '世界人口(万)']
},
legendData: ['2011', '2012'],
}
}
}
上图是最基本使用,上图三个是必传的三个参数:保存文件,刷新页面后如下图:
下面我们加上标题,并且将表格展示区放大一点
修改为上面代码后,绘制的表格如下:
可看出图表多出了标题,并且宽度明显拉长了
如果我们需要修改标题颜色和字体,则需要传入一个json对象,具体实现如下:
:data="barData"
:title="echarttitle"
width="1000"
height="400"
:category="categoryData"
:legend="legendData">
import barEcharts from './bardemo' //导入组件export default {
name: 'HelloWorld',
data () {
return {
// 图表数据 barData: {
'2011': [18203, 23489, 29034, 104970, 131744, 630230],
'2012': [19325, 23438, 31000, 121594, 134141, 681807]
},
focusType: 1,
//定义组件,在上面用:is使用,不需要components注册 currentView: barEcharts,
categoryData: {
data: ['巴西', '印尼', '美国', '印度', '中国', '世界人口(万)']
},
legendData: ['2011', '2012'],
echarttitle: {
text: '世界人口总量',
left: 'right',
textStyle: {
color: '#FF0000',
// 字体大小 fontSize: 20 // 默认大小是18 }
}
}
}
}
上面代码将图表标题字体颜色设置为红色,并修改字体大小为20,位置为靠右,展示结果如下图:
还可以设置字体的风格、字体的粗细、字体系列等样式:具体配置如下
import barEcharts from './bardemo' // 导入组件export default {
name: 'HelloWorld',
data () {
return {
// 图表数据 barData: {
'2011': [18203, 23489, 29034, 104970, 131744, 630230],
'2012': [19325, 23438, 31000, 121594, 134141, 681807]
},
focusType: 1,
// 定义组件,在上面用:is使用,不需要components注册 currentView: barEcharts,
categoryData: {
data: ['巴西', '印尼', '美国', '印度', '中国', '世界人口(万)']
},
legendData: ['2011', '2012'],
echarttitle: {
text: '世界人口总量',
left: 'right',
textStyle: {
color: '#FF0000',
// 字体风格,'normal','italic','oblique' fontStyle: 'normal',
// 字体粗细 'normal','bold','bolder','lighter',100 | 200 | 300 | 400... fontWeight: 'bold',
// 字体系列 fontFamily: 'sans-serif',
// 字体大小 fontSize: 18
}
}
}
}
}
下面我们开启数据视图、折线图/柱状图切换、还原、保存图表为图片:
组件代码修改为如下,新增了四个prop属性赋值:dataView、magicType、restore、saveAsImage
:data="barData"
:title="echarttitle"
width="1000"
height="400"
:category="categoryData"
:legend="legendData"
:dataView="openMagicType"
:magicType="openMagicType"
:restore="openRestore"
:saveAsImage="openSaveAsImage">
定义数据代码如下:
import barEcharts from './bardemo' // 导入组件export default {
name: 'HelloWorld',
data () {
return {
// 图表数据 barData: {
'2011': [18203, 23489, 29034, 104970, 131744, 630230],
'2012': [19325, 23438, 31000, 121594, 134141, 681807]
},
focusType: 1,
// 定义组件,在上面用:is使用,不需要components注册 currentView: barEcharts,
categoryData: {
data: ['巴西', '印尼', '美国', '印度', '中国', '世界人口(万)']
},
legendData: ['2011', '2012'],
echarttitle: {
text: '世界人口总量',
left: 'left',
textStyle: {
color: '#FF0000',
// 字体风格,'normal','italic','oblique' fontStyle: 'normal',
// 字体粗细 'normal','bold','bolder','lighter',100 | 200 | 300 | 400... fontWeight: 'bold',
// 字体系列 fontFamily: 'sans-serif',
// 字体大小 fontSize: 18
}
},
openDataView: true, // dataView openMagicType: 'line', // magicType可以是字符串,也可以是字符串数组,例如传入['bar','line']就是可以切换为折线图和柱状图 openRestore: true, // restore openSaveAsImage: true // saveAsImage }
}
}
运行结果展示:
点击数据视图:
点击切换为折线图:
点击还原:上面的折线图又切换回柱状图
点击保存为图片:下载一张当前图表的图片
也可以设置柱状图使用堆叠模式展示,堆叠模式需要设置stack属性,设置stack=“总量”,则会展示堆叠的柱状图:
:data="barData"
:title="echarttitle"
width="1000"
height="400"
:category="categoryData"
:legend="legendData"
:dataView="openMagicType"
:magicType="openMagicType"
:restore="openRestore"
:saveAsImage="openSaveAsImage"
stack="总量"
seriesType="bar">
保存展示结果如下:
如果要使用该组件默认绘制折线图,则需要设置stack属性,这里stack属性默认是柱状图,设置seriesType=“line”,则会绘制折线图
:data="barData"
:title="echarttitle"
width="1000"
height="400"
:category="categoryData"
:legend="legendData"
:dataView="openMagicType"
:magicType="openMagicType"
:restore="openRestore"
:saveAsImage="openSaveAsImage"
seriesType="line">
保存后页面刷新如下: