From 52a7bf6ee2c2f9a4ce529dc98440c8777e950377 Mon Sep 17 00:00:00 2001 From: wangwenrui Date: Fri, 18 Sep 2020 10:53:09 +0800 Subject: [PATCH 01/11] =?UTF-8?q?feat:=E5=9B=BE=E8=A1=A8=E9=A2=84=E8=A7=88?= =?UTF-8?q?singleStat=E6=B7=BB=E5=8A=A0valueMapping=20&=20legendOption?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/charts/chart.scss | 6 + .../src/components/charts/chartPreview.vue | 262 +++++++++++------ .../components/charts/line-chart-block.vue | 269 ++++++++++++++---- .../src/components/common/exportXLSX.vue | 3 + .../components/page/dashboard/chartBox.vue | 2 + 5 files changed, 404 insertions(+), 138 deletions(-) diff --git a/nezha-fronted/src/components/charts/chart.scss b/nezha-fronted/src/components/charts/chart.scss index bb90106c4..b6dd1efbb 100644 --- a/nezha-fronted/src/components/charts/chart.scss +++ b/nezha-fronted/src/components/charts/chart.scss @@ -35,6 +35,12 @@ position: absolute; padding-bottom:3px; } +.legend-container .option-th{ + color:#33a2e5; +} +.legend-container .option-th:hover{ + cursor:pointer; +} .nz-icon-warning{ color: #e6a23c; } diff --git a/nezha-fronted/src/components/charts/chartPreview.vue b/nezha-fronted/src/components/charts/chartPreview.vue index 2576c4b9c..c464efab0 100644 --- a/nezha-fronted/src/components/charts/chartPreview.vue +++ b/nezha-fronted/src/components/charts/chartPreview.vue @@ -79,12 +79,41 @@
No Data
-
-
- {{item.alias?item.alias:item.name}} -
+ + + +
@@ -212,6 +241,8 @@ titleHeight:58, legendHeight:80, pageHeight:40, + hasLegendOptions:false, + screenLegendOptions:[], }; }, computed: { @@ -266,7 +297,6 @@ chartDiv.style.height = `${height-this.chartSpaceHeight-this.titleHeight-15}px`; } }); - this.setSize(chartInfo.span); // 设置该图表宽度 this.screenModal = true; @@ -321,15 +351,6 @@ this.setLoadFrame(); } }, - // 设置图表的宽度 - setSize(size) { - this.$nextTick(() => { - const listContainer = document.getElementById('listContainer'); - //chartBox.style.width = `${(size / 12) * 100}%`; - let containerWidth= listContainer.clientWidth; - this.dailogWidth = `${containerWidth*(size / 12)}px`; - }); - }, getAlertListChartData:function(chartInfo,filterType){ this.$refs.alertListChart.getAlertList(filterType,true,this.chart); this.$refs.loadingPreview.endLoading(); @@ -524,7 +545,19 @@ }else if(chartItem.type ==='singleStat'){ if(Number(singleStatRlt)){ let singleStatTmp =parseFloat(Number(singleStatRlt).toFixed(2)); - this.serieSingleStat = chartDataFormat.getUnit(chartItem.unit?chartItem.unit:2).compute(singleStatTmp,null,2); + if(chartItem.param.valueMapping && chartItem.param.valueMapping.type){ + let type=chartItem.param.valueMapping.type; + let mappings=chartItem.param.valueMapping.mapping?chartItem.param.valueMapping.mapping:[]; + let mapping; + if(type == 'value'){ + mapping=mappings.find(item=>{return item.value == singleStatTmp}) + }else{ + mapping=mappings.find(item=>{return item.from <= singleStatTmp&& item.to >= singleStatTmp}); + } + this.serieSingleStat = mapping?mapping.text:chartDataFormat.getUnit(chartItem.unit?chartItem.unit:2).compute(singleStatTmp,null,2); + }else{ + this.serieSingleStat = chartDataFormat.getUnit(chartItem.unit?chartItem.unit:2).compute(singleStatTmp,null,2); + } }else { this.serieSingleStat =singleStatRlt; } @@ -773,78 +806,15 @@ }, }, legend: { - type:'scroll', - height:80, show:false, - icon:"roundRect", - itemHeight:5, - itemWidth:15, - formatter:function(name){ - //console.log('==========='+name); - if(!name){ - return ''; - } - //计算宽度 - var span = document.createElement("span"); - var result = {}; - result.width = span.offsetWidth; - result.height = span.offsetHeight; - span.style.visibility = "hidden"; - span.style.fontSize = 14; - span.style.fontFamily = "Arial"; - span.style.display = "inline-block"; - document.body.appendChild(span); - if(typeof span.textContent != "undefined"){ - span.textContent = name; - }else{ - span.innerText = name; - } - var txtWidth = parseFloat(window.getComputedStyle(span).width) - result.width; - document.body.removeChild(span); - - if(txtWidth < chartWidth){ - return name; - }else { - var charNum = `${(chartWidth-100)/(txtWidth/name.length)}`; - return name.slice(0,charNum)+'...'; - } - }, - tooltip:{ - show:true, - formatter:function(params){ - //alert(params.length); - //alert(JSON.stringify(params)); - - return `
${params.name}
`; - }, - }, - data: legend, - orient:'vertical', - x:'center', - y:'bottom', - //top:'5%', - //bottom:0 }, grid: { top: 30, left: 0, right: 30, containLabel: true, - bottom:8,//156 - },/* - dataZoom: [{ - type: 'slider', - show:true, - xAxisIndex: [0], - start: 0, - end: 100, - height:25, - bottom:10,//96 - left:40, - right:48, - } - ],*/ - + bottom: 8,//156 + }, xAxis: { type: 'time', // boundaryGap: false,//line-false; bar-true; @@ -929,12 +899,16 @@ let _legend = { name:item.name, alias:item.alias, + color:item.color, showText:this.formatLegend(chartWidth,item.alias?item.alias:item.name) }; this.screenLegendList.push(_legend); this.isGreyScreen.push(false); }); } + if(this.hasLegendOptions){ + this.computeLegendData(this.screenLegendList,dataArg,'screen'); + } this.$nextTick(() => { let divHeight = this.$refs.screenLegendArea.offsetHeight; this.echartModalStore.resize({height:(chartInfo.height-this.chartSpaceHeight-divHeight-this.titleHeight)}); @@ -1031,11 +1005,16 @@ // 设置数据 setData(chartItem, seriesItem,legend) { this.setColor(legend.length); + legend.forEach((t,i)=>{t.color = this.bgColorList[i]}) this.legend = legend; //this.data = chartItem; //this.seriesItem = seriesItem; this.seriesItemScreen = seriesItem; - + this.hasLegendOptions=this.findLegendOptions() + if(this.hasLegendOptions){ + let sortedOptionKeys=['min','max','avg','last','total'] + this.screenLegendOptions=sortedOptionKeys.map(item=>{ return {option:item,sort:'',value:chartItem.param.legendValue[item]}}) + } this.initChart(chartItem, seriesItem, this.$refs.screenShowArea,legend); }, // 设置数据 @@ -1264,6 +1243,125 @@ this.screenModal = false; this.clearChart(); }, + findLegendOptions:function(){ + if((!this.chart.param.legendValue) || Object.keys(this.chart.param.legendValue)<1) return false; + let legendOptions= this.chart.param.legendValue; + let onVal=Object.keys(legendOptions).find(item=>{return legendOptions[item] == 'on'}); + + return onVal; + }, + computeLegendData:function(legend,dataArr){ + let options=this.screenLegendOptions; + let keys=options.filter(item=>{return item.value == 'on'}).map(item=>{return item.option}); + let $self=this; + keys.forEach(item=>{ + switch (item) { + case 'min': + $self.legendMinValue(legend,dataArr); + break; + case 'max': + $self.legendMaxValue(legend,dataArr); + break; + case 'avg': + $self.legendAvgValue(legend,dataArr); + break; + case 'last': + $self.legendLastValue(legend,dataArr); + break; + case 'total': + $self.legendTotalValue(legend,dataArr); + break; + } + }) + }, + legendMinValue:function(legend,dataArr){ + return new Promise(resolve => { + legend.forEach(item=>{ + let data=dataArr.find(t=>t.name == item.name) + if(data&&data.data){ + let copy=JSON.parse(JSON.stringify(data.data)); + let min =copy.sort((x,y)=>{return parseFloat(x[1]) - parseFloat(y[1])})[0][1] + item['min']=parseFloat(min) + } + }) + + resolve(); + }) + }, + legendMaxValue:function(legend,dataArr){ + return new Promise(resolve => { + legend.forEach(item=>{ + let data=dataArr.find(t=>t.name == item.name) + if(data&&data.data){ + let copy=JSON.parse(JSON.stringify(data.data)); + let max =copy.sort((x,y)=>{return parseFloat(y[1]) - parseFloat(x[1])})[0][1] + item['max']=parseFloat(max) + } + }) + resolve(); + }) + }, + legendAvgValue:function(legend,dataArr){ + return new Promise(resolve => { + legend.forEach(item=>{ + let data=dataArr.find(t=>t.name == item.name) + if(data&&data.data){ + let copy=JSON.parse(JSON.stringify(data.data)); + copy = copy.map(t=>parseFloat(t[1])) + let sum = eval(copy.join('+')); + let avg = sum / copy.length; + + item['avg'] = avg; + } + }) + resolve(); + }) + }, + legendLastValue:function(legend,dataArr){ + return new Promise(resolve => { + legend.forEach(item=>{ + let data=dataArr.find(t=>t.name == item.name) + if(data&&data.data){ + let copy=JSON.parse(JSON.stringify(data.data)); + let last =copy.sort((x,y)=>{return parseFloat(y[0]) - parseFloat(x[0])})[0][1] + item['last']=parseFloat(last) + } + }) + resolve(); + }) + }, + legendTotalValue:function(legend,dataArr){ + return new Promise(resolve => { + legend.forEach(item=>{ + let data=dataArr.find(t=>t.name == item.name) + if(data&&data.data){ + let copy=JSON.parse(JSON.stringify(data.data)); + copy = copy.map(t=>parseFloat(t[1])) + let sum = eval(copy.join('+')); + + item['total'] = sum; + } + }) + resolve(); + }) + }, + formatLegendData:function(data){ + return chartDataFormat.getUnit(this.chart.unit).compute(data,null,2) + }, + legendValueSort:function(option,legend,options){ + options.forEach(item=>{ + if(item.option != option.option){ + item.sort=''; + } + }) + if(!option.sort || option.sort == 'asc'){ + legend.sort((x,y)=>x[option.option] - y[option.option]) + option.sort = 'desc' + }else{ + legend.sort((x,y)=>y[option.option] - x[option.option]) + option.sort = 'asc' + } + } }, mounted() { diff --git a/nezha-fronted/src/components/charts/line-chart-block.vue b/nezha-fronted/src/components/charts/line-chart-block.vue index 8486bb4c1..054c8ed49 100644 --- a/nezha-fronted/src/components/charts/line-chart-block.vue +++ b/nezha-fronted/src/components/charts/line-chart-block.vue @@ -57,11 +57,38 @@
No Data
-
-
- {{item.alias?item.alias:item.name}} + +
No Data
-
-
- {{item.alias?item.alias:item.name}} -
+ + @@ -217,7 +271,10 @@ showLegend:true, searchTime:[new Date().setHours(new Date().getHours()-1),new Date()], oldSearchTime:[], - screenTitleHeight:58 + screenTitleHeight:58, + hasLegendOptions:false, + legendOptions:[], + screenLegendOptions:[], }; }, watch: {}, @@ -268,10 +325,11 @@ } }, clickLegend(legendName,index){ + console.log('cur index',index,legendName) //点击图表某一个legend,图表只显示当前点击的曲线或柱状图,其它隐藏,再次点击已选中的legend ,显示全部 let curIsGrey=this.isGrey[index]; if(this.echartStore){ - this.legendList.forEach((item,i)=>{ + this.legendListMore.forEach((item,i)=>{ let isGrey = this.isGrey[i]; if(index != i){ if(!curIsGrey && !isGrey){ @@ -318,7 +376,7 @@ //点击图表某一个legend,图表只显示当前点击的曲线或柱状图,其它隐藏,再次点击已选中的legend ,显示全部 let curIsGrey=this.isGreyScreen[index]; if(this.echartModalStore){ - this.screenLegendList.forEach((item,i)=>{ + this.screenLegendListMore.forEach((item,i)=>{ let isGrey = this.isGreyScreen[i]; if(index != i){ if(!curIsGrey && !isGrey){ @@ -403,6 +461,7 @@ if ( chartInfo.type === 4) {//line,bar this.chartType = 'line'; } + console.log('chartData',chartInfo,legend,dataArg) let minTime = null; let maxTime = null if(dataArg.length>0 && dataArg[0].data @@ -578,51 +637,7 @@ }, }, legend: { - type:'scroll', - height:80, show:false, - icon:"roundRect", - itemHeight:5, - itemWidth:15, - formatter:function(name){ - if(!name){ - return ''; - } - //计算宽度 - var span = document.createElement("span"); - var result = {}; - result.width = span.offsetWidth; - result.height = span.offsetHeight; - span.style.visibility = "hidden"; - span.style.fontSize = 14; - span.style.fontFamily = "Arial"; - span.style.display = "inline-block"; - document.body.appendChild(span); - if(typeof span.textContent != "undefined"){ - span.textContent = name; - }else{ - span.innerText = name; - } - var txtWidth = parseFloat(window.getComputedStyle(span).width) - result.width; - document.body.removeChild(span); - - if(txtWidth < chartWidth){ - return name; - }else { - var charNum = `${(chartWidth-100)/(txtWidth/name.length)}`; - return name.slice(0,charNum)+'...'; - } - }, - tooltip:{ - show:true, - formatter:function(params){ - return `
${params.name}
`; - }, - }, - data: legend, - orient:'vertical', - x:'center', - y:'bottom', }, grid: { top: 30, @@ -726,6 +741,7 @@ const legend = { name:item.name, alias:item.alias, + color:item.color, showText:this.formatLegend(chartWidth,item.name) }; this.legendList.push(legend); @@ -733,6 +749,9 @@ }); } this.legendListMore=this.legendList.filter((item,index)=>index { setTimeout(function () { let divHeight = self.$refs.legendArea.offsetHeight; @@ -839,6 +858,7 @@ const legend = { name:item.name, alias:item.alias, + color:item.color, showText:this.formatLegend(chartWidth,item.name) }; this.screenLegendList.push(legend); @@ -848,6 +868,9 @@ if(this.screenLegendListMore.length!==this.screenLegendList.length){ this.screenLegendListMore=this.screenLegendList.filter((item,index)=>index{t.color = this.bgColorList[i]}) this.filter.from = filter.from; if(errorMsg && errorMsg!==''){ this.isError = true; @@ -1033,6 +1057,13 @@ this.seriesItem = seriesItem; this.seriesItemScreen = seriesItem; + this.hasLegendOptions=this.findLegendOptions() + if(this.hasLegendOptions){ + let sortedOptionKeys=['min','max','avg','last','total'] + this.legendOptions=sortedOptionKeys.map(item=>{ return {option:item,sort:'',value:chartItem.param.legendValue[item]}}) + this.screenLegendOptions=sortedOptionKeys.map(item=>{ return {option:item,sort:'',value:chartItem.param.legendValue[item]}}) + } + if (filter) { // 保存数据,用于同步时间 this.stableFilter = filter; this.searchTime[0] = filter.start_time; @@ -1050,6 +1081,7 @@ }else{ this.seriesItemArr=seriesItem } + this.initChart(chartItem, seriesItemArr, this.$refs.lineChartArea, 'local',legend); } @@ -1417,6 +1449,9 @@ this.echartStore.setOption({ series:this.seriesItem }); + if(this.hasLegendOptions){ + this.computeLegendData(this.legendListMore,this.seriesItemArr,'local') + } this.$nextTick(()=>{ let divHeight = this.$refs.legendArea.offsetHeight; if(!this.chartInfo.height){ @@ -1433,6 +1468,9 @@ this.echartModalStore.setOption({ series:this.seriesItemScreen }); + if(this.hasLegendOptions){ + this.computeLegendData(this.screenLegendListMore,this.seriesItemArrScreen,'screen') + } this.$nextTick(()=>{ let legendDiv = document.getElementById('screenLegendArea'+this.chartIndex); let divHeight = legendDiv.offsetHeight; @@ -1442,6 +1480,125 @@ this.$refs.screenShowArea.style.height = `${sumHeight - divHeight - this.screenTitleHeight}px`; this.echartModalStore.resize({height: (sumHeight - divHeight - this.screenTitleHeight)});//图表的高度 }) + }, + findLegendOptions:function(){ + if((!this.data.param.legendValue) || Object.keys(this.data.param.legendValue)<1) return false; + let legendOptions= this.data.param.legendValue; + let onVal=Object.keys(legendOptions).find(item=>{return legendOptions[item] == 'on'}); + + return onVal; + }, + computeLegendData:function(legend,dataArr,where){ + let options=where =='local'?this.legendOptions:this.screenLegendOptions; + let keys=options.filter(item=>{return item.value == 'on'}).map(item=>{return item.option}); + let $self=this; + keys.forEach(item=>{ + switch (item) { + case 'min': + $self.legendMinValue(legend,dataArr); + break; + case 'max': + $self.legendMaxValue(legend,dataArr); + break; + case 'avg': + $self.legendAvgValue(legend,dataArr); + break; + case 'last': + $self.legendLastValue(legend,dataArr); + break; + case 'total': + $self.legendTotalValue(legend,dataArr); + break; + } + }) + }, + legendMinValue:function(legend,dataArr){ + return new Promise(resolve => { + legend.forEach(item=>{ + let data=dataArr.find(t=>t.name == item.name) + if(data&&data.data){ + let copy=JSON.parse(JSON.stringify(data.data)); + let min =copy.sort((x,y)=>{return parseFloat(x[1]) - parseFloat(y[1])})[0][1] + item['min']=parseFloat(min) + } + }) + + resolve(); + }) + }, + legendMaxValue:function(legend,dataArr){ + return new Promise(resolve => { + legend.forEach(item=>{ + let data=dataArr.find(t=>t.name == item.name) + if(data&&data.data){ + let copy=JSON.parse(JSON.stringify(data.data)); + let max =copy.sort((x,y)=>{return parseFloat(y[1]) - parseFloat(x[1])})[0][1] + item['max']=parseFloat(max) + } + }) + resolve(); + }) + }, + legendAvgValue:function(legend,dataArr){ + return new Promise(resolve => { + legend.forEach(item=>{ + let data=dataArr.find(t=>t.name == item.name) + if(data&&data.data){ + let copy=JSON.parse(JSON.stringify(data.data)); + copy = copy.map(t=>parseFloat(t[1])) + let sum = eval(copy.join('+')); + let avg = sum / copy.length; + + item['avg'] = avg; + } + }) + resolve(); + }) + }, + legendLastValue:function(legend,dataArr){ + return new Promise(resolve => { + legend.forEach(item=>{ + let data=dataArr.find(t=>t.name == item.name) + if(data&&data.data){ + let copy=JSON.parse(JSON.stringify(data.data)); + let last =copy.sort((x,y)=>{return parseFloat(y[0]) - parseFloat(x[0])})[0][1] + item['last']=parseFloat(last) + } + }) + resolve(); + }) + }, + legendTotalValue:function(legend,dataArr){ + return new Promise(resolve => { + legend.forEach(item=>{ + let data=dataArr.find(t=>t.name == item.name) + if(data&&data.data){ + let copy=JSON.parse(JSON.stringify(data.data)); + copy = copy.map(t=>parseFloat(t[1])) + let sum = eval(copy.join('+')); + + item['total'] = sum; + } + }) + resolve(); + }) + }, + formatLegendData:function(data){ + return chartDataFormat.getUnit(this.chartInfo.unit).compute(data,null,2) + }, + legendValueSort:function(option,legend,options){ + options.forEach(item=>{ + if(item.option != option.option){ + item.sort=''; + } + }) + if(!option.sort || option.sort == 'asc'){ + legend.sort((x,y)=>x[option.option] - y[option.option]) + option.sort = 'desc' + }else{ + legend.sort((x,y)=>y[option.option] - x[option.option]) + option.sort = 'asc' + } } }, mounted() { diff --git a/nezha-fronted/src/components/common/exportXLSX.vue b/nezha-fronted/src/components/common/exportXLSX.vue index 4723a3cbd..f0c3015a0 100644 --- a/nezha-fronted/src/components/common/exportXLSX.vue +++ b/nezha-fronted/src/components/common/exportXLSX.vue @@ -210,6 +210,9 @@ exportAll:function(){ let params=JSON.parse(JSON.stringify(this.params)); params.pageSize=-1; + if (this.importUrl.indexOf('panel') > -1){ + delete params.panelId + } params.language=localStorage.getItem('nz-language') || 'en'; this.exportExcel(this.exportUrl,params,this.exportFileName+'-'+this.getTimeString()+'.xlsx'); diff --git a/nezha-fronted/src/components/page/dashboard/chartBox.vue b/nezha-fronted/src/components/page/dashboard/chartBox.vue index d417f4962..0d25de3b7 100644 --- a/nezha-fronted/src/components/page/dashboard/chartBox.vue +++ b/nezha-fronted/src/components/page/dashboard/chartBox.vue @@ -1125,11 +1125,13 @@ }; if(this.editChart.type === 'singleStat'){ params.param.statistics=this.statistics; + params.param.valueMapping=this.editChart.param.valueMapping; } else { delete params.param.statistics; } if(this.editChart.type==='line'||this.editChart.type==='bar'||this.editChart.type==='stackArea'){ params.param.threshold=this.editChart.param.threshold; + params.param.legendValue=this.editChart.param.legendValue; } else { delete params.param.threshold; } From af13b0902b9ccac1a71112226764b79a766f36f4 Mon Sep 17 00:00:00 2001 From: wangwenrui Date: Fri, 18 Sep 2020 11:04:44 +0800 Subject: [PATCH 02/11] =?UTF-8?q?style:legend=20option=20=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- nezha-fronted/src/components/charts/chart.scss | 3 +++ nezha-fronted/src/components/charts/chartPreview.vue | 4 ++-- .../src/components/charts/line-chart-block.vue | 12 ++++++------ 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/nezha-fronted/src/components/charts/chart.scss b/nezha-fronted/src/components/charts/chart.scss index b6dd1efbb..531de283e 100644 --- a/nezha-fronted/src/components/charts/chart.scss +++ b/nezha-fronted/src/components/charts/chart.scss @@ -41,6 +41,9 @@ .legend-container .option-th:hover{ cursor:pointer; } +.legend-option-cell{ + white-space: nowrap;padding: 0px 5px 0px 5px; +} .nz-icon-warning{ color: #e6a23c; } diff --git a/nezha-fronted/src/components/charts/chartPreview.vue b/nezha-fronted/src/components/charts/chartPreview.vue index c464efab0..d29d857aa 100644 --- a/nezha-fronted/src/components/charts/chartPreview.vue +++ b/nezha-fronted/src/components/charts/chartPreview.vue @@ -93,7 +93,7 @@