主页面
介绍
echarts的事例页面,导入数据比较麻烦,此项目从excel中提取数据(含过滤数据),以注入页面.
代码阐明
- 全部的需要从excel中读取的参数,从代码中替换.需以{{data}} 包罗在内
- 使用绘制参数的剖析代码参数可以剖析出来全部参数
- 数据配置上传文件后,可以选择列
- 数据过滤条件设定后,点击绘制,此时才会过滤数据
- 代码块现实是返回echarts.options的信息,下面给了多个事例
- 图表设置现实没有使用,需要后端支持才可以,将编辑好的代码生存到数据库,大概取出
- 其他的参数,比方绘制参数,建议直接编辑代码完成,而且建议以变量的形式填写在最上方,以提供出来
页面代码 commonChart.vue
- <template>
- <div class="app-container" style="height: 100vh;">
- <el-row style="height: 100%;">
- <el-col :span="10" style="height: 100%;">
- <el-tabs type="border-card" style="height: 100%;">
- <el-tab-pane label="图表设置" style="height: 100%;">
- <el-form ref="elForm" :model="chartForm" label-width="80px" style="height: 100%;">
- <el-form-item label="选择图表" prop="chartType" >
- <el-select v-model="chartForm.chartType" placeholder="请选择选择图表" clearable
- :style="{width: '100%'}">
- <el-option v-for="(item, index) in chartFormOptions" :key="index" :label="item.label"
- :value="item.value" :disabled="item.disabled"></el-option>
- </el-select>
- </el-form-item>
- </el-form>
- </el-tab-pane>
- <el-tab-pane label="代码编辑" style="height: 100%;">
- <vue-ace-editor v-model="chartForm.customChartCode" ref="aceEditor"
- :width="'100%'"
- :height="'100%'"
- mode='javascript'
- theme='github'/>
- </el-tab-pane>
- <el-tab-pane label="数据配置">
- <el-form :model="dataForm" ref="queryForm" :inline="false" label-width="80px">
- <el-form-item label="上传文件">
- <el-upload
- class="upload-demo"
- action=""
- :before-upload="handleBeforeUpload"
- :file-list="dataForm.fileList"
- :show-file-list="false">
- <el-button size="mini" slot="trigger" type="primary">XLSX文件</el-button>
- </el-upload>
- </el-form-item>
- <el-form-item label="数据过滤">
- <el-table :data="dataForm.dataFilter" style="width: 100%">
- <!-- 选择变量 -->
- <el-table-column label="选择变量" prop="variable">
- <template v-slot="{ row, $index }">
- <el-select v-model="row.variable" placeholder="请选择变量">
- <el-option
- v-for="(item, index) in dataHeader"
- :key="index"
- :label="item"
- :value="index"
- ></el-option>
- </el-select>
- </template>
- </el-table-column>
- <!-- 选择判断关系 -->
- <el-table-column label="选择判断关系" prop="relation">
- <template v-slot="{ row, $index }">
- <el-select v-model="row.relation" placeholder="请选择关系">
- <el-option label="等于" value="equals"></el-option>
- <el-option label="不等于" value="not_equals"></el-option>
- <el-option label="大于" value="greater_than"></el-option>
- <el-option label="小于" value="less_than"></el-option>
- </el-select>
- </template>
- </el-table-column>
- <!-- 选择判断值 -->
- <el-table-column label="选择判断值" prop="value">
- <template v-slot="{ row, $index }">
- <el-select v-if="uniqueData[row.variable] && uniqueData[row.variable].length<10"
- v-model="row.value"
- placeholder="请选择值">
- <el-option
- v-for="(item, index) in uniqueData[row.variable]"
- :key="index"
- :label="item"
- :value="item"
- ></el-option>
- </el-select>
- <el-input v-else v-model="row.value">
- </el-input>
- </template>
- </el-table-column>
- <el-table-column label="操作" >
- <template v-slot="scope">
- <el-button type="primary" @click="addRow" size="small">+</el-button>
- <el-button type="danger" @click="deleteRow(scope.$index)" size="small">x</el-button>
- </template>
- </el-table-column>
- </el-table>
- </el-form-item>
- </el-form>
- </el-tab-pane>
- <el-tab-pane label="绘制参数">
- <el-form ref="elForm" :model="drawForm" label-width="180px">
- <el-form-item label="操作">
- <el-button type="primary" icon="el-icon-search" size="mini" @click="analyzeCode()">解析代码参数
- </el-button>
- </el-form-item>
- <el-form-item
- v-for="(item, index) in customStatisticalDataColumnName"
- :key="index"
- :label="`统计参数:${item}`">
- <el-select
- v-model="drawForm.statisticalDataColumnCustom[index]"
- placeholder="请选择统计数据列"
- >
- <el-option
- v-for="(item, index) in dataHeader"
- :key="index"
- :label="item"
- :value="index"
- ></el-option>
- </el-select>
- </el-form-item>
- </el-form>
- </el-tab-pane>
- </el-tabs>
- </el-col>
- <el-col :span="14" style="height: 100%;">
- <el-card style="height: 100%;">
- <div slot="header" class="clearfix">
- <el-button type="primary" icon="el-icon-search" size="medium" @click="redrawChart()">绘制</el-button>
- </div>
- <div id="chart" style="height: 100%;width: 100%"/>
- </el-card>
- </el-col>
- </el-row>
- </div>
- </template>
- <script>
- import * as echarts from "echarts";
- import * as XLSX from 'xlsx';
- import VueAceEditor from 'vue2-ace-editor';
- import 'brace/mode/javascript'; // 引入 JavaScript 语言模式
- import 'brace/theme/chrome';
- export default {
- components: {
- VueAceEditor,
- },
- data() {
- return {
- dialogVisible: true,
- // 图表配置参数
- chartForm: {
- //图表类型
- chartType: "",
- //自定义图表-代码块
- customChartCode: null,
- },
- // 数据配置参数
- dataForm: {
- fileList: [], // 存储上传的文件列表
- //过滤条件
- dataFilter: [
- {
- variable: "",
- relation: "",
- value: ""
- }
- ]
- },
- // 绘制参数
- drawForm: {
- statisticalDataColumnCustom: []
- },
- //自定义图表的配置参数列表
- customStatisticalDataColumnName: [],
- chartFormOptions: [
- {
- "label": "自定义图表",
- "value": 0
- },
- {
- "label": "正态分布图",
- "value": 1
- }, {
- "label": "散点图",
- "value": 2
- }
- ],
- dataHeader: [],
- dataBody: [],
- uniqueData: [],
- filterData: [],
- validConditions: [],
- chart: null
- };
- },
- mounted() {
- this.initChart();
- this.$nextTick(() => {
- const aceEditor = this.$refs.aceEditor.editor;
- if (aceEditor) {
- console.log('Editor Loaded:', aceEditor); // 确认编辑器加载成功
- aceEditor.getSession().setMode('ace/mode/javascript'); // 强制设置语言模式
- aceEditor.setTheme('ace/theme/chrome'); // 强制设置主题
- aceEditor.setFontSize('14px'); // 设置字体大小
- aceEditor.setHighlightActiveLine(true); // 设置是否高亮活动行
- }
- });
- },
- methods: {
- onEditorLoaded(editor) {
- console.log('Editor Loaded:', editor); // 检查 editor 对象
- if (editor) {
- console.log('Ace Mode:', editor.getSession().getMode().$id); // 确认是否加载了 JavaScript 模式
- console.log('Ace Theme:', editor.getTheme()); // 确认主题
- }
- },
- analyzeCode() {
- //提取含{{}}的字符列表["{{d1}}", "{{d2}}"]
- let matchesName = this.chartForm.customChartCode.match(/\{\{(.*?)\}\}/g);
- let matchesNameLabel = []
- if (matchesName) {
- // 去除 {{ 和 }}列表
- matchesNameLabel = matchesName.map(match => match.slice(2, -2));
- }
- this.customStatisticalDataColumnName = matchesNameLabel;
- this.$message({message: '解析成功,提取自定义统计参数' + matchesNameLabel, type: 'warning'})
- },
- addRow() {
- // 添加一行数据到表单中
- this.dataForm.dataFilter.push({variable: '', relation: '', value: ''});
- },
- deleteRow(index) {
- // 删除对应的行
- this.dataForm.dataFilter.splice(index, 1);
- },
- handleBeforeUpload(file) {
- const reader = new FileReader()
- reader.onload = (e) => {
- const data = e.target.result;
- // 解析xlsx文件
- const workbook = XLSX.read(data, {type: 'binary'});
- // 获取第一个工作表
- const sheetName = workbook.SheetNames[0];
- const sheet = workbook.Sheets[sheetName];
- // 原始
- let data_json = XLSX.utils.sheet_to_json(sheet, {header: 1, defval: null});
- // 取出第一行作为表头
- this.dataHeader = data_json.shift(); // 删除并返回数组的第一个元素
- // 剩余的行作为表体
- this.dataBody = data_json;
- this.buildDynamicForms();
- this.$message({message: '解析文件成功,获取记录:' + this.dataBody.length + "条", type: 'warning'})
- // this.drawChart();
- };
- // 读取文件为二进制字符串
- reader.readAsBinaryString(file);
- return false; // 阻止默认的上传行为
- },
- buildDynamicForms() {
- //获取每列的distance值
- let headLength = this.dataHeader.length;
- for (let i = 0; i < headLength; i++) {
- this.uniqueData[i] = this.getUniqueColumn(this.dataBody, i);
- }
- },
- getUniqueColumn(data, index) {
- // 检查数据是否为空
- if (!data || !Array.isArray(data) || data.length === 0) {
- return [];
- }
- // 如果数据是二维数组
- if (Array.isArray(data[0])) {
- // 提取指定列并去重
- const column = data.map(row => row[index]);
- return [...new Set(column)];
- }
- // 如果数据是对象数组
- if (typeof data[0] === 'object') {
- // 提取指定列(即对象的属性)并去重
- const column = data.map(item => item[index]);
- return [...new Set(column)];
- }
- // 如果数据格式不匹配
- return [];
- },
- redrawChart() {
- this.filterDataByConditions();
- let opt = this.chartForm.customChartCode;
- let mapping = this.drawForm.statisticalDataColumnCustom;
- for (let i = 0; i < mapping.length; i++) {
- let data = this.filterData.map(row => row[mapping[i]]);
- opt = opt.replace("{{" + this.customStatisticalDataColumnName[i] + "}}", JSON.stringify(data));
- }
- let rx = (new Function(opt))();
- console.log(rx)
- this.chart.clear()
- this.chart.setOption(rx)
- },
- initChart() {
- let chartDom = document.getElementById("chart");
- this.chart = echarts.init(chartDom);
- },
- filterDataByConditions() {
- this.filterData = this.dataBody.filter(item => {
- this.validConditions = this.dataForm.dataFilter.filter(c => c.value !== "");
- return this.validConditions.every(row => {
- let rowKey = row.variable;
- let operator = row.relation;
- let rowValue = row.value;
- switch (operator) {
- case "equals":
- return item[rowKey] == rowValue;
- case "not_equals":
- return item[rowKey] != rowValue;
- case "greater_than":
- return item[rowKey] > rowValue;
- case "less_than":
- return item[rowKey] < rowValue;
- default:
- throw new Error(`Unknown operator: ${operator}`);
- }
- });
- });
- },
- }
- }
- ;
- </script>
- <style scoped>
- /* 或者使用 ::v-deep */
- ::v-deep .el-dialog__wrapper {
- pointer-events: none;
- }
- ::v-deep .el-dialog__body {
- padding-bottom: 5px
- }
- ::v-deep .el-dialog {
- pointer-events: auto;
- }
- ::v-deep .el-card__body{
- height: 100%;
- }
- ::v-deep .el-tabs__content{
- height: 90% !important;
- }
- </style>
复制代码 代码事例
事例1 单散点分布 singleScatterDistribution
- // 单散点分布 singleScatterDistribution
- /*******************配置参数*******************/
- //第一组的x,y,和size,都是一维数组
- let dx = {{dx}};
- let dy = {{dy}};
- let dSize = {{dSize}};
- /*******************计算实体********************/
- let dataX = dx.map((value, index) => [value, dy[index], dSize[index]]);
- return {
- toolbox: {
- show: true,
- feature: {
- dataZoom: {
- yAxisIndex: 'none'
- },
- dataView: {readOnly: false},
- restore: {},
- saveAsImage: {}
- }
- },
- xAxis: {},
- yAxis: {},
- legend: {
- data: ['数据名称'] // 图例项,对应 series 中的 name
- },
- dataZoom: [
- {
- type: 'slider',
- show: true,
- yAxisIndex: [0],
- //不过滤数据,只改变数轴范围。
- filterMode: 'none'
- },
- {
- type: 'inside',
- yAxisIndex: [0],
- filterMode: 'none'
- },
- //x缩放轴
- {
- type: 'slider',
- show: true,
- xAxisIndex: [0],
- filterMode: 'none',
- height: 20
- },
- {
- type: 'inside',
- xAxisIndex: [0],
- filterMode: 'none'
- }
- ],
- series: [
- {
- name: '数据名称',
- symbolSize: function (data) {
- //return data[2]/100; // 返回 data 中的第三个值(size)
- return 7;
- },
- data: dataX,
- type: 'scatter',
- itemStyle: {
- color: '#5470C6' // 设置第一组数据的颜色
- }
- }
- ]
- }
复制代码 事例2 双散点分布 doubleScatterDistribution
- // 双散点分布 doubleScatterDistribution
- /*******************配置参数*******************/
- //第一组的x,y,和size,都是一维数组
- let d1x = {{d1.x}};
- let d1y = {{d1.y}};
- let d1size = {{d1.size}};
- //第二组的x,y,和size,都是一维数组
- let d2x = {{d1.x}};
- let d2y = {{d1.y}};
- let d2size = {{d1.size}};
- /*******************计算实体********************/
- let d1 = d1x.map((value, index) => [value, d1y[index], d1size[index]]);
- let d2 = d2x.map((value, index) => [value, d2y[index], d2size[index]]);
- return {
- toolbox: {
- show: true,
- feature: {
- dataZoom: {
- yAxisIndex: 'none'
- },
- dataView: {readOnly: false},
- restore: {},
- saveAsImage: {}
- }
- },
- xAxis: {},
- yAxis: {},
- legend: {
- data: ['新数据', '旧数据'] // 图例项,对应 series 中的 name
- },
- dataZoom: [
- {
- type: 'slider',
- show: true,
- yAxisIndex: [0],
- //不过滤数据,只改变数轴范围。
- filterMode: 'none'
- },
- {
- type: 'inside',
- yAxisIndex: [0],
- filterMode: 'none'
- },
- //x缩放轴
- {
- type: 'slider',
- show: true,
- xAxisIndex: [0],
- filterMode: 'none',
- height: 20
- },
- {
- type: 'inside',
- xAxisIndex: [0],
- filterMode: 'none'
- }
- ],
- series: [
- {
- name: '新数据',
- symbolSize: function (data) {
- //return data[2]/100; // 返回 data 中的第三个值(size)
- return 7;
- },
- data: d1,
- type: 'scatter',
- itemStyle: {
- color: '#5470C6' // 设置第一组数据的颜色
- }
- },
- {
- name: '旧数据',
- symbolSize: function (data) {
- //return data[2] + 5; // 返回 data 中的第三个值(size)
- return 7;
- },
- data: d2,
- type: 'scatter',
- itemStyle: {
- color: '#EE6666' // 设置第二组数据的颜色
- }
- }
- ]
- }
复制代码 事例3 单正态分布图 singleNormalDistribution
- // 单正态分布图 singleNormalDistribution
- /*******************配置参数*******************/
- // 组间距
- let interval = 0.1;
- // 参数实例 [1,2,3,4,5]
- let da = {{data}}
- // 标题
- let title = "这里填写标题"
- //图表颜色
- let drawColor = "rgba(19,72,206,0.5)"
- /*******************计算实体********************/
- //计算正态曲线
- function fxNormalDistribution(array, std, mean) {
- let valueList = [];
- for (let i = 0; i < array.length; i++) {
- let ND =
- Math.sqrt(2 * Math.PI) *
- std *
- Math.pow(
- Math.E,
- -(Math.pow(array[i] - mean, 2) / (2 * Math.pow(std, 2)))
- );
- valueList.push(ND.toFixed(3));
- }
- return valueList;
- }
- //计算标准差
- function getStd(data, mean) {
- let sumXY = function (x, y) {
- return Number(x) + Number(y);
- };
- let square = function (x) {
- return Number(x) * Number(x);
- };
- let deviations = data.map(function (x) {
- return x - mean;
- });
- return Math.sqrt(deviations.map(square).reduce(sumXY) / (data.length - 1));
- }
- //对有序数组求中位数
- function getMedianSorted(arr) {
- // 获取数组长度
- let len = arr.length;
- // 如果没有元素,返回undefined或你可以返回其他合适的值
- if (len === 0) {
- return undefined;
- }
- // 如果只有一个元素,那么它就是中位数
- if (len === 1) {
- return arr[0];
- }
- // 如果数组长度是奇数,返回中间的数
- if (len % 2 === 1) {
- return arr[Math.floor(len / 2)];
- }
- // 如果数组长度是偶数,返回中间两个数的平均值
- else {
- let mid1 = arr[len / 2 - 1];
- let mid2 = arr[len / 2];
- return (mid1 + mid2) / 2.0;
- }
- }
- function getUniqueColumn(data, index) {
- // 检查数据是否为空
- if (!data || !Array.isArray(data) || data.length === 0) {
- return [];
- }
- // 如果数据是二维数组
- if (Array.isArray(data[0])) {
- // 提取指定列并去重
- const column = data.map(row => row[index]);
- return [...new Set(column)];
- }
- // 如果数据是对象数组
- if (typeof data[0] === 'object') {
- // 提取指定列(即对象的属性)并去重
- const column = data.map(item => item[index]);
- return [...new Set(column)];
- }
- // 如果数据格式不匹配
- return [];
- }
- function getInterval(va, xAxisX) {
- for (let i = 0; i < xAxisX.length; i++) {
- if (xAxisX[i] > va) {
- if (i === 0) {
- return xAxisX[0].toString();
- } else {
- return xAxisX[i - 1].toString();
- }
- }
- }
- return xAxisX[xAxisX.length - 1].toString();
- }
- function extracted(da) {
- //获取参数指定的公差,标准值,组间据
- da.sort((a, b) => a - b);
- let mid = getMedianSorted(da).toFixed(3) * 1.0;
- // 计算各项最大值,最小值,均值,以及cpk值,以及数组长度
- let length = da.length;
- let max = Math.max(...da);
- let min = Math.min(...da);
- let mean = da.reduce((sum, val) => sum + val, 0) / length;
- //依据原始数据重新计算数据X轴
- //x轴最大最小前后范围
- let dataRangeMinOP = 1;
- let dataRangeMaxOP = 1.1;
- // 设定区间起始和结束值,以及间距 ,X轴的步距
- let start = min - dataRangeMinOP;
- let end = max + dataRangeMaxOP;
- start = start.toFixed(0) * 1.0;
- // 计算区间数量
- let numIntervals = Math.ceil((end - start) / interval);
- //获取所有区间,以及初始化区间每个区间的频数为0
- let xAxis = [];
- let barYaxis = new Array(numIntervals).fill(0);
- for (let i = start; i <= end; i = i + interval) {
- let str = i.toFixed(1).toString();
- xAxis.push(str);
- }
- // 遍历数组并计算频数
- da.forEach((value) => {
- if (value >= start && value <= end) {
- // 找到值所在的区间
- let intervalIndex = Math.floor((value - start) / interval);
- // 增加该区间的频数
- barYaxis[intervalIndex]++;
- }
- });
- //正态曲线计算的基本数据和方法
- //计算标准差
- let std = getStd(da, mean);
- //依据x轴,计算正态分布值
- let lineYaxis = fxNormalDistribution(xAxis, std, mean);
- //求得分布曲线最大值,用以绘制纵向线
- let y1Max = Math.max(...lineYaxis);
- let midInterval = getInterval(mid, xAxis);
- //定义Y轴
- //定义实际数据的频数柱状图
- let barDataSet = {
- type: "bar",
- smooth: true,
- xAxisIndex: 0,
- yAxisIndex: 0,
- areaStyle: {
- opacity: 0,
- },
- markLine: {
- silent: true,
- label: {
- fontSize: 10
- },
- lineStyle: {
- color: "rgb(255,0,0)",
- },
- data: [],
- },
- data: barYaxis,
- itemStyle: {
- normal: {
- label: {
- formatter: "{c} %",
- show: false, //默认显示
- position: "top", //在上方显示
- textStyle: {
- //数值样式
- fontSize: 10,
- },
- },
- },
- },
- };
- //计算实际数据的正态分布图
- let lineDataSet = {
- type: "line",
- smooth: true,
- xAxisIndex: 0,
- yAxisIndex: 1,
- areaStyle: {
- opacity: 0,
- },
- markLine: {
- silent: true,
- label: {
- fontSize: 12
- },
- data: [
- [
- {
- name: "中位数:" + mid,
- coord: [midInterval, 0],
- },
- {
- coord: [midInterval, y1Max],
- },
- ],
- ],
- },
- data: lineYaxis,
- itemStyle: {
- normal: {
- label: {
- formatter: "{c} %",
- show: false, //开启显示
- position: "top", //在上方显示
- textStyle: {
- //数值样式
- fontSize: 10,
- },
- },
- },
- },
- };
- return {xAxis, barDataSet, lineDataSet};
- }
- function getOptions() {
- x = extracted(da);
- return {
- color: drawColor,
- //工具栏。内置有导出图片,数据视图,动态类型切换,数据区域缩放,重置五个工具。
- toolbox: {
- show: true,
- feature: {
- dataZoom: {
- yAxisIndex: "none",
- },
- dataView: {readOnly: false},
- restore: {},
- saveAsImage: {},
- },
- },
- title: {
- text: title,
- left: "center"
- },
- grid: {
- top: 80
- },
- //提示框组件
- tooltip: {
- trigger: "axis",
- axisPointer: {
- type: "shadow",
- },
- },
- //图例
- legend: {
- right: "0%",
- top: "0",
- textStyle:
- {
- fontSize: 12
- }
- },
- xAxis: [
- {boundaryGap: true, type: "category", gridIndex: 0, data: x.xAxis},
- ],
- //定义y轴,2个轴
- yAxis: [
- {
- type: "value", gridIndex: 0, max: function (value) {
- return value.max + 5;
- }
- },
- {
- type: "value", gridIndex: 0, max: function (value) {
- return (value.max + 0.2).toFixed(1);
- }
- }
- ],
- series: [
- x.barDataSet, x.lineDataSet,
- ],
- }
- }
- return getOptions();
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |