This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
nezha-nezha-fronted/nezha-fronted/src/components/charts/chartPreview.vue
2021-11-17 17:27:14 +08:00

1977 lines
79 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<style scoped>
.chart-preview-dialog{
}
.nz-preview-picker{
}
.hidden{
visibility: hidden;
}
.visible{
visibility: visible;
}
.chart-title {
display: flex;
justify-content: space-between;
}
.chart-title-text{
max-width:calc(100% - 20px);
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
/deep/ .el-dialog__body{
height: 70vh;
display: block;
min-height: 300px;
}
/deep/ .el-table thead{
color: #333;
}
</style>
<style>
.char-url-preview html{
}
#chartPreviewDailog .el-dialog__body {
padding-bottom:20px !important;
}
#chartPreviewDailog .el-dialog__header{
padding: 20px 20px 0px !important;
}
</style>
<template>
<el-dialog class="chart-preview-dialog nz-dialog" id="chartPreviewDailog"
:visible.sync="screenModal"
:width="dailogWidth"
:modal-append-to-body='false'
:modal="!fromEndpoint"
@close="handleClose"
@opened="initDialog">
<el-popover
v-if="isError"
placement="top-start"
:close-delay=10
trigger="hover"
popper-class="chart-error-popper">
<div >{{errorContent}}</div>
<span slot="reference" class="panel-info-corner panel-info-corner--error">
<i class="nz-icon nz-icon-warning fa-model" ></i>
<span class="panel-info-corner-inner"></span>
</span>
</el-popover>
<div slot="title" class="chart-title">
<span class="nz-dialog-title chart-title-text">{{chart.name}}</span>
<div class="float-right panel-calendar dialog-tool" v-if="chart.type!=='url' && chart.type !=='alertList'">
<!-- <time-picker ref="calendarPanel" class="nz-dashboard-picker" style="margin-top: -12px;" @change="dateChange" v-if="chart.type !='text'"></time-picker>-->
<pick-time :refresh-data-func="dateChange" v-model="searchTime" :use-chart-unit="false" ref="pickTime" style="height: 28px;" id="chart-preview"></pick-time>
<!--
<my-date-picker ref="calendar" prefix-icon=" " size="mini" class="nz-preview-picker"
format="yyyy/MM/dd HH:mm"
@change="dateChange"
v-model="searchTime"
type="datetimerange"
:picker-options="pickerOptions"
:range-separator="$t('dashboard.panel.to')"
:start-placeholder="$t('dashboard.panel.startTime')"
:end-placeholder="$t('dashboard.panel.endTime')"
align="right">
</my-date-picker>-->
</div>
</div>
<template v-if="chart.type==='line' || chart.type==='bar'||chart.type==='stackArea' || chart.type == 'endpointInfo' ||chart.type =='pie'">
<div id="chartEchartPreview" @mouseenter="mouseEnterFullChart" @mouseleave="mouseLeaveFullChart" class="height-100">
<div class="line-area" ref="screenShowArea" id="screenShowArea" style="margin-top:0px;height: calc(70vh - 50px);min-height: 300px;"></div>
<div class="chart-no-data height-100" v-show="noData">No Data</div>
<template v-if="!hasLegendOptions">
<div @mouseenter="mouseEnterFullChart" @mouseleave="mouseLeaveFullChart" class="legend-container legend-container-screen" ref="screenLegendArea" v-show="showLegend">
<div v-for="(item, index) in screenLegendList" :title="item.alias?item.alias:item.name" @click="clickScreenLegend(item.name,index)" class="legend-item" :class="{'ft-gr':isGreyScreen[index]}" :key="'legend_' + item.name+'_'+index">
<span class="legend-shape" :style="{background:(isGreyScreen[index]?'#D3D3D3':item.color)}"></span>{{item.alias?item.alias:item.name}}
<br/><!--bgColorList[index]-->
</div>
</div>
</template>
<template v-else>
<div @mouseenter="mouseEnterFullChart" @mouseleave="mouseLeaveFullChart" class="legend-container legend-container-screen" ref="screenLegendArea" v-show="showLegend">
<table style="width: 100%" border="0" cellpadding="0" cellspacing="0">
<th style="width: 100%"></th>
<template v-for="(legendOption, index) in screenLegendOptions">
<th v-if="legendOption.value == 'on'" :key="index" class="option-th legend-option-cell" >
<span @click="legendValueSort(legendOption,screenLegendList,screenLegendOptions)">{{legendOption.option}}</span>
<span ><i style="font-size: 12px !important;" :class="{'nz-icon nz-icon-arrow-down':legendOption.sort =='asc','nz-icon nz-icon-arrow-up':legendOption.sort=='desc'}" ></i></span>
</th>
</template>
<tbody>
<tr v-for="(item, index) in screenLegendList" :key="'legend_' + item.name+'_'+index">
<td>
<div :title="item.alias?item.alias:item.name" @click="clickScreenLegend(item.name,index)" class="legend-item" :class="{'ft-gr':isGreyScreen[index]}" >
<span class="legend-shape" :style="{background:(isGreyScreen[index]?'#D3D3D3':item.color)}"></span>{{item.alias?item.alias:item.name}}
</div>
</td>
<template v-for="(legendOption, i) in screenLegendOptions">
<td v-if="legendOption.value == 'on'" :key="i" class="legend-option-cell">{{formatLegendData(item[legendOption.option])}}</td>
</template>
</tr>
</tbody>
</table>
</div>
</template>
</div>
</template>
<template v-if="chart.type==='singleStat'">
<div id="chartSingleStatPreview" @mouseenter="mouseEnterFullChart" @mouseleave="mouseLeaveFullChart"
style=" height:100%;display:table;text-align:center;width:calc(100% - 16px);color:#000;" class="height-100">
<div class="line-area" ref="singleStatArea" id="singleStatArea" :style="{color:mapping&&mapping.color?mapping.color.text:'#000',background:mapping&&mapping.color?mapping.color.bac:'#fff'}" style="margin-top:0px; text-align:center;
vertical-align: middle;
display:table-cell;
font-size:30px;">
{{serieSingleStat}}
<div class="chart-no-data" v-show="noData">No Data</div>
</div>
</div>
</template>
<template v-if="chart.type==='table'">
<div id="chartTablePreview" :class="{'visible':tableShow,'hidden':!tableShow}" class="height-100">
<el-table :data="seriesItemScreen" border class="nz-table2" height="95%" id="tableContainer" style="margin-top: 10px;" tooltip-effect="light">
<!-- <el-table-column sortable :show-overflow-tooltip="true" prop="metric" :label="$t('dashboard.panel.chartTableColumn.metric')" ></el-table-column>-->
<!-- <el-table-column sortable :show-overflow-tooltip="true" prop="label" :label="$t('dashboard.panel.chartTableColumn.label')" ></el-table-column>-->
<el-table-column sortable prop="time" :label="$t('dashboard.panel.chartTableColumn.time')" width="145" ></el-table-column>
<el-table-column sortable prop="element" :show-overflow-tooltip="true" :label="$t('dashboard.panel.chartTableColumn.element')" >
<template slot-scope="scope">
{{ scope.row.element.alias?scope.row.element.alias:scope.row.element.element}}
</template>
</el-table-column>
<el-table-column sortable prop="value" :label="$t('dashboard.panel.chartTableColumn.value')" width="150" class-name="costom-value">
<template slot-scope="scope">
<div :style="{color:scope.row.mapping?scope.row.mapping.color.text:'#000',background:scope.row.mapping?scope.row.mapping.color.bac:'#fff'}">
<span style="padding: 0px 10px;">{{ scope.row.value}}</span>
</div>
</template>
</el-table-column>
</el-table>
<Pagination :pageObj="screenPageObj" @pageNo='screenPageNo' @pageSize='screenPageSize' ref="Pagination" ></Pagination>
</div>
</template>
<template v-if="chart.type==='url'">
<div id="chartUrlPreview" :class="{'visible':urlShow,'hidden':!urlShow}" class="char-url-preview height-100" >
<iframe id="chartUrlFrame" frameborder="0" width="100%" height="100%" name="showHere" scrolling=auto
style="z-index:5000;"
></iframe>
</div>
</template>
<chart-alert-list v-if="chart.type === 'alertList'" :showTitle="false" ref="alertListChart" id="chartAlertListPreview" :chart-info="chart" :chart-data="chart" class="height-100"></chart-alert-list>
<template v-if="chart.type==='text'">
<div id="chartTextPreview" class="chart-text-preview height-100">
<div style="height: 100%; overflow: auto;">
<div style="height: 100%;" v-html="chart.param.text" ></div>
</div>
</div>
</template>
<template v-if="chart.type==='diagram'">
<div id="chartDiagramPreview" class="chart-text-preview height-100">
<div style="height: 100%; overflow: auto;">
<diagram :topoData="chart.param.topo" :fromChartBox="true" :topologyIndexF="-3"/>
</div>
</div>
</template>
<loading ref="loadingPreview" class="height-100"></loading>
</el-dialog>
</template>
<script>
import axios from 'axios'
import * as echarts from 'echarts'
import bus from '../../libs/bus'
import loading from '../common/loading'
import chartDataFormat from './chartDataFormat'
import { randomcolor } from '../common/js/radomcolor/randomcolor.js'
import chartAlertList from './chart-alert-list'
import chartConfig from '../page/dashboard/overview/chartConfig'
import diagram from '@/components/common/ChartDiagram/diagram'
import moment from 'moment-timezone'
import { formatScientificNotation } from '@/components/common/js/tools'
export default {
name: 'chartPreview',
components: {
loading: loading,
'chart-alert-list': chartAlertList,
diagram
},
props: {
panelId: Number,
fromEndpoint: Boolean
},
data () {
return {
chart: {},
noData: false,
// data: {}, // 该图表信息,chartItem
dailogWidth: '80%',
// seriesItem: [], // 保存信息
isError: false,
errorContent: '',
serieSingleStat: '',
seriesItemScreen: [],
screenPageObj: {
pageNo: 1,
pageSize: this.$CONSTANTS.defaultPageSize,
total: 0
},
unit: {},
// images: '',
// toolbox: false,
items: {
metric_name: [], // 每条数据列名称
xAxis: [],
theData: [] // series数据组
},
isStackArea: false,
echartModalStore: null, // 全屏查看时数据
chartType: 'line', // 图表类型
screenModal: false,
// 查询数据使用
stableFilter: {}, // 保存数据使用,初始化起止时间,单图or多图等
legend: [],
legendList: [],
screenLegendList: [],
isGrey: [],
isGreyScreen: [],
bgColorList: ['#7bbfea', '#b3424a', '#f05b72', '#596032', '#bd6758',
'#cd9a5b', '#918597', '#70a19f', '#005344', '#FF00FF',
'#f7acbc', '#5f5d46', '#66ffff', '#ccFF66', '#f47920',
'#769149', '#1d953f', '#abc88b', '#7f7522', '#9b95c9',
'#f3715c', '#ea66a6', '#d1c7b7', '#9d9087', '#77787b',
'#f58220', '#c37e00', '#00ae9d', '#f26522', '#76becc',
'#76624c', '#d71345', '#2468a2', '#ca8687', '#1b315e'
],
// firstShow: false, // 默认不显示操作按钮,
// dropdownMenuShow:false,
showLegend: true,
tableShow: true,
urlShow: true,
searchTime: bus.getTimezontDateRange(),
// oldSearchTime:[],
minHeight: 500,
chartSpaceHeight: 5, // top-border: 1,bottom-border: 1,padding-bottome:3
titleHeight: 58,
legendHeight: 80,
pageHeight: 40,
hasLegendOptions: false,
screenLegendOptions: [],
mapping: {}
}
},
computed: {
},
watch: {},
methods: {
// 全屏查看
show (chartInfo) {
this.isError = false
this.searchTime = bus.getTimezontDateRange()
this.chart = JSON.parse(JSON.stringify(chartInfo))
const chartType = chartInfo.type
let chartContainerId = 'chartEchartPreview'
if (chartType === 'table') {
this.tableShow = false
chartContainerId = 'chartTablePreview'
} else if (chartType === 'line' || chartType === 'bar' || chartType === 'stackArea' || chartType === 4 || chartType == 'pie') {
this.isGreyScreen = []
this.showLegend = false
chartContainerId = 'chartEchartPreview'
} else if (chartType === 'url') {
this.urlShow = false
chartContainerId = 'chartUrlPreview'
} else if (chartType === 'singleStat') {
chartContainerId = 'chartSingleStatPreview'
} else if (chartType === 'alertList') {
chartContainerId = 'chartAlertListPreview'
} else if (chartType == 'text') {
chartContainerId = 'chartTextPreview'
} else if (chartType == 'diagram') {
chartContainerId = 'chartDiagramPreview'
}
// 设置高度 chart-table
// this.$nextTick(() => {
// // const chartBox = document.getElementById('chartPreviewDailog');
// // let height = this.chart.height;
// let height = Math.round(this.chart.height / 10) * 10
// if (height < this.minHeight) {
// height = this.minHeight
// }
//
// const dailogBox = document.querySelector('#chartPreviewDailog .el-dialog')
// dailogBox.style.height = `${height}px`
//
// // const addChartBox = document.querySelector('.right-box-add-chart')
// // addChartBox.style.cssText = 'z-index:1500 !important';
// if (chartType === 'table') {
// const chartDiv = document.getElementById(chartContainerId)
// chartDiv.style.height = `${height - this.chartSpaceHeight - this.titleHeight - 25}px`
// const tableBox = document.getElementById('tableContainer')
// tableBox.style.height = `${height - this.titleHeight - this.pageHeight - 25}px`// -75-32+25
// } else {
// const chartDiv = document.getElementById(chartContainerId)
// chartDiv.style.height = `${height - this.chartSpaceHeight - this.titleHeight - 15}px`
// }
// })
this.screenModal = true
},
setLoadFrame () {
if (this.chart.type === 'url') {
const that = this
this.$nextTick(() => {
// const iframeBox = document.querySelector('#chartUrlPreview')
const iframe = document.querySelector('#chartUrlFrame')
// 处理兼容行问题
if (iframe.attachEvent) {
iframe.attachEvent('onload', function () {
// iframe加载完毕以后执行操作
that.$refs.loadingPreview.endLoading()
})
} else {
iframe.onload = function () {
// iframe加载完毕以后执行操作
that.$refs.loadingPreview.endLoading()
}
}
iframe.src = this.chart.param.url
})
}
},
filterShowData (source, pageObj) {
return source.slice((pageObj.pageNo - 1) * pageObj.pageSize, pageObj.pageNo * pageObj.pageSize)
},
initDialog () {
// 此时初始化才能获得screenShowArea对象否则此对象为undefined无法初始化图表
this.$refs.pickTime.$refs.timePicker.setCustomTime()
const chartType = this.chart.type
if (chartType === 'table') {
//
} else if (chartType === 'line' || chartType === 'bar' || chartType === 'stackArea' || chartType === 4 || chartType == 'endpointInfo' || chartType == 'pie') {
if (this.echartModalStore) {
this.echartModalStore.clear()
}
} else if (chartType === 'url') {
//
}
// 后台获得数据
if (this.chart.type !== 'url' && this.chart.type !== 'text' && this.chart.type !== 'diagram') {
this.$refs.loadingPreview.startLoading()
this.getChartData()
} else if (this.chart.type == 'url') {
this.$refs.loadingPreview.startLoading()
this.urlShow = true
this.setLoadFrame()
}
},
getAlertListChartData: function (chartInfo, filterType) {
this.$refs.alertListChart.getAlertList(filterType, true, this.chart)
this.$refs.loadingPreview.endLoading()
},
// 获取一个图表具体数据
getChartData () {
const chartItem = this.chart
if (chartItem.type === 'alertList') {
this.getAlertListChartData(chartItem, null)
return
}
const len = chartItem.elements.length
// 没有数据的设置提示信息暂无数据-针对每一个图
if (len === 0) {
this.noData = true
this.$nextTick(() => {
if (chartItem.type === 'table') { // 表格
this.setTableData([])
} else if (chartItem.type === 'line' || chartItem.type === 'bar' || chartItem.type === 'stackArea' || chartItem.type === 4 || chartItem.type === 'endpointInfo' || chartItem == 'pie') {
this.setData(chartItem, [], [])
} else if (chartItem.type === 'singleStat') {
this.serieSingleStat = ''
this.$refs.loadingPreview.endLoading()
}
})
} else {
let startTime = bus.timeFormate(this.searchTime[0], 'yyyy-MM-dd hh:mm:ss')
let endTime = bus.timeFormate(this.searchTime[1], 'yyyy-MM-dd hh:mm:ss')
if (!startTime || !endTime) { // 如果时间为空则默认取最近1小时
const now = new Date(bus.computeTimezone(new Date().getTime()))
startTime = bus.timeFormate(now, 'yyyy-MM-dd hh:mm:ss')
endTime = bus.timeFormate(now.setHours(now.getHours() - 1), 'yyyy-MM-dd hh:mm:ss')
this.searchTime[0] = startTime
this.searchTime[1] = endTime
}
const step = bus.getStep(startTime, endTime)
this.$nextTick(() => {
const axiosArr = chartItem.elements.map((ele) => {
const filterItem = ele
const query = filterItem.expression
// if(chartItem.type === 'table'&&chartItem.param&&chartItem.param.last == 1){
// return this.$get('/prom/api/v1/query_range?query=' + query + "&start=" + this.$stringTimeParseToUnix(endTime) + "&end=" + this.$stringTimeParseToUnix(endTime) + '&step=' + step);
// }
let str = ''
if ((chartItem.type === 'line' || chartItem.type === 'bar' || chartItem.type === 'stackArea' || chartItem.type === 'table') && chartItem.param) { // 如果是这三个 默认给null
chartItem.param.nullType = chartItem.param.nullType || 'null'
str += '&nullType=' + chartItem.param.nullType
}
return this.$get('/prom/api/v1/query_range?query=' + query + '&start=' + this.$stringTimeParseToUnix(startTime) + '&end=' + this.$stringTimeParseToUnix(endTime) + '&step=' + step + str)
})
// 一个图表的所有element单独获取数据
axios.all(axiosArr).then((res) => {
if (res.length > 0) {
const series = []
let singleStatRlt = ''
const legend = []
const tableData = []
let pieSeries
if (chartItem.type === 'pie') {
pieSeries = {
type: 'pie',
radius: '100%',
center: ['50%', '50%'],
top: '20%',
bottom: '20%',
// roseType: 'radius',
minAngle: 10,
itemStyle: {
borderRadius: 5,
borderColor: '#fff',
borderWidth: 1
},
label: {
show: false
},
emphasis: {
label: {
show: false
}
},
data: []
}
}
if (chartItem.type === 'bar' && chartItem.param.statistics && chartItem.param.statistics !== 'null') {
pieSeries = {
type: 'bar',
// roseType: 'radius',
itemStyle: {
borderRadius: 5,
borderColor: '#fff',
borderWidth: 1
},
label: {
show: false
},
emphasis: {
label: {
show: false
}
},
data: []
}
}
res.forEach((response, innerPos) => {
if (response.status === 'success') {
this.isError = false
this.errorContent = ''
if (response.data.result) {
if (chartItem.type === 'singleStat') {
if (response.data.result.length === 1) {
const statistics = chartItem.param.statistics || 'null'
if (response.data.result[0].values) {
singleStatRlt = bus.getSingleStatRlt(statistics, response.data.result[0].values)
this.noData = false
} else {
this.noData = true
}
} else if (response.data.result.length > 1) {
this.noData = true
singleStatRlt = this.$t('dashboard.panel.singleStatErrorTip')
}
} else {
// 循环处理每个elements下获取的数据列
response.data.result.forEach((queryItem, resInnerPos) => {
const seriesItem = {
theData: {
name: '',
symbol: 'emptyCircle', // 去掉点
symbolSize: [2, 2],
smooth: 0.2, // 曲线变平滑
showSymbol: false,
data: [],
type: chartItem.type,
lineStyle: {
width: 1,
opacity: 0.9
}
},
metric_name: ''
}
if (chartItem.type === 'stackArea') {
seriesItem.theData.type = 'line'
seriesItem.theData.stack = chartItem.name
seriesItem.theData.areaStyle = { opacity: 0.3 }
}
if (chartItem.type === 'endpointInfo') {
seriesItem.theData.type = 'line'
}
if ((chartItem.type === 'line' || chartItem.type === 'stackArea' || chartItem.type === 'bar') && chartItem.param && chartItem.param.threshold) {
seriesItem.theData.markLine = {
silent: true,
symbol: ['circle', 'circle'],
label: {
distance: this.computeDistance(chartDataFormat.getUnit(chartItem.unit ? chartItem.unit : 2).compute(chartItem.param.threshold)),
formatter (params) {
return chartDataFormat.getUnit(chartItem.unit ? chartItem.unit : 2).compute(params.value)
}
},
lineStyle: {
color: '#d64f40',
width: 2,
type: 'dotted'
},
data: [{
yAxis: Number(chartItem.param.threshold)
}]
}
}
// 图表中每条线的名字,后半部分
let host = ''// up,
if (queryItem.metric.__name__) {
host = `${queryItem.metric.__name__}{`// up,
}
const tagsArr = Object.keys(queryItem.metric)// ["__name__","asset","idc","instance","job","module","project"]
// 设置时间-数据格式对
const dpsArr = Object.entries(queryItem.values)// [ ["0",[1577959830.781,"0"]], ["1",[1577959845.781,"0"]] ]
// 判断是否有数据, && tagsArr.length > 0
if (dpsArr.length > 0) {
tagsArr.forEach((tag, i) => {
if (tag !== '__name__') {
host += `${tag}="${queryItem.metric[tag]}",`
}
})
if (host.endsWith(',')) {
host = host.substr(0, host.length - 1)
}
if (queryItem.metric.__name__) {
host += '}'
}
if (!host || host === '') {
host = chartItem.elements[innerPos].expression
}
// 处理legend别名
let alias = this.dealLegendAlias(host, chartItem.elements[innerPos].legend)
if (!alias || alias === '') {
alias = host
}
legend.push({ name: host + resInnerPos, alias: alias })
// 图表中每条线的名字,去掉最后的逗号与空格:metric名称, 标签1=a,标签2=c
seriesItem.theData.name = host + resInnerPos
seriesItem.metric_name = seriesItem.theData.name
// 将秒改为毫秒
// alert('table=='+JSON.stringify(queryItem))
seriesItem.theData.data = queryItem.values.map((dpsItem, dpsIndex) => {
// const tData = new Date(dpsItem[0] * 1000)
// const timeTmp = bus.timeFormate(tData, 'yyyy-MM-dd hh:mm:ss')
tableData.push({ // 表格数据
// label: host.slice(host.indexOf('{') + 1,host.indexOf('}')),//label
// metric: queryItem.metric.__name__?queryItem.metric.__name__:'',//metric列
element: { element: host, alias: alias },
// time: timeTmp,//采集时间
// value: dpsItem[1],//数值
data: [dpsItem[0] * 1000, dpsItem[1]]
})
return [dpsItem[0] * 1000, dpsItem[1]]
})
if (chartItem.type === 'pie') {
pieSeries.data.push({ value: bus.getSingleStatRlt(chartItem.param.statistics, seriesItem.theData.data), name: host + resInnerPos })
} else if (chartItem.type === 'bar' && chartItem.param.statistics && chartItem.param.statistics !== 'null') {
pieSeries.data.push({ value: bus.getSingleStatRlt(chartItem.param.statistics, seriesItem.theData.data), name: host + resInnerPos })
} else {
series.push(seriesItem.theData)
}
} else if (chartItem.elements && chartItem.elements[innerPos]) {
// 无数据提示
/*
const currentInfo = chartItem.elements[innerPos];
const errorMsg = `图表 ${chartItem.title} 中 ${currentInfo.metric},${currentInfo.tags} 无数据`;
this.$message.warning({
duration: 15,
content: errorMsg,
closable: true,
});
*/
}
})
}
}
} else {
this.isError = true
if (response.msg) {
this.errorContent = response.msg
} else if (response.error) {
this.errorContent = response.error
} else {
this.errorContent = response
}
}
})
// if(this.$refs.editChart&&this.$refs.editChart[index]) {
if (chartItem.type === 'table') { // 表格
this.setTableData(tableData)
} else if (chartItem.type === 'bar' && chartItem.param.statistics && chartItem.param.statistics !== 'null') {
series.push(pieSeries)
if (series[0].data.length < 1) {
this.noData = true
} else {
this.noData = false
}
this.setData(chartItem, series, legend)
} else if (chartItem.type === 'line' || chartItem.type === 'bar' || chartItem.type === 'stackArea' || chartItem.type === 4 || chartItem.from == 'endpoint') {
if (series.length && chartItem.type === 4) { // 曲线汇总
}
if (series.length < 1) {
this.noData = true
} else {
this.noData = false
}
const _chartItem = JSON.parse(JSON.stringify(chartItem))
if (chartItem.from == this.$CONSTANTS.fromRoute.endpoint) {
_chartItem.type = 'line'
}
this.setData(_chartItem, series, legend)
} else if (chartItem.type === 'singleStat') {
if (Number(singleStatRlt)) {
const singleStatTmp = parseFloat(Number(singleStatRlt).toFixed(2))// parseFloat 如果没有小数点或者小数点后都是零parseFloat() 会返回整数。
if (chartItem.param.valueMapping && chartItem.param.valueMapping.type) {
const type = chartItem.param.valueMapping.type
const 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.mapping = mapping
if (this.mapping && !this.mapping.color) {
this.mapping.color = { bac: '#fff', text: '#000' }
}
this.serieSingleStat = mapping ? mapping.text.replace('{{value}}', singleStatTmp).replace('{{name}}', chartItem.elements[0].legend) : 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
}
this.$refs.loadingPreview.endLoading()
} else if (chartItem.type == 'pie') {
series.push(pieSeries)
if (series[0].data.length < 1) {
this.noData = true
} else {
this.noData = false
}
this.setData(chartItem, series, legend)
}
// }
} else {
const type = chartItem.type
// if(this.$refs.editChart[index]) {
if (type === 'table') {
this.setTableData([])
} else if (type === 'line' || type === 'bar' || type === 'stackArea' || type === 4 || chartItem.type == 'pie') {
this.setData(chartItem, [])
} else if (chartItem.type === 'singleStat') {
this.serieSingleStat = ''
this.$refs.loadingPreview.endLoading()
}
// }
}
}).catch((error) => {
if (error) {
this.$message.error(error.toString())
console.error(error)
}
})
})
}
},
clickScreenLegend (legendName, index) {
/* 点击legend
* 1.当前如果是全高亮状态则全部置灰只留被点击的legend高亮
* 2.如果点击的是唯一高亮的legend则变为全高亮状态
* 3.否则只改变被点击的legend状态
* */
let highlightNum = 0 // 高亮数量
this.isGreyScreen.forEach(g => {
if (!g) {
highlightNum++
}
})
const hasGrey = highlightNum < this.isGreyScreen.length // 是否有置灰的
const curIsGrey = this.isGreyScreen[index] // 当前legend的状态
const currentIsTheOnlyOneHighlight = !curIsGrey && highlightNum === 1 // 当前legend是否是目前唯一高亮的
const echart = this.echartModalStore
if (echart) {
if (!hasGrey) { // 1.除当前legend外全置灰
echart.dispatchAction({
type: 'legendInverseSelect'
})
echart.dispatchAction({
type: 'legendSelect',
name: legendName
})
this.isGreyScreen = this.isGreyScreen.map((g, i) => i !== index)
} else if (currentIsTheOnlyOneHighlight) { // 2.全高亮
echart.dispatchAction({
type: 'legendAllSelect'
})
this.isGreyScreen = this.isGreyScreen.map(() => false)
} else {
const type = curIsGrey ? 'legendSelect' : 'legendUnSelect'
echart.dispatchAction({
type: type,
name: legendName
})
this.$set(this.isGreyScreen, index, !this.isGreyScreen[index])
}
}
// 点击图表某一个legend图表只显示当前点击的曲线或柱状图其它隐藏再次点击已选中的legend ,显示全部
/* let curIsGrey=this.isGreyScreen[index];
if(this.echartModalStore){
this.screenLegendList.forEach((item,i)=>{
let isGrey = this.isGreyScreen[i];
if(index != i){
if(!curIsGrey && !isGrey){
this.echartModalStore.dispatchAction({
type: 'legendUnSelect',
name: item.name
});
}else if(!curIsGrey && isGrey){
this.echartModalStore.dispatchAction({
type: 'legendSelect',
name: item.name
});
}else{
this.echartModalStore.dispatchAction({
type: 'legendUnSelect',
name: item.name
});
}
}else {
this.echartModalStore.dispatchAction({
type: 'legendSelect',
name: item.name
});
}
});
this.isGreyScreen.forEach((item,i)=>{
if(index != i){
if(!curIsGrey && !item){
this.$set(this.isGreyScreen, i, true);
}else if(!curIsGrey && item){
this.$set(this.isGreyScreen, i, false);
}else{
this.$set(this.isGreyScreen, i, true);
}
}else{
if(item === true){
this.$set(this.isGreyScreen, i, false);
}
}
})
} */
},
formatLegend (chartWidth, name) {
if (!name) {
return ''
}
// 计算宽度
const span = document.createElement('span')
const 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
}
const txtWidth = parseFloat(window.getComputedStyle(span).width) - result.width
document.body.removeChild(span)
if (txtWidth < chartWidth) {
return name
} else {
const charNum = `${(chartWidth - 100) / (txtWidth / name.length)}`
return name.slice(0, charNum) + '...'
}
},
// chartSite用于区分是全屏显示还是局部显示
initChart (chartInfo, dataArg, ele, legend) {
const self = this
let minTime = null
let maxTime = null
if (dataArg.length > 0 && dataArg[0].data &&
dataArg[0].data.length > 0 && dataArg[0].data[0].length > 0) {
const len = dataArg[0].data.length - 1
minTime = dataArg[0].data[0][0]
maxTime = dataArg[0].data[len][0]
}
this.chartType = '' // 图表类型
if (chartInfo.type === 4) { // line,bar
this.chartType = 'line'
}
this.echartModalStore = echarts.init(ele)
const chartWidth = ele.clientWidth
/* const title = {
show: false,
text: chartInfo.title || null,
textAlign: 'left',
useHTML: true,
textStyle: {
width: '60%',
fontStyle: 'normal',
fontWeight: 'bold',
color: '#333'
}
} */
const stackIconBorderColor = (chartInfo.type === 'stackArea' ? '#53a3cb' : '#7e7e7e')
const stackIconChooseBorderColor = (chartInfo.type === 'stackArea' ? '#7e7e7e' : '#53a3cb')
const option = {
title: {
show: false
},
color: this.bgColorList,
toolbox: {
show: false,
top: '0',
showTitle: false,
feature: {
dataZoom: {
yAxisIndex: false
},
magicType: {
type: ['stack'],
iconStyle: {
borderColor: stackIconBorderColor
},
emphasis: {
borderColor: stackIconChooseBorderColor
}
}
}
},
tooltip: {
trigger: 'axis',
confine: false,
backgroundColor: 'rgba(221,228,237,1)',
textStyle: {
color: '#000'
},
axisPointer: {
snap: false,
animation: false
},
extraCssText: 'z-index:1000;',
/* enterable:true, 导致tooltip不消失显示多个tooltip */
position: function (point, params, dom, rect, size) {
dom.style.transform = 'translateZ(0)'
// 提示框位置
let x = 0
let y = 0
// 当前鼠标位置
const pointX = point[0]
const pointY = point[1]
// 外层div大小
const viewWidth = size.viewSize[0]
const viewHeight = size.viewSize[1]
// 提示框大小
const boxWidth = size.contentSize[0]
const boxHeight = size.contentSize[1]
if (pointX < (viewWidth / 2)) { // 说明鼠标在左边放不下提示框
x = pointX + 10
} else {
x = pointX - boxWidth
}
if (pointY < (viewHeight / 2)) { // 说明鼠标上面放不下提示框
y = pointY + 10
} else {
y = pointY - boxHeight
}
return [x, y]
},
formatter: function (params) {
let str = '<div>'
let sum = 0
params.forEach((item, i) => {
const tip = legend[item.seriesIndex]
const color = self.bgColorList[item.seriesIndex]
if (i === 0) {
const value = bus.computeTimezone(item.data[0])
const tData = new Date(value)
str += [tData.getFullYear(), tData.getMonth() + 1, tData.getDate()].join('-') + ' ' +
[tData.getHours(), tData.getMinutes(), tData.getSeconds()].join(':')
str += '<br/>'
}
const val = formatScientificNotation(item.data[1], 2)
sum += self.numberWithEConvent(val)
str += '<div style="white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;display: flex; justify-content: space-between; min-width: 150px; max-width: 600px; line-height: 18px; font-size: 12px;">'
str += `<div style="max-width: 500px;white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;"><span style='display:inline-block;margin-right:5px;border-radius:10px;width:15px;height:5px;background-color: ${color};}'></span>${tip ? (tip.alias ? tip.alias : tip.name) : item.seriesName} </div>`
str += '<div style="padding-left: 10px;">'
str += (function () {
if (chartInfo.from == 'endpoint') {
return val == 1 ? 'up' : 'down'
}
return chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(val, null, 2)
}())
str += '</div>'
str += '</div>'
})
if (chartInfo.type === 'stackArea' || self.isStackArea) {
sum = parseFloat(Number(sum).toFixed(2))
str += '<div style="white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;display: flex; justify-content: space-between; min-width: 150px; max-width: 600px; line-height: 18px; font-size: 12px;">'
str += '<div style="line-height: 18px; font-size: 12px;padding-left:20px;">'
str += self.$t('dashboard.panel.chartTotal')
str += '</div>'
str += '<div style="padding-left: 10px;">'
str += chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(sum, null, 2)
str += '</div>'
str += '</div>'
}
str += '</div>'
return str
}
},
legend: {
show: false
},
grid: {
top: 30,
left: 0,
right: 30,
containLabel: true,
bottom: 20// 156
},
xAxis: {
type: 'time',
// boundaryGap: false,//line-false; bar-true;
// data: ['20190101', '20190102', '周三', '周四', '周五', '周六', '周日']
axisLabel: {
interval: 0,
rotate: 0,
formatter: function (value) {
value = bus.computeTimezone(value)
const tData = new Date(value)
let hour = tData.getHours()
hour = hour > 9 ? hour : '0' + hour // 加0补充为两位数字
let minute = tData.getMinutes()
minute = minute > 9 ? minute : '0' + minute // 如果分钟小于10,则在前面加0补充为两位数字
if (minTime !== null && maxTime !== null) {
const diffSec = (maxTime - minTime) / 1000
const secOneDay = 24 * 60 * 60// 1天的秒数
const secOneMonth = secOneDay * 30// 30天的秒数
if (diffSec <= secOneDay) { // 同一天
return [hour, minute].join(':')
} else if (diffSec < secOneMonth) { // 大于1天小于30天
return [tData.getMonth() + 1, tData.getDate()].join('/') + ' ' + [hour, minute].join(':')
} else { // 大于等于30天
return [tData.getMonth() + 1, tData.getDate()].join('/')
}
} else {
return [tData.getFullYear(), tData.getMonth() + 1, tData.getDate()].join('/') + '\n' +
[hour, minute].join(':')
}
}
},
axisPointer: { // y轴上显示指针对应的值
show: true
},
splitLine: {
show: true
}
},
yAxis: {
type: 'value',
minInterval: 1,
splitLine: {
show: true
},
// 去掉y轴--start
axisLine: {
show: false
},
axisTick: {
show: false
},
// 去掉y轴--end
axisLabel: {
formatter: function (value, index) {
if (self.chart.from == 'endpoint') {
if (value == 1) {
return 'up'
} else {
return 'down'
}
}
let chartUnit = chartInfo.unit
chartUnit = chartUnit || 2
const unit = chartDataFormat.getUnit(chartUnit)
return unit.compute(value, index)
}
}
// boundaryGap:[0,0.2]
},
useUTC: false, // 使用本地时间
series: dataArg
}
if (this.echartModalStore) {
this.echartModalStore.clear()
}
option.title = {}
// this.echartModalStore.setOption(option);//显示全屏界面
this.showLegend = true
if (legend) {
this.screenLegendList = []
legend.forEach((item, i) => {
const _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(() => {
const divHeight = this.$refs.screenLegendArea.offsetHeight
this.echartModalStore.resize()
this.echartModalStore.clear()
if (dataArg.length < 1) {
this.echartModalStore.setOption(chartConfig.getOption('noData'))
} else {
this.echartModalStore.setOption(option)// 创建图表
}
this.$refs.loadingPreview.endLoading()
})
this.echartModalStore.on('magictypechanged', function (params) {
self.isStackArea = !self.isStackArea
if (self.isStackArea) {
this.setOption({
toolbox: {
feature: {
magicType: {
iconStyle: {
borderColor: '#7e7e7e'
}
}
}
},
tooltip: {
axisPointer: {
snap: false,
animation: false
},
formatter: function (params) {
let str = '<div>'; let sum = 0
params.forEach((item, i) => {
const tip = legend[item.seriesIndex]
const color = self.bgColorList[item.seriesIndex]
if (i === 0) {
const value = bus.computeTimezone(item.data[0])
const tData = new Date(value)
str += [tData.getFullYear(), tData.getMonth() + 1, tData.getDate()].join('-') + ' ' +
[tData.getHours(), tData.getMinutes(), tData.getSeconds()].join(':')
str += '<br/>'
}
const val = formatScientificNotation(item.data[1], 2)
sum += self.numberWithEConvent(val)
str += '<div style="white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;display: flex; justify-content: space-between; min-width: 150px; max-width: 600px; line-height: 18px; font-size: 12px;">'
str += `<div style="max-width: 500px;white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;"><span style='display:inline-block;margin-right:5px;border-radius:10px;width:15px;height:5px;background-color: ${color};}'></span>${tip ? (tip.alias ? tip.alias : tip.name) : item.seriesName} </div>`
str += '<div style="padding-left: 10px;">'
str += chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(val, null, 2)
str += '</div>'
str += '</div>'
})
if (chartInfo.type === 'stackArea' || self.isStackArea) {
sum = parseFloat(Number(sum).toFixed(2))
str += '<div style="white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;display: flex; justify-content: space-between; min-width: 150px; max-width: 600px; line-height: 18px; font-size: 12px;">'
str += '<div style="line-height: 18px; font-size: 12px;padding-left:20px;">'
str += self.$t('dashboard.panel.chartTotal')
str += '</div>'
str += '<div style="padding-left: 10px;">'
str += chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(sum, null, 2)
str += '</div>'
str += '</div>'
}
str += '</div>'
return str
}
}
})
}
})
setTimeout(() => {
this.echartModalStore.resize()
}, 100)
},
initStaticsBar: function (chartInfo, dataArg, legend) {
const self = this
const option = {
title: {
show: false
},
legend: {
show: false
},
grid: {
top: 30,
left: 10,
right: 30,
containLabel: true,
bottom: 8// 156
},
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(221,228,237,1)',
borderColor: 'rgba(255,255,255,0)',
textStyle: {
color: '#000'
},
axisPointer: {
snap: false,
animation: false
},
extraCssText: 'z-index:1000;'
},
series: null
}
option.xAxis = {
type: 'category',
axisLine: {
show: false
},
axisTick: {
show: false
},
showAllSymbol: false,
axisPointer: { // y轴上显示指针对应的值
show: false
},
axisLabel: {
margin: 8,
formatter: function (params) {
const dataLength = dataArg.length || 1
const chartWidth = (document.getElementById('chartEchartPreview').offsetWidth - 80) / dataLength// 容器宽 - padding - 空余
const length = Math.ceil((chartWidth) / 16)
let val = ''
if (params.length > length) {
val = params.substr(0, length) + '...'
return val
} else {
return params
}
}
}
}
const nweSeriesItem = []
dataArg[0].data.forEach((item, index) => {
const obj = { ...dataArg[0] }
const legendShow = legend[index].alias ? legend[index].alias : legend[index].name
obj.name = legend[index].name
obj.data = [[legendShow, item.value]]
obj.stack = true
if (chartInfo.param.threshold) {
obj.markLine = {
silent: true,
symbol: ['circle', 'circle'],
label: {
distance: this.computeDistance(chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(chartInfo.param.threshold)),
formatter: function (params) {
return chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(params.value)
}
},
lineStyle: {
color: '#d64f40',
width: 2,
type: 'dotted'
},
data: [{
yAxis: Number(chartInfo.param.threshold)
}]
}
}
nweSeriesItem.push(obj)
})
dataArg = nweSeriesItem
option.yAxis = {
type: 'value',
minInterval: 1,
splitLine: {
show: true
},
// 去掉y轴--start
axisLine: {
show: false
},
axisTick: {
show: false
},
// 去掉y轴--end
axisLabel: {
formatter: function (value, index) {
if (self.chart.from && self.chart.from == 'endpoint') {
if (value == 1) {
return 'up'
} else {
return 'down'
}
}
let chartUnit = chartInfo.unit
chartUnit = chartUnit || 2
const unit = chartDataFormat.getUnit(chartUnit)
return unit.compute(value, index)
}
}
// boundaryGap:[0,0.2]
}
option.series = dataArg
option.color = this.bgColorList
option.tooltip.formatter = this.barFormatterFunc
const dom = document.getElementById('screenShowArea')
this.echartModalStore = echarts.init(dom)
this.showLegend = true
if (legend) {
this.screenLegendList = []
legend.forEach((item, i) => {
const _legend = {
name: item.name,
alias: item.alias,
color: item.color,
showText: this.formatLegend(dom.clientWidth, item.alias ? item.alias : item.name)
}
this.screenLegendList.push(_legend)
this.isGreyScreen.push(false)
})
}
this.$nextTick(() => {
const divHeight = this.$refs.screenLegendArea.offsetHeight
this.echartModalStore.resize()
this.echartModalStore.clear()
if (dataArg[0].data.length < 1) {
this.echartModalStore.setOption(chartConfig.getOption('noData'))
} else {
this.echartModalStore.setOption(option)// 创建图表
}
this.$refs.loadingPreview.endLoading()
})
},
computeDistance: function (str) {
const span = document.querySelector('.temp-dom')
span.textContent = str
const txtWidth = parseFloat(window.getComputedStyle(span).width)
return Number('-' + (txtWidth + 5))
},
initPie: function (chartInfo, dataArg, legend) {
const option = {
title: {
show: false
},
legend: {
show: false
},
grid: {
left: 'center',
top: 'middle'
},
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(221,228,237,1)',
borderColor: 'rgba(255,255,255,0)',
textStyle: {
color: '#000'
},
axisPointer: {
snap: false,
animation: false
},
extraCssText: 'z-index:1000;'
},
series: null
}
option.series = dataArg
option.color = this.bgColorList
option.tooltip.formatter = this.pieFormatterFunc
const dom = document.getElementById('screenShowArea')
this.echartModalStore = echarts.init(dom)
this.showLegend = true
if (legend) {
this.screenLegendList = []
legend.forEach((item, i) => {
const _legend = {
name: item.name,
alias: item.alias,
color: item.color,
showText: this.formatLegend(dom.clientWidth, item.alias ? item.alias : item.name)
}
this.screenLegendList.push(_legend)
this.isGreyScreen.push(false)
})
}
this.$nextTick(() => {
const divHeight = this.$refs.screenLegendArea.offsetHeight
this.echartModalStore.resize()
this.echartModalStore.clear()
if (dataArg[0].data.length < 1) {
this.echartModalStore.setOption(chartConfig.getOption('noData'))
} else {
this.echartModalStore.setOption(option)// 创建图表
}
this.$refs.loadingPreview.endLoading()
})
},
mouseEnterFullChart () {
if (this.echartModalStore) {
this.echartModalStore.setOption({
toolbox: {
show: true
}
})
}
},
mouseLeaveFullChart () {
if (this.echartModalStore) {
this.echartModalStore.setOption({
toolbox: {
show: false
}
})
}
},
setColor (colorNum) {
this.bgColorList = []
for (let i = 0; i < colorNum; i++) {
this.bgColorList.push(randomcolor())
}
},
// 设置数据
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) {
const sortedOptionKeys = ['min', 'max', 'avg', 'last', 'total']
this.screenLegendOptions = sortedOptionKeys.map(item => { return { option: item, sort: '', value: chartItem.param.legendValue[item] } })
}
if (chartItem.type == 'pie') {
this.initPie(chartItem, seriesItem, legend)
return
}
if (chartItem.type === 'bar' && chartItem.param.statistics && chartItem.param.statistics !== 'null') {
this.initStaticsBar(chartItem, seriesItem, legend)
return
}
this.initChart(chartItem, seriesItem, this.$refs.screenShowArea, legend)
},
// 设置数据
setTableData (seriesItem) {
this.unit = chartDataFormat.getUnit(this.chart.unit)
const statistics = this.chart.param.statistics
seriesItem = this.getStatisticsResult(statistics, seriesItem)
seriesItem = seriesItem.map(item => {
if (this.chart.param.valueMapping && this.chart.param.valueMapping.type) {
const type = this.chart.param.valueMapping.type
const mappings = this.chart.param.valueMapping.mapping ? this.chart.param.valueMapping.mapping : []
const value = Number(item.value)
let mapping
if (type == 'value') {
mapping = mappings.find(t => { return Number(t.value) == value })
} else {
mapping = mappings.find(t => { return Number(t.from) <= value && Number(t.to) >= value })
}
this.mapping = mapping
item.mapping = mapping
item.value = mapping ? mapping.text.replace('{{value}}', item.value).replace('{{name}}', item.element.alias) : this.unit.compute(value, null, 2)
}
return item
})
this.storedScreanTableData = seriesItem
this.storedScreanTableData = Object.assign([], this.storedScreanTableData.reverse())
this.screenPageObj.total = this.storedScreanTableData.length
this.seriesItemScreen = this.filterShowData(this.storedScreanTableData, this.screenPageObj)
this.tableShow = true
this.$refs.loadingPreview.endLoading()
},
getStatisticsResult: function (statistics, seriesItem) {
if (!seriesItem || !seriesItem.length > 0) return []
if (!statistics) return seriesItem
const copy = JSON.parse(JSON.stringify(seriesItem))
copy.sort((x, y) => { return parseFloat(y.data[0]) - parseFloat(x.data[0]) })
const classifies = []
let maxGroup = 0
const map = new Map()// 用于记录在第几组
copy.forEach(item => {
const element = item.element.element
const group = map.get(element)
if (typeof group != 'undefined') {
classifies[group].push(item)
} else {
classifies.push([item])
map.set(element, maxGroup++)
}
})
let result
switch (statistics) {
case 'null': {
result = copy.map(item => {
return {
element: item.element,
time: bus.timeFormate(new Date(item.data[0]), 'yyyy-MM-dd hh:mm:ss'),
value: item.data[1]
}
})
break
}
case 'min': {
result = classifies.map(group => {
const groupMin = group.sort((x, y) => {
return parseFloat(x.data[1]) - parseFloat(y.data[1])
})[0]
return {
element: groupMin.element,
time: bus.timeFormate(new Date(groupMin.data[0]), 'yyyy-MM-dd hh:mm:ss'),
value: groupMin.data[1]
}
})
break
}
case 'max': {
result = classifies.map(group => {
const groupMax = group.sort((x, y) => {
return parseFloat(y.data[1]) - parseFloat(x.data[1])
})[0]
return {
element: groupMax.element,
time: bus.timeFormate(new Date(groupMax.data[0]), 'yyyy-MM-dd hh:mm:ss'),
value: groupMax.data[1]
}
})
break
}
case 'average': {
result = classifies.map(group => {
const groupData = group.map(t => parseFloat(t.data[1]))
const sum = eval(groupData.join('+'))
const avg = sum / groupData.length
const last = group.sort((x, y) => {
return parseFloat(y.data[0]) - parseFloat(x.data[0])
})[0]
return {
element: last.element,
time: bus.timeFormate(new Date(last.data[0]), 'yyyy-MM-dd hh:mm:ss'),
value: avg
}
})
break
}
case 'total': {
result = classifies.map(group => {
const groupData = group.map(t => parseFloat(t.data[1]))
const total = eval(groupData.join('+'))
const last = group.sort((x, y) => {
return parseFloat(y.data[0]) - parseFloat(x.data[0])
})[0]
return {
element: last.element,
time: bus.timeFormate(new Date(last.data[0]), 'yyyy-MM-dd hh:mm:ss'),
value: total
}
})
break
}
case 'first': {
result = classifies.map(group => {
const first = group.sort((x, y) => {
return parseFloat(y.data[0]) - parseFloat(x.data[0])
})[copy.length - 1]
return {
element: first.element,
time: bus.timeFormate(new Date(first.data[0]), 'yyyy-MM-dd hh:mm:ss'),
value: first.data[1]
}
})
break
}
case 'last': {
result = classifies.map(group => {
const last = group.sort((x, y) => {
return parseFloat(y.data[0]) - parseFloat(x.data[0])
})[0]
return {
element: last.element,
time: bus.timeFormate(new Date(last.data[0]), 'yyyy-MM-dd hh:mm:ss'),
value: last.data[1]
}
})
break
}
case 'range': {
result = classifies.map(group => {
const sort = JSON.parse(JSON.stringify(group)).sort((x, y) => {
return parseFloat(y.data[1]) - parseFloat(x.data[1])
})
const last = group.sort((x, y) => {
return parseFloat(y.data[0]) - parseFloat(x.data[0])
})[0]
const max = sort[0]
const min = sort[sort.length - 1]
const range = max.data[1] - min.data[1]
return {
element: last.element,
time: bus.timeFormate(new Date(last.data[0]), 'yyyy-MM-dd hh:mm:ss'),
value: range
}
})
break
}
case 'different': {
result = classifies.map(group => {
const sort = group.sort((x, y) => {
return parseFloat(y.data[0]) - parseFloat(x.data[0])
})
const last = sort[0]
const first = sort[copy.length - 1]
return {
element: last.element,
time: bus.timeFormate(new Date(last.data[0]), 'yyyy-MM-dd hh:mm:ss'),
value: last.data[1] - first.data[1]
}
})
break
}
}
return result
},
clearChart () {
if (this.echartModalStore) {
this.echartModalStore.clear()
}
this.chart = {}
// this.seriesItem = [];
this.seriesItemScreen = []
this.searchTime[0] = new Date().setHours(new Date().getHours() - 1)
this.searchTime[1] = new Date()
const iframe = document.querySelector('#chartUrlFrame')
if (iframe) {
iframe.src = ''
}
this.isStackArea = false
this.serieSingleStat = ''
},
// 重新请求数据 刷新操作
/*
refreshChart() {
//this.dropdownMenuShow=false;
this.clearChart();
this.$refs['localLoading'+this.chartIndex].startLoading();
//this.firstShow = false;
this.$emit('on-refresh-data', this.data.id);
},
*/
dateChange (time) {
const chartType = this.chart.type
if (chartType === 'table') {
this.seriesItemScreen = []
for (let i = 0; i < 8; i++) {
this.seriesItemScreen.push({ // 表格数据
// label: '',//label
// metric: '',//metric列
element: '',
time: '', // 采集时间
value: ''// 数值
})
}
this.tableShow = false
this.$refs.loadingPreview.startLoading()
this.getChartData()
} else if (chartType === 'line' || chartType === 'bar' || chartType === 'stackArea' || chartType === 4 || this.chart.from == 'endpoint') {
this.echartModalStore.clear()
if (this.chart.from != this.$CONSTANTS.fromRoute.endpoint) {
this.showLegend = false
}
this.$refs.loadingPreview.startLoading()
this.getQueryChart()
} else if (chartType === 'singleStat') {
this.serieSingleStat = ''
this.$refs.loadingPreview.startLoading()
this.getChartData()
}
},
// 查询数据,修改日期查询全屏数据
getQueryChart (type) {
let axiosArr = []
const chartItem = this.chart
this.$nextTick(() => {
let startTime = bus.timeFormate(this.searchTime[0], 'yyyy-MM-dd hh:mm:ss')
let endTime = bus.timeFormate(this.searchTime[1], 'yyyy-MM-dd hh:mm:ss')
if (!startTime || !endTime) { // 如果时间为空则默认取最近1小时
const now = new Date()
startTime = bus.timeFormate(now, 'yyyy-MM-dd hh:mm:ss')
endTime = bus.timeFormate(now.setHours(now.getHours() - 1), 'yyyy-MM-dd hh:mm:ss')
this.searchTime[0] = startTime
this.searchTime[1] = endTime
}
const step = bus.getStep(startTime, endTime)
axiosArr = this.chart.elements.map((ele) => {
const filterItem = ele
const query = filterItem.expression
let str = ''
if ((chartItem.type === 'line' || chartItem.type === 'bar' || chartItem.type === 'stackArea' || chartItem.type === 'table') && chartItem.param) { // 如果是这三个 默认给null
chartItem.param.nullType = chartItem.param.nullType || 'null'
str += '&nullType=' + chartItem.param.nullType
}
if (chartItem.type === 'table' && chartItem.param && chartItem.param.last == 1) {
return this.$get('/prom/api/v1/query_range?query=' + query + '&start=' + this.$stringTimeParseToUnix(endTime) + '&end=' + this.$stringTimeParseToUnix(endTime) + '&step=' + step + str)
}
return this.$get('/prom/api/v1/query_range?query=' + query + '&start=' + this.$stringTimeParseToUnix(startTime) + '&end=' + this.$stringTimeParseToUnix(endTime) + '&step=' + step + str)
})
// 一个图表
axios.all(axiosArr).then(res => {
if (res.length > 0) {
const series = []
const legend = []
/* const sumData = {
name: 'sum',
data: [],
visible: true,
threshold: null
} */
if (!this.chart.type || this.chart.from == 'endpoint') {
this.chart.type = 'line'
}
res.forEach((response, pos) => {
if (response.status === 'success') {
this.isError = false
this.errorContent = ''
if (response.data.result) {
// 循环处理每个elements下获取的数据列
response.data.result.forEach((queryItem, innerPos) => {
const seriesItem = {
theData: {
name: '',
symbol: 'emptyCircle', // 去掉点
symbolSize: [2, 2],
smooth: 0.2, // 曲线变平滑
showSymbol: false,
data: [],
type: this.chart.type,
lineStyle: {
width: 1,
opacity: 0.9
}
// visible: true,
// threshold: null,
},
metric_name: ''
}
if (this.chart.type === 'stackArea') {
seriesItem.theData.type = 'line'
seriesItem.theData.stack = this.chart.name
seriesItem.theData.areaStyle = { opacity: 0.3 }
}
// 图表中每条线的名字,后半部分
// let host = `${queryItem.metric.__name__}{`;//up,
let host = ''// up,
if (queryItem.metric.__name__) {
host = `${queryItem.metric.__name__}{`// up,
}
const tagsArr = Object.keys(queryItem.metric)// ["__name__","asset","idc","instance","job","module","project"]
// 设置时间-数据格式对
const dpsArr = Object.entries(queryItem.values)// [ ["0",[1577959830.781,"0"]], ["1",[1577959845.781,"0"]] ]
// 判断是否有数据,&& tagsArr.length > 0
if (dpsArr.length > 0) {
tagsArr.forEach((tag, i) => {
if (tag !== '__name__') {
host += `${tag}="${queryItem.metric[tag]}",`
}
})
if (host.endsWith(',')) { host = host.substr(0, host.length - 1) }
if (queryItem.metric.__name__) {
host += '}'
}
if (!host || host === '') {
host = this.chart.elements[pos].expression
}
let alias = this.dealLegendAlias(host, this.chart.elements[pos].legend)
if (!alias || alias === '') {
alias = host
}
legend.push({ name: host + innerPos, alias: alias })
// 图表中每条线的名字,去掉最后的逗号与空格:metric名称, 标签1=a,标签2=c
seriesItem.theData.name = host + innerPos
seriesItem.metric_name = seriesItem.theData.name
// 将秒改为毫秒
// alert('table=='+JSON.stringify(queryItem))
seriesItem.theData.data = queryItem.values.map((dpsItem, dpsIndex) => {
return [dpsItem[0] * 1000, dpsItem[1]]
})
series.push(seriesItem.theData)
} else if (this.chart.elements && this.chart.elements[innerPos]) {
// 无数据提示
}
})
}
} else {
this.isError = true
if (response.msg) {
this.errorContent = response.msg
} else if (response.error) {
this.errorContent = response.error
} else {
this.errorContent = response
}
}
})
this.setColor(legend.length)
this.initChart(this.chart, series, this.$refs.screenShowArea, legend)
}
}).catch(error => {
if (error) {
this.$message.error(error.toString())
}
})
})
},
dealLegendAlias: function (legend, expression) {
if (/\{\{.+\}\}/.test(expression)) {
const labelValue = expression.replace(/(\{\{.+?\}\})/g, function (i) {
const label = i.substr(i.indexOf('{{') + 2, i.indexOf('}}') - i.indexOf('{{') - 2)
const reg = new RegExp(',' + label + '=".+?"')
const reg1 = new RegExp('{' + label + '=".+?"')
let value = null
if (reg.test(legend)) {
const find = legend.match(reg)[0]
value = find.substr(find.indexOf('"') + 1, find.lastIndexOf('"') - find.indexOf('"') - 1)
} else if (reg1.test(legend)) {
const find = legend.match(reg1)[0]
value = find.substr(find.indexOf('"') + 1, find.lastIndexOf('"') - find.indexOf('"') - 1)
}
return value || label
})
return labelValue
} else {
return expression
}
},
screenPageNo (val) {
this.screenPageObj.pageNo = val
this.seriesItemScreen = this.filterShowData(this.storedScreanTableData, this.screenPageObj)
},
screenPageSize (val) {
this.screenPageObj.pageSize = val
this.seriesItemScreen = this.filterShowData(this.storedScreanTableData, this.screenPageObj)
},
handleClose () {
this.screenModal = false
this.clearChart()
},
findLegendOptions: function () {
if ((!this.chart.param) || (!this.chart.param.legendValue) || Object.keys(this.chart.param.legendValue) < 1) return false
const legendOptions = this.chart.param.legendValue
const onVal = Object.keys(legendOptions).find(item => { return legendOptions[item] == 'on' })
return onVal
},
computeLegendData: function (legend, dataArr) {
const options = this.screenLegendOptions
const keys = options.filter(item => { return item.value == 'on' }).map(item => { return item.option })
const $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 => {
const data = dataArr.find(t => t.name == item.name)
if (data && data.data) {
const copy = JSON.parse(JSON.stringify(data.data))
const 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 => {
const data = dataArr.find(t => t.name == item.name)
if (data && data.data) {
const copy = JSON.parse(JSON.stringify(data.data))
const 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 => {
const 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]))
const sum = eval(copy.join('+'))
const avg = sum / copy.length
item.avg = avg
}
})
resolve()
})
},
legendLastValue: function (legend, dataArr) {
return new Promise(resolve => {
legend.forEach(item => {
const data = dataArr.find(t => t.name == item.name)
if (data && data.data) {
const copy = JSON.parse(JSON.stringify(data.data))
const 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 => {
const 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]))
const 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'
}
},
pieFormatterFunc: function (params, ticket, callback) {
const chartInfo = this.chart
return `
<div>
<div style="white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis; min-width: 150px; max-width: 600px; line-height: 18px; font-size: 14px;">
<div style="max-width: 500px;white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis;margin-bottom: 5px">${this.legend[params.dataIndex].alias}</div>
<div style="font-size:12px;display:flex;justify-content: space-between;">
<div>value</div>
<div>${chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(params.value, null, -1, 2)}</div>
</div>
<div style="font-size:12px;display:flex;justify-content: space-between;">
<div>percent</div>
<div>${params.percent}%</div>
</div>
</div>
</div>
`
},
barFormatterFunc: function (params, ticket, callback) {
const chartInfo = this.chart
return `
<div>
<div style="white-space:nowrap;overflow-x:hidden;text-overflow:ellipsis; min-width: 150px; max-width: 600px; line-height: 18px; font-size: 14px;">
<div style="max-width: 500px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-bottom: 5px">${this.legend[params.seriesIndex].alias}</div>
<div style="font-size:12px;display:flex;justify-content: space-between;">
<div>value</div>
<div>${chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(params.value[1], null, -1, 2)}</div>
</div>
</div>
</div>
`
}
},
beforeDestroy () {
this.clearChart()
if (this.echartModalStore) {
this.echartModalStore.off('magictypechanged')
}
}
}
</script>