「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。
一.功能描述
步骤1
分别点击 “x轴字段”,“y轴字段” 下拉框选择需要画图的字段(x轴只支持一个,y轴最多支持2个),如图所示:
步骤2
点击 “添加画图信息” 按钮,弹出弹框,填写图表的名字(后续将作为图表的 title 进行展示)及备注信息,如图所示:
步骤3
点击 “确认画图” 按钮获取数据进行画图,点击 “取消画图” 按钮则取消画图并关闭弹框及初始化数据,如图所示:
步骤4
对于画好的图,可以通过图表右下角的下拉框选择公式进行数据统计拟合,默认不拟合,如图所示:
步骤5
如果是多张图,还可以通过图表正上方的下拉框选择相应的图表进行切换展示,如图所示:
以上步骤1-3按理来说可无限画图,但是最终页面只允许展示最多4张图表,当超过4张图表时,可按步骤5的方式进行查看;结合实际项目需求,默认需要展示的图表,我们才会通过接口请求数据进行画图,不需要展示的图表,当选择它时才会去通过接口请求数据进行画图;这样可以减少接口请求的次数,提高性能。在这里我使用的是本地数据,不存在这样的问题。
二、依赖
- Vue
- Element ui
- ECharts
三、功能实现
1.本地画图数据 chartData.json
{
"data":[
[
[6, 82],
[9, 110],
[12, 140],
[15, 153],
[18, 160],
[21, 165],
[24, 172],
[27, 175],
[30, 178],
[33, 180],
[36, 183],
[39, 186],
[42, 188],
[45, 188],
[48, 190],
[51, 190],
[54, 188],
[57, 188],
[60, 188],
[63, 183],
[66, 183],
[69, 180],
[72, 178],
[75, 175],
[78, 170],
[81, 170],
[84, 168],
[87, 168],
[90, 165]
],[
[6, 35],
[9, 56],
[12, 62],
[15, 78],
[18, 89],
[21, 92],
[24, 93],
[27, 95],
[30, 96],
[33, 98],
[36, 102],
[39, 105],
[42, 108],
[45, 110],
[48, 115],
[51, 120],
[54, 125],
[57, 135],
[60, 140],
[63, 130],
[66, 130],
[69, 126],
[72, 120],
[75, 120],
[78, 118],
[81, 115],
[84, 110],
[87, 102],
[90, 98]
]
]
}
2.HTML代码
<template>
<div class="app-container">
<div class="div1 normalDivStyle">
<el-form>
<el-form-item label="X轴字段:">
<el-select
v-model="xColumn"
clearable
placeholder="请选择"
style="width:100%"
>
<el-option
v-for="(item,index) in xColumnList"
:key="index"
:label="item"
:value="index"
/>
</el-select>
</el-form-item>
<el-form-item label="Y轴字段:">
<el-select
v-model="yColumn"
multiple
clearable
placeholder="请选择"
style="width:100%"
>
<el-option
v-for="(item,index) in yColumnList"
:key="index"
:label="item"
:value="index"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button
style="width:100%;margin-bottom:20px"
type="danger"
size="small"
:disabled="xColumn!=='' && yColumn.length > 0 ? false : true"
@click="addChartDialog=true"
>
添加画图信息</el-button>
</el-form-item>
</el-form>
</div>
<!-- 图表面板 -->
<div class="div2 normalDivStyle">
<!-- 图1 -->
<div class="myChart">
<div class="myChartSelect">
<el-select
v-model="chartValue1"
placeholder="请选择"
style="width:100%"
@change="chartOnChange1(chartValue1,0,1)"
>
<el-option
v-for="(item,index) in showData"
:key="index"
:label="item.chartName"
:value="item.chartName"
/>
</el-select>
</div>
<div id="myChart1" class="myChartCon" />
<div class="myChartBtn">
<el-select
v-model="orderValue1"
placeholder="请选择"
style="width:100%"
@change="orderOnChange1"
>
<el-option
v-for="(item,index) in orderList"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
<!-- 图2 -->
<div class="myChart">
<div class="myChartSelect">
<el-select
v-model="chartValue2"
placeholder="请选择"
style="width:100%"
@change="chartOnChange2(chartValue2,0,1)"
>
<el-option
v-for="(item,index) in showData"
:key="index"
:label="item.chartName"
:value="item.chartName"
/>
</el-select>
</div>
<div id="myChart2" class="myChartCon" />
<div class="myChartBtn">
<el-select
v-model="orderValue2"
placeholder="请选择"
style="width:100%"
@change="orderOnChange2"
>
<el-option
v-for="(item,index) in orderList"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
<!-- 图3 -->
<div class="myChart">
<div class="myChartSelect">
<el-select
v-model="chartValue3"
placeholder="请选择"
style="width:100%"
@change="chartOnChange3(chartValue3,0,1)"
>
<el-option
v-for="(item,index) in showData"
:key="index"
:label="item.chartName"
:value="item.chartName"
/>
</el-select>
</div>
<div id="myChart3" class="myChartCon" />
<div class="myChartBtn">
<el-select
v-model="orderValue3"
placeholder="请选择"
style="width:100%"
@change="orderOnChange3"
>
<el-option
v-for="(item,index) in orderList"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
<!-- 图4 -->
<div class="myChart">
<div class="myChartSelect">
<el-select
v-model="chartValue4"
placeholder="请选择"
style="width:100%"
@change="chartOnChange4(chartValue4,0,1)"
>
<el-option
v-for="(item,index) in showData"
:key="index"
:label="item.chartName"
:value="item.chartName"
/>
</el-select>
</div>
<div id="myChart4" class="myChartCon" />
<div class="myChartBtn">
<el-select
v-model="orderValue4"
placeholder="请选择"
style="width:100%"
@change="orderOnChange4"
>
<el-option
v-for="(item,index) in orderList"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</div>
</div>
<!-- 添加图表信息弹框 -->
<el-dialog
:visible.sync="addChartDialog"
title="添加画图信息"
:close-on-click-modal="false"
@close="closeDialog('addChartForm')"
>
<el-form
ref="addChartForm"
:model="addChartForm"
label-width="120px"
:rules="rules"
>
<el-form-item style="margin-bottom: 20px;" label="图表名称" prop="chartName">
<el-input v-model="addChartForm.chartName" placeholder="请输入图表名称" />
</el-form-item>
<el-form-item label="备注">
<el-input v-model="addChartForm.remarks" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div style="text-align:right;">
<el-button type="danger" @click="closeDialog('addChartForm')">取消画图</el-button>
<el-button type="primary" @click="addChartConfirm('addChartForm')">确认画图</el-button>
</div>
</el-dialog>
</div>
</template>
3.CSS代码
<style lang="scss">
.normalDivStyle{
float: left;
background-color: #fff;
padding: 10px;
box-shadow: 0 1px 1px rgb(0 0 0 / 10%);
border-top: 3px solid #d2d6de;
}
// 私有样式
.div1{
width: 14.5%;
position: absolute;
left: 20px;
top: 20px;
bottom: 20px;
}
.div2{
width: 82.5%;
position: absolute;
right: 20px;
top: 20px;
bottom: 20px;
.myChart{
float: left;
height: 50%;
width: 50%;
padding: 10px;
border: 1px solid #EBEEF5;
text-align: center;
position: relative;
.myChartCon{
width: 100%;
height: 98%;
}
.myChartSelect{
position: absolute;
top: 10px;
left: 30%;
width: 40%;
text-align: right;
z-index: 1;
.el-select .el-input__inner{
text-align: center;
font-weight: bold;
}
}
.myChartBtn{
position: absolute;
right: 10px;
bottom: 10px;
text-align: right;
width: 120px;
}
}
}
</style>
4.JS代码
<script>
import { EleResize } from '@/utils/esresize'// 图表自适应
import ecStat from 'echarts-stat'
import chartData from '/static/chartData.json'// 引入本地画图数据
export default {
data() {
return {
// 选择画图参数
xColumnList: {
'age': '年龄'
},
yColumnList: {
'weight': '体重',
'height': '身高'
},
xColumn: '',
yColumn: [],
// 添加图表信息弹框
addChartDialog: false,
addChartForm: {
chartName: '',
remarks: ''
},
// 页面需要显示的图表信息
showData: [],
// 拟合下拉数据
orderList: [{
label: '不拟合',
value: 0
}, {
label: '1阶多项式',
value: 1
}, {
label: '2阶多项式',
value: 2
}, {
label: '3阶多项式',
value: 3
}, {
label: '4阶多项式',
value: 4
}, {
label: '5阶多项式',
value: 5
}],
orderValue1: 0,
orderValue2: 0,
orderValue3: 0,
orderValue4: 0,
// 画图下拉选择
chartValue1: '',
chartValue2: '',
chartValue3: '',
chartValue4: '',
// 表单验证规则
rules: {
chartName: [
{ required: true, message: '图表名称不能为空', trigger: 'blur' },
{ pattern: /^\S+$/, message: '不允许有空格', trigger: 'blur' }
]
}
}
},
methods: {
// 确认画图
addChartConfirm(formName) {
this.$refs[formName].validate(valid => {
if (valid) {
var legendData = [] // 显示的标签
for (var index in this.yColumn) {
legendData.push(this.yColumnList[this.yColumn[index]])
}
var resultData = [] // 画图数据
if (this.yColumn.length === 2) { // 双y轴数据
resultData = chartData.data
} else { // 单y轴数据
if (this.yColumn[0] === 'height') {
resultData.push(chartData.data[0])
} else {
resultData.push(chartData.data[1])
}
}
// 画图数据,多张图
this.showData.push({
chartName: this.addChartForm.chartName,
chartData: resultData,
legendData: legendData,
xLabel: this.xColumnList[this.xColumn]
})
// 画图的公共方法
this.drawChart(this.showData, 0)
}
})
},
// 画图的公共方法
drawChart(showData, order) {
if (showData.length > 0) {
for (var i in this.showData) {
if (Number(i) < 4) {
var mychart = 'myChart' + (Number(i) + 1)
this.initCharts(showData[i].chartName, showData[i].legendData, showData[i].chartData, showData[i].xLabel, order, document.getElementById(mychart))
// 图表下拉框默认值
if (i === '0') {
this.chartValue1 = this.showData[i].chartName
} else if (i === '1') {
this.chartValue2 = this.showData[i].chartName
} else if (i === '2') {
this.chartValue3 = this.showData[i].chartName
} else if (i === '3') {
this.chartValue4 = this.showData[i].chartName
}
this.addChartDialog = false
} else {
break
}
}
}
},
// 画图的基本配置信息
initCharts(chartName, legendData, data, xLabel, order, domName) {
var myChart
if (myChart !== null && myChart !== '' && myChart !== undefined) {
myChart.dispose()// 图表销毁
}
// 基于准备好的dom,初始化echarts实例
myChart = this.$echarts.init(domName)
// 图表自适应
const listener = function() {
myChart.resize()
}
EleResize.on(domName, listener)
// 指定图表的配置项和数据
const colors = ['#5470C6', '#91CC75', '#000', '#fbd379']
const colors1 = ['#003fff', '#37a900']
const position = ['left', 'right']
var yAxis = []
var series = []
for (var i in legendData) {
// y轴配置(最多2个)
yAxis.push({
type: 'value',
name: legendData[i],
position: position[i],
axisLine: {
show: true,
lineStyle: {
color: colors[i]
}
},
axisLabel: {
formatter: legendData[i] === '身高' ? '{value} cm' : '{value} 斤'
}
})
// y轴数据
series.push({
name: legendData[i],
data: data[i],
type: 'scatter',
yAxisIndex: i, // 指定哪个y轴
emphasis: {
focus: 'series',
label: {
show: true,
formatter: function(param) {
return param.data[2]
},
position: 'top'
}
},
itemStyle: {
normal: {
color: colors[i]
}
}
})
// 数据拟合
if (order !== 0) {
// polynomial:多项式回归
var myRegression = ecStat.regression('polynomial', data[i], order)
// 将内部数据进行从小到大排序
myRegression.points.sort(function(a, b) {
return a[0] - b[0]
})
series.push({
name: 'line',
type: 'line',
smooth: true,
data: myRegression.points,
yAxisIndex: i, // 指定哪个y轴
symbolSize: 0.1,
symbol: 'circle',
itemStyle: {
normal: {
lineStyle: {
color: colors1[i]
}
}
},
markPoint: {
itemStyle: {
color: 'transparent'
},
label: {
show: true,
position: 'left',
formatter: myRegression.expression,
color: colors[i],
fontSize: 14
},
data: [{
coord: myRegression.points[myRegression.points.length - 1]
}]
}
})
}
}
var option = {
title: {
text: chartName,
left: 'center'
},
tooltip: { // 提示框配置
trigger: 'item', // 触发类型:'item'图形触发(散点图,饼图等无类目轴的图表中使用); 'axis'坐标轴触发;'none':什么都不触发。
axisPointer: { // 坐标轴指示器配置项。
type: 'cross', // 'line':直线指示器, 'shadow':阴影指示器,'none':无指示器,'cross':十字准星指示器。
axis: 'auto', // 指示器的坐标轴。
snap: true, // 坐标轴指示器是否自动吸附到点上
crossStyle: {
color: '#999'
}
},
formatter: function(arg) {
var message = ''
if (arg.componentType === 'series') {
message = xLabel + ':' + arg.data[0] + '<br>' + arg.seriesName + ':' + arg.data[1]
} else {
message = xLabel + ':' + arg.data.coord[0] + '<br>' + arg.seriesName + ':' + arg.data.coord[1]
}
return message
}
},
legend: {
data: legendData,
bottom: -5
},
xAxis: [
{
type: 'value',
name: xLabel,
nameGap: 35,
nameLocation: 'middle',
nameTextStyle: {
fontWeight: 700,
color: colors[2]
},
axisPointer: {
type: 'shadow'
}
}
],
yAxis: yAxis,
series: series
}
// 清除图表,必须加,否则数据不拟合时没效果
myChart.clear()
// 绘制图表
option && myChart.setOption(option)
},
// 下拉框1
chartOnChange1(val, order, type) {
this.showData.filter(item => {
if (val === item.chartName) {
this.initCharts(item.chartName, item.legendData, item.chartData, item.xLabel, order, document.getElementById('myChart1'))
}
})
if (type === 1) {
this.orderValue1 = 0
}
},
// 数据拟合
orderOnChange1(val) {
this.orderValue1 = val
this.chartOnChange1(this.chartValue1, this.orderValue1, 0)
},
// 下拉框2
chartOnChange2(val, order, type) {
this.showData.filter(item => {
if (val === item.chartName) {
this.initCharts(item.chartName, item.legendData, item.chartData, item.xLabel, order, document.getElementById('myChart2'))
}
})
if (type === 1) {
this.orderValue2 = 0
}
},
// 数据拟合
orderOnChange2(val) {
this.orderValue2 = val
this.chartOnChange2(this.chartValue2, this.orderValue2, 0)
},
// 下拉框3
chartOnChange3(val, order, type) {
this.showData.filter(item => {
if (val === item.chartName) {
this.initCharts(item.chartName, item.legendData, item.chartData, item.xLabel, order, document.getElementById('myChart3'))
}
})
if (type === 1) {
this.orderValue3 = 0
}
},
// 数据拟合
orderOnChange3(val) {
this.orderValue3 = val
this.chartOnChange3(this.chartValue3, this.orderValue3, 0)
},
// 下拉框4
chartOnChange4(val, order, type) {
this.showData.filter(item => {
if (val === item.chartName) {
this.initCharts(item.chartName, item.legendData, item.chartData, item.xLabel, order, document.getElementById('myChart4'))
}
})
if (type === 1) {
this.orderValue4 = 0
}
},
// 数据拟合
orderOnChange4(val) {
this.orderValue4 = val
this.chartOnChange4(this.chartValue4, this.orderValue4, 0)
},
// 关闭弹框
closeDialog(formName) {
// 重置表单验证规则
this.$refs[formName].resetFields()
this.addChartForm.chartName = ''
this.addChartForm.remarks = ''
this.xColumn = ''
this.yColumn = []
this.orderValue1 = 0
this.orderValue2 = 0
this.orderValue3 = 0
this.orderValue4 = 0
this.addChartDialog = false
}
}
}
</script>