NEZ-3319 feat:dashboard bar图表类型样式优化

This commit is contained in:
zyh
2023-11-15 16:09:04 +08:00
parent 017fd56a21
commit 68b10ef24b
13 changed files with 816 additions and 154 deletions

View File

@@ -17732,7 +17732,7 @@
}, },
"node-sass": { "node-sass": {
"version": "4.14.1", "version": "4.14.1",
"resolved": "https://registry.npmmirror.com/node-sass/-/node-sass-4.14.1.tgz", "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.14.1.tgz",
"integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==", "integrity": "sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g==",
"dev": true, "dev": true,
"requires": { "requires": {
@@ -24364,7 +24364,7 @@
}, },
"webpack-bundle-analyzer": { "webpack-bundle-analyzer": {
"version": "2.13.1", "version": "2.13.1",
"resolved": "https://registry.npmmirror.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz", "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz",
"integrity": "sha512-rwxyfecTAxoarCC9VlHlIpfQCmmJ/qWD5bpbjkof+7HrNhTNZIwZITxN6CdlYL2axGmwNUQ+tFgcSOiNXMf/sQ==", "integrity": "sha512-rwxyfecTAxoarCC9VlHlIpfQCmmJ/qWD5bpbjkof+7HrNhTNZIwZITxN6CdlYL2axGmwNUQ+tFgcSOiNXMf/sQ==",
"dev": true, "dev": true,
"requires": { "requires": {

View File

@@ -876,3 +876,155 @@
height: auto !important; height: auto !important;
} }
} }
.bar-item {
&:last-of-type{
margin: 0 !important;
}
}
.bar-basic-vertical{
width: 100%;
height: 100%;
display: flex;
overflow: hidden;
.bar-rect{
border-radius: 2px;
background: $--chart-bar-background;
display: flex;
flex-direction: column;
justify-content: flex-end;
.bar-actual{
border-radius: 2px;
}
}
.bar-value{
display: flex;
justify-content: center;
align-items: center;
white-space: nowrap;
line-height: 1;
color: $--color-text-primary;
}
.bar-title{
width: 100%;
font-size: 14px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
text-align: center;
line-height: 1.5;
color: $--color-text-primary;
}
}
.bar-basic-horizontal{
width: 100%;
height: 100%;
overflow: hidden;
.bar-item{
display: flex;
align-items: center;
overflow: hidden;
}
.bar-rect{
border-radius: 2px;
background: $--chart-bar-background;
.bar-actual{
border-radius: 2px;
}
}
.bar-title{
padding-right: 10px;
box-sizing: border-box;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
text-align: right;
color: $--color-text-primary;
}
.bar-value{
padding: 0 10px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: flex-end;
white-space: nowrap;
color: $--color-text-primary;
}
}
.bar-retro-vertical{
width: 100%;
height: 100%;
display: flex;
overflow: hidden;
.bar-item{
display: flex;
overflow: hidden;
flex-direction: column-reverse;
}
.bar-rect{
display: flex;
flex-direction: column-reverse;
align-items: center;
.bar-cell{
border-radius: 2px;
}
}
.bar-value{
display: flex;
justify-content: center;
align-items: center;
white-space: nowrap;
line-height: 1;
color: $--color-text-primary;
}
.bar-title{
width: 100%;
font-size: 14px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
text-align: center;
line-height: 1.5;
color: $--color-text-primary;
}
}
.bar-retro-horizontal{
width: 100%;
height: 100%;
overflow: hidden;
.bar-item{
display: flex;
align-items: center;
overflow: hidden;
.bar-rect{
display: flex;
align-items: center;
.bar-cell{
border-radius: 2px;
}
}
}
.bar-title{
padding-right: 10px;
box-sizing: border-box;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
text-align: right;
color: $--color-text-primary;
}
.bar-value{
padding: 0 10px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: flex-end;
white-space: nowrap;
color: $--color-text-primary;
}
}

View File

@@ -261,6 +261,7 @@ $--chart-border-color: $--border-color-light;
$--chart-title-hover-background-color: #323238; $--chart-title-hover-background-color: #323238;
$--chart-box-border-color: $--border-color-light; $--chart-box-border-color: $--border-color-light;
$--chart-shadow: -1px 0 8px -3px rgba(0,0,0,0.8); $--chart-shadow: -1px 0 8px -3px rgba(0,0,0,0.8);
$--chart-bar-background: #2D2E35;
/* 14.popover */ /* 14.popover */
$--popover-background-color: $--background-color-empty; $--popover-background-color: $--background-color-empty;

View File

@@ -258,6 +258,7 @@ $--chart-border-color: $--color-text-label;
$--chart-title-hover-background-color: $--background-color-1; $--chart-title-hover-background-color: $--background-color-1;
$--chart-box-border-color: $--border-color-light; $--chart-box-border-color: $--border-color-light;
$--chart-shadow: -1px 0 8px -3px #ccc; $--chart-shadow: -1px 0 8px -3px #ccc;
$--chart-bar-background: #F4F5F5;
/* 14.popover */ /* 14.popover */
$--popover-background-color: $--color-text-label; $--popover-background-color: $--color-text-label;

View File

@@ -1,19 +1,265 @@
<template> <template>
<div <div
:class="legendPlacement"
ref="bar-chart-box" ref="bar-chart-box"
class="nz-chart__component nz-chart__component--time-series" @mouseenter="mouseEnterChart" class="nz-chart__component" @mouseenter="mouseEnterChart"
@mouseleave="mouseLeaveChart" @mouseleave="mouseLeaveChart"
> >
<div :id="`chart-canvas-${chartId}`" class="chart__canvas" :class="{'chart-cursor-default':!(dataLink.length || chartInfo.datasource === 'metrics' || chartInfo.datasource === 'logs')}"></div> <div :id="`chart-canvas-${chartId}`" class="chart__canvas" style="overflow: hidden;" :style="{padding:boxPadding + 'px'}">
<chart-legend <!-- retro led -->
v-if="hasLegend" <template v-if="chartInfo.param.displayMode==='led'">
:chart-data="chartData" <!-- horizontal -->
:chart-info="chartInfo" <ul class="bar-retro-horizontal" v-if="chartInfo.param.orientation==='horizontal'">
:legends="legends" <li
:series="series" class="bar-item"
:is-fullscreen="isFullscreen" v-for="(item,index) in barData"
></chart-legend> :key="index"
:style="{
height:item.height + 'px',
marginBottom:item.spacing + 'px',
}"
>
<template v-if="item.height!==undefined">
<!-- legend -->
<p
v-if="chartInfo.param.text === 'legend' || chartInfo.param.text === 'all'"
class="bar-title"
:title="item.alias"
:style="{
width: item.titleWidth + 'px',
fontSize: item.titleFontSize +'px'
}"
>
{{item.alias}}
</p>
<div class="bar-rect" :style="{width:item.width + 'px',}">
<div
v-for="(cell) in item.cellCount"
:key="cell"
>
<div
v-if="item.litCount >= cell"
class="bar-cell"
:class="{'chart-cursor-pointer':(dataLink.length || chartInfo.datasource === 'metrics' || chartInfo.datasource === 'logs')}"
:style="{
width:item.cellWidth + 'px',
height:item.cellHeight + 'px',
marginRight:item.spacing + 'px',
backgroundImage:item.cellBackgroundImage,
}"
@mouseenter="barEnter($event,item)"
@mousemove="barMove"
@mouseleave="barLeave"
@click="chartClick($event,item)"
>
</div>
<div
v-else
class="bar-cell"
:style="{
width:item.cellWidth + 'px',
height:item.cellHeight + 'px',
marginRight:item.spacing + 'px',
background:item.cellBackground,
}"
>
</div>
</div>
</div>
<!-- value -->
<p
v-if="chartInfo.param.text === 'value' || chartInfo.param.text === 'all'"
class="bar-value"
:style="{
width:item.valueWidth + 'px',
fontSize:item.valueFontSize + 'px',
color:item.mapping && item.mapping.color && item.mapping.color.text
}"
>
<i v-if="item.mapping && item.mapping.icon" :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
<span>{{item.mapping ? handleDisplay(item.mapping.display, { ...item.label, value: item.showValue }) : item.showValue}}</span>
</p>
</template>
</li>
</ul>
<!-- vertical -->
<ul class="bar-retro-vertical" v-else>
<li
class="bar-item"
v-for="(item,index) in barData"
:key="index"
:style="{
width: item.width + 'px',
marginRight:item.spacing + 'px',
}"
>
<template v-if="item.width!==undefined">
<!-- legend -->
<p v-if="chartInfo.param.text === 'legend' || chartInfo.param.text === 'all'" class="bar-title" :title="item.alias">{{item.alias}}</p>
<div class="bar-rect" :style="{height:item.height + 'px',}">
<div
v-for="(cell) in item.cellCount"
:key="cell"
>
<div
v-if="item.litCount >= cell"
class="bar-cell"
:class="{'chart-cursor-pointer':(dataLink.length || chartInfo.datasource === 'metrics' || chartInfo.datasource === 'logs')}"
:style="{
width:item.cellWidth + 'px',
height:item.cellHeight + 'px',
marginTop:item.spacing + 'px',
backgroundImage:item.cellBackgroundImage,
}"
@mouseenter="barEnter($event,item)"
@mousemove="barMove"
@mouseleave="barLeave"
@click="chartClick($event,item)"
>
</div>
<div
v-else
class="bar-cell"
:style="{
width:item.cellWidth + 'px',
height:item.cellHeight + 'px',
marginTop:item.spacing + 'px',
background:item.cellBackground,
}"
>
</div>
</div>
</div>
<!-- value -->
<p
v-if="chartInfo.param.text === 'value' || chartInfo.param.text === 'all'"
class="bar-value"
:style="{
height:item.valueHeight + 'px',
fontSize:item.valueFontSize + 'px',
color:item.mapping && item.mapping.color && item.mapping.color.text
}"
>
<i v-if="item.mapping && item.mapping.icon" :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
<span>{{item.mapping ? handleDisplay(item.mapping.display, { ...item.label, value: item.showValue }) : item.showValue}}</span>
</p>
</template>
</li>
</ul>
</template>
<!-- basic -->
<template v-else>
<!-- horizontal -->
<ul class="bar-basic-horizontal" v-if="chartInfo.param.orientation==='horizontal'">
<li
class="bar-item"
v-for="(item,index) in barData"
:key="index"
:style="{
height:item.height + 'px',
marginBottom:item.spacing + 'px',
}"
>
<template v-if="item.height!==undefined">
<!-- legend -->
<p
v-if="chartInfo.param.text === 'legend' || chartInfo.param.text === 'all'"
class="bar-title"
:title="item.alias"
:style="{
width: item.titleWidth + 'px',
fontSize: item.titleFontSize +'px'
}"
>
{{item.alias}}
</p>
<div class="bar-rect" :style="{width:item.width + 'px',}">
<div
class="bar-actual"
:class="{'chart-cursor-pointer':(dataLink.length || chartInfo.datasource === 'metrics' || chartInfo.datasource === 'logs')}"
:style="{ width:item.actualWidth + 'px',height:item.height + 'px',background:item.background,}"
@mouseenter="barEnter($event,item)"
@mousemove="barMove"
@mouseleave="barLeave"
@click="chartClick($event,item)"
>
</div>
</div>
<!-- value -->
<p
v-if="chartInfo.param.text === 'value' || chartInfo.param.text === 'all'"
class="bar-value"
:style="{
width:item.valueWidth + 'px',
fontSize:item.valueFontSize + 'px',
color:item.mapping && item.mapping.color && item.mapping.color.text
}"
>
<i v-if="item.mapping && item.mapping.icon" :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
<span>{{item.mapping ? handleDisplay(item.mapping.display, { ...item.label, value: item.showValue }) : item.showValue}}</span>
</p>
</template>
</li>
</ul>
<!-- vertical -->
<ul class="bar-basic-vertical" v-else>
<li
class="bar-item"
v-for="(item,index) in barData"
:key="index"
:style="{
width:item.width + 'px',
marginRight:item.spacing + 'px'
}"
>
<template v-if="item.width!==undefined">
<!-- value -->
<p
v-if="chartInfo.param.text === 'value' || chartInfo.param.text === 'all'"
class="bar-value"
:style="{
height:item.valueHeight + 'px',
fontSize:item.valueFontSize + 'px',
color:item.mapping && item.mapping.color && item.mapping.color.text
}"
>
<i v-if="item.mapping && item.mapping.icon" :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
<span>{{item.mapping ? handleDisplay(item.mapping.display, { ...item.label, value: item.showValue }) : item.showValue}}</span>
</p>
<div class="bar-rect" :style="{height:item.height + 'px',}">
<div
class="bar-actual"
:class="{'chart-cursor-pointer':(dataLink.length || chartInfo.datasource === 'metrics' || chartInfo.datasource === 'logs')}"
:style="{ height:item.actualHeight + 'px', background:item.background,}"
@mouseenter="barEnter($event,item)"
@mousemove="barMove"
@mouseleave="barLeave"
@click="chartClick($event,item)"
>
</div>
</div>
<!-- legend -->
<p v-if="chartInfo.param.text === 'legend' || chartInfo.param.text === 'all'" class="bar-title" :title="item.alias">{{item.alias}}</p>
</template>
</li>
</ul>
</template>
</div>
<!-- tooltip -->
<div :id="`chart-canvas-tooltip-${chartId}`" class="chart-canvas-tooltip no-style-class" :style="{left:tooltip.x+'px',top:tooltip.y+'px'}" v-if="tooltip.show">
<div class="chart-canvas-tooltip-title tooltip-title" :title="tooltip.title">
{{tooltip.title}}
</div>
<div class="chart-canvas-tooltip-content">
<div>value</div>
<div>
<div v-if="tooltip.mapping && tooltip.mapping.icon" style="display: inline-block">
<i :class="tooltip.mapping.icon" :style="{color: tooltip.mapping.color.icon}"></i>
</div>
<div style="display: inline-block">{{tooltip.value}}</div>
</div>
</div>
</div>
<!-- toolbox --> <!-- toolbox -->
<div :id="`chart-toolbox-${chartId}`" class="chart-canvas-tooltip no-style-class chart-toolbox" :style="{left:toolbox.x+'px',top:toolbox.y+'px'}" v-if="toolbox.show" v-clickoutside="clickout"> <div :id="`chart-toolbox-${chartId}`" class="chart-canvas-tooltip no-style-class chart-toolbox" :style="{left:toolbox.x+'px',top:toolbox.y+'px'}" v-if="toolbox.show" v-clickoutside="clickout">
<div class="chart-canvas-tooltip-title tooltip-title" :title="toolbox.title">{{toolbox.title}}</div> <div class="chart-canvas-tooltip-title tooltip-title" :title="toolbox.title">{{toolbox.title}}</div>
@@ -40,25 +286,22 @@
</div> </div>
</div> </div>
</div> </div>
<span class="temp-dom" ref="temp-dom"></span>
<span class="temp-dom" ref="temp-dom2"></span>
</div> </div>
</template> </template>
<script> <script>
import legend from '@/components/chart/chart/legend'
import chartMixin from '@/components/chart/chartMixin' import chartMixin from '@/components/chart/chartMixin'
import chartFormat from '@/components/chart/chartFormat' import chartFormat from '@/components/chart/chartFormat'
import { chartLegendPlacement } from '@/components/common/js/constants'
import * as echarts from 'echarts'
import { getChart, setChart } from '@/components/common/js/common'
import { formatScientificNotation, getMetricTypeValue } from '@/components/common/js/tools' import { formatScientificNotation, getMetricTypeValue } from '@/components/common/js/tools'
import chartDataFormat from '@/components/chart/chartDataFormat' import chartDataFormat from '@/components/chart/chartDataFormat'
import { initColor } from '@/components/chart/chart/tools' import { initColor } from '@/components/chart/chart/tools'
import lodash from 'lodash' import lodash from 'lodash'
import tinycolor from 'tinycolor2'
export default { export default {
name: 'chart-bar', name: 'chart-bar',
components: { components: {
chartLegend: legend
}, },
mixins: [chartMixin, chartFormat], mixins: [chartMixin, chartFormat],
props: { props: {
@@ -68,27 +311,6 @@ export default {
isFullscreen: Boolean isFullscreen: Boolean
}, },
computed: { computed: {
hasLegend () {
try {
return [chartLegendPlacement.bottom, chartLegendPlacement.left, chartLegendPlacement.right].indexOf(this.chartInfo.param.legend.placement) > -1 && this.chartInfo.param.enable.legend
} catch (e) {
return false
}
},
legendPlacement () {
try {
switch (this.chartInfo.param.legend.placement) {
case 'left':
case 'right':
case 'bottom': {
return `nz-chart__component--${this.chartInfo.param.legend.placement}`
}
default: return ''
}
} catch (e) {
return ''
}
}
}, },
data () { data () {
return { return {
@@ -100,88 +322,284 @@ export default {
active: '#53a3cb', active: '#53a3cb',
inactive: '#7e7e7e' inactive: '#7e7e7e'
}, },
chartId: '' chartId: '',
barData: [],
boxWidth: 0,
boxHeight: 0,
boxPadding: 10
} }
}, },
methods: { methods: {
initChart (chartOption = this.chartOption) { initChart () {
this.setDataLink() this.setDataLink()
const self = this
this.legends = [] this.legends = []
this.series = chartOption.series = this.initBarData(this.chartInfo, chartOption.series[0], this.chartData) // 生成series和legends this.initBarData(this.chartInfo, this.chartData)
if (this.isNoData) { if (this.isNoData) {
return return
} }
chartOption.xAxis.data = chartOption.series[0].data.map(item => {
if (this.chartInfo.param.text == 'all' || this.chartInfo.param.text == 'legend') {
return item.name
} else {
return ''
}
})
chartOption.axisLabel = {
margin: 8,
formatter (params) {
const dataLength = chartOption.series[0].data.length || 1
const chartWidth = (document.getElementById('chart-canvas-' + self.chartInfo.id).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
}
}
}
chartOption.yAxis.axisLabel.formatter = function (val, index) {
const value = formatScientificNotation(val, 6)
let chartUnit = self.chartInfo.unit
chartUnit = chartUnit || 2
const unit = chartDataFormat.getUnit(chartUnit)
return unit.compute(value, index, -1, 2)
}
chartOption.tooltip.formatter = this.formatterFunc
chartOption.tooltip.position = this.tooltipPosition
if (navigator.userAgent.match(/Mobi/i) ||
navigator.userAgent.match(/Android/i) ||
navigator.userAgent.match(/iPhone/i)) {
chartOption.tooltip.confine = true
chartOption.tooltip.appendToBody = false
delete chartOption.tooltip.position
}
/* 使用setTimeout延迟渲染图表避免样式错乱 */ /* 使用setTimeout延迟渲染图表避免样式错乱 */
setTimeout(() => { setTimeout(() => {
const myChart = this.isInit ? echarts.init(document.getElementById(`chart-canvas-${this.chartId}`)) : getChart(this.chartId) this.drawBarChart()
if (!myChart) {
return
}
myChart.setOption(chartOption)
this.isInit && setChart(this.chartId, myChart) // 缓存不使用vue的data是为避免整个chart被监听导致卡顿
if (this.isInit && (this.dataLink.length || this.chartInfo.datasource === 'metrics' || this.chartInfo.datasource === 'logs')) {
myChart.on('click', this.chartClick)
}
this.isInit = false this.isInit = false
}, 200) }, 200)
}, },
initBarData (chartInfo, seriesTemplate, originalDatas) { resize () {
setTimeout(() => {
this.drawBarChart()
}, 50)
},
drawBarChart () {
const wrap = document.getElementById(`chart-canvas-${this.chartId}`)
this.boxWidth = wrap.offsetWidth - 2 * this.boxPadding
this.boxHeight = wrap.offsetHeight - 2 * this.boxPadding
const displayMode = lodash.get(this.chartInfo, 'param.displayMode', 'basic')
if (displayMode === 'basic') {
this.renderBasicBars()
} else {
this.renderRetroBars()
}
},
renderBasicBars () {
const orientation = lodash.get(this.chartInfo, 'param.orientation', 'vertical')
const maxValue = this.calcMax(this.barData)
const spacing = 10
const numBars = this.barData.length
const totalSpacing = (numBars - 1) * spacing
if (orientation === 'vertical') {
const width = (this.boxWidth - totalSpacing) / numBars
const minValueHeight = 18
const maxValueHeight = 50
const valueHeight = this.chartInfo.param.text === 'value' || this.chartInfo.param.text === 'all' ? Math.min(Math.max(this.boxHeight * 0.1, minValueHeight), maxValueHeight) : 0
const titleHeight = this.chartInfo.param.text === 'legend' || this.chartInfo.param.text === 'all' ? 14 * 1.5 : 0
const height = this.boxHeight - valueHeight - titleHeight
this.barData.forEach((item) => {
item.width = width
item.spacing = spacing
item.valueHeight = valueHeight
item.height = height
const valuePercent = this.getValuePercent(item.value, 0, maxValue)
item.actualHeight = item.height * valuePercent
let valueText = ''
if (item.mapping) {
valueText = this.handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })
if (item.mapping.icon) {
valueText += 'AA'
}
} else {
valueText = item.showValue
}
item.valueFontSize = this.calculateFontSize(valueText, width, valueHeight, 1)
})
const valueFontArr = this.barData.map(item => item.valueFontSize)
const minValueFont = Math.min(...valueFontArr)
this.barData.forEach(item => {
item.valueFontSize = minValueFont
})
this.$forceUpdate()
} else {
const height = (this.boxHeight - totalSpacing) / numBars
const maxTitleHeightRatio = 0.6
const titleHeight = height * maxTitleHeightRatio
const titleFontSize = Math.min(titleHeight, 28)
const maxValueWidth = 150
const valueWidth = this.chartInfo.param.text === 'value' || this.chartInfo.param.text === 'all' ? Math.min(this.boxWidth * 0.2, maxValueWidth) : 0
this.barData.forEach((item) => {
item.height = height
item.spacing = spacing
item.titleFontSize = titleFontSize
const text = item.alias
const el = this.$refs['temp-dom2']
el.innerText = text
el.style.fontSize = titleFontSize + 'px'
item.titleWidth = el.offsetWidth
let valueText = ''
if (item.mapping) {
valueText = this.handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })
if (item.mapping.icon) {
valueText += 'AA'
}
} else {
valueText = item.showValue
}
item.valueWidth = valueWidth
item.valueFontSize = this.calculateFontSize(valueText, item.valueWidth - 10 * 2, height, 1.2)
})
const titleWidthArr = this.barData.map(item => item.titleWidth)
const maxTitleWidth = Math.max(...titleWidthArr)
const titleWidth = this.chartInfo.param.text === 'legend' || this.chartInfo.param.text === 'all' ? Math.min(maxTitleWidth + 15, this.boxWidth * 0.4) : 0
const valueFontArr = this.barData.map(item => item.valueFontSize)
const minValueFont = Math.min(...valueFontArr)
this.barData.forEach(item => {
item.titleWidth = titleWidth
item.valueFontSize = minValueFont
item.width = this.boxWidth - item.titleWidth - item.valueWidth
const valuePercent = this.getValuePercent(item.value, 0, maxValue)
item.actualWidth = item.width * valuePercent
})
this.$forceUpdate()
}
},
renderRetroBars () {
const orientation = lodash.get(this.chartInfo, 'param.orientation', 'vertical')
const maxValue = this.calcMax(this.barData)
const spacing = 2
const numBars = this.barData.length
const totalSpacing = (numBars - 1) * spacing
if (orientation === 'vertical') {
const width = (this.boxWidth - totalSpacing) / numBars
const minValueHeight = 18
const maxValueHeight = 50
const valueHeight = this.chartInfo.param.text === 'value' || this.chartInfo.param.text === 'all' ? Math.min(Math.max(this.boxHeight * 0.1, minValueHeight), maxValueHeight) : 0
const titleHeight = this.chartInfo.param.text === 'legend' || this.chartInfo.param.text === 'all' ? 14 * 1.5 : 0
const height = this.boxHeight - valueHeight - titleHeight
const cellHeight = 10
const cellWidth = width
const cellCount = Math.floor(height / (cellHeight + spacing))
this.barData.forEach((item, index) => {
item.width = width
item.spacing = spacing
item.valueHeight = valueHeight
item.height = height
let valueText = ''
if (item.mapping) {
valueText = this.handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })
if (item.mapping.icon) {
valueText += 'AA'
}
} else {
valueText = item.showValue
}
item.valueFontSize = this.calculateFontSize(valueText, width * 0.9, valueHeight, 1)
item.cellWidth = cellWidth
item.cellHeight = cellHeight
item.cellCount = cellCount
item.litCount = Math.ceil(Math.max(item.value / (maxValue / item.cellCount), 1))
item.cellBackground = tinycolor(item.background).setAlpha(0.18).toRgbString()
item.cellBackgroundImage = `radial-gradient(${tinycolor(item.background).setAlpha(0.95).toRgbString()} 10%, ${tinycolor(item.background).setAlpha(0.55).toRgbString()})`
})
const valueFontArr = this.barData.map(item => item.valueFontSize)
const minValueFont = Math.min(...valueFontArr)
this.barData.forEach(item => {
item.valueFontSize = minValueFont
})
this.$forceUpdate()
} else {
const height = (this.boxHeight - totalSpacing) / numBars
const maxTitleHeightRatio = 0.6
const titleHeight = height * maxTitleHeightRatio
const titleFontSize = Math.min(titleHeight, 28)
const maxValueWidth = 150
const valueWidth = this.chartInfo.param.text === 'value' || this.chartInfo.param.text === 'all' ? Math.min(this.boxWidth * 0.2, maxValueWidth) : 0
const cellWidth = 10
const cellHeight = height
this.barData.forEach((item) => {
item.height = height
item.spacing = spacing
item.titleFontSize = titleFontSize
const text = item.alias
const el = this.$refs['temp-dom2']
el.innerText = text
el.style.fontSize = titleFontSize + 'px'
item.titleWidth = el.offsetWidth
let valueText = ''
if (item.mapping) {
valueText = this.handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })
if (item.mapping.icon) {
valueText += 'AA'
}
} else {
valueText = item.showValue
}
item.valueWidth = valueWidth
item.valueFontSize = this.calculateFontSize(valueText, item.valueWidth - 10 * 2, height, 1.2)
})
const titleWidthArr = this.barData.map(item => item.titleWidth)
const maxTitleWidth = Math.max(...titleWidthArr)
const titleWidth = this.chartInfo.param.text === 'legend' || this.chartInfo.param.text === 'all' ? Math.min(maxTitleWidth + 15, this.boxWidth * 0.4) : 0
const valueFontArr = this.barData.map(item => item.valueFontSize)
const minValueFont = Math.min(...valueFontArr)
this.barData.forEach(item => {
item.titleWidth = titleWidth
item.valueFontSize = minValueFont
item.width = this.boxWidth - item.titleWidth - item.valueWidth
const cellCount = Math.floor(item.width / (cellWidth + spacing))
item.cellWidth = cellWidth
item.cellHeight = cellHeight
item.cellCount = cellCount
item.litCount = Math.ceil(Math.max(item.value / (maxValue / item.cellCount), 1))
item.cellBackground = tinycolor(item.background).setAlpha(0.18).toRgbString()
item.cellBackgroundImage = `radial-gradient(${tinycolor(item.background).setAlpha(0.95).toRgbString()} 10%, ${tinycolor(item.background).setAlpha(0.55).toRgbString()})`
})
this.$forceUpdate()
}
},
// 计算字体大小
calculateFontSize (text, width, height, lineHeight = 1.2, maxSize) {
const el = this.$refs['temp-dom']
el.innerText = text
const elWidth = el.offsetWidth
const fontSizeBasedOnWidth = (width / elWidth) * 14
const fontSizeBasedOnHeight = height / lineHeight
const optimalSize = Math.min(fontSizeBasedOnHeight, fontSizeBasedOnWidth)
return Math.min(optimalSize, maxSize || optimalSize)
},
getValuePercent (value, minValue, maxValue) {
// Need special logic for when minValue === maxValue === value to prevent returning NaN
const valueRatio = Math.min((value - minValue) / (maxValue - minValue), 1)
return isNaN(valueRatio) ? 0 : valueRatio
},
calcMax (arr) {
let maxNum = arr.reduce((maxValue, obj) => {
return Math.max(maxValue, obj.value)
}, 0)
if (maxNum <= 1) {
return 1
}
let bite = 1
while (maxNum >= 10) {
maxNum /= 10
if (maxNum > 1) {
bite += 1
}
}
return Math.pow(10, bite)
},
initBarData (chartInfo, originalDatas) {
this.barData = []
let colorIndex = 0 let colorIndex = 0
const self = this
const decimals = this.chartInfo.param.decimals || 2 const decimals = this.chartInfo.param.decimals || 2
const s = lodash.cloneDeep(seriesTemplate)
s.data = []
originalDatas.forEach((originalData, expressionIndex) => { originalDatas.forEach((originalData, expressionIndex) => {
originalData.forEach((data, dataIndex) => { originalData.forEach((data, dataIndex) => {
this.isNoData = false this.isNoData = false
if (s) {
const value = getMetricTypeValue(data.values, chartInfo.param.statistics) const value = getMetricTypeValue(data.values, chartInfo.param.statistics)
const showValue = chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(value, null, -1, decimals) const showValue = chartDataFormat.getUnit(chartInfo.unit ? chartInfo.unit : 2).compute(value, null, -1, decimals)
const mapping = this.selectMapping(value, chartInfo.param.valueMapping, chartInfo.param.enable && this.chartInfo.param.enable.valueMapping) const mapping = this.selectMapping(value, chartInfo.param.valueMapping, chartInfo.param.enable && this.chartInfo.param.enable.valueMapping)
// eslint-disable-next-line vue/no-mutating-props // eslint-disable-next-line vue/no-mutating-props
mapping && this.chartOption.color && (this.chartOption.color[colorIndex] = mapping.color.bac) mapping && this.chartOption.color && (this.chartOption.color[colorIndex] = mapping.color.bac)
const legend = this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex) const legend = this.handleLegend(chartInfo, data, expressionIndex, dataIndex, colorIndex)
s.data.push({ this.barData.push({
value: value, value: Number(value),
realValue: value, realValue: value,
showValue: showValue, showValue: showValue,
name: legend.name, name: legend.name,
@@ -193,21 +611,12 @@ export default {
expressionIndex: expressionIndex, expressionIndex: expressionIndex,
dataIndex: dataIndex, dataIndex: dataIndex,
mapping: mapping, mapping: mapping,
label: { background: mapping ? mapping.color.bac : this.colorList[colorIndex]
...s.label,
formatter: this.pieFormatterLabel,
color: mapping ? mapping.color.text : '#000000'
},
itemStyle: {
color: mapping ? mapping.color.bac : this.colorList[colorIndex]
}
}) })
colorIndex++ colorIndex++
}
}) })
}) })
this.$emit('chartIsNoData', this.isNoData) this.$emit('chartIsNoData', this.isNoData)
return [s]
}, },
formatterFunc (params, ticket, callback) { formatterFunc (params, ticket, callback) {
const self = this const self = this
@@ -225,6 +634,44 @@ export default {
</div> </div>
` `
}, },
barEnter (e, data) {
this.tooltip.title = data.alias
this.tooltip.value = data.mapping && data.mapping.display ? this.handleDisplay(data.mapping.display, { ...data.labels, value: data.showValue }) : data.showValue
this.tooltip.mapping = data.mapping
this.tooltip.show = true
this.setPosition(e)
},
barMove (e) {
this.tooltip.show = true
this.setPosition(e)
},
barLeave () {
this.tooltip.show = false
},
setPosition (e) {
const windowWidth = window.innerWidth// 窗口宽度
const windowHeight = window.innerHeight// 窗口高度
this.$nextTick(() => {
const box = document.getElementById(`chart-canvas-tooltip-${this.chartId}`)
if (box) {
const boxWidth = box.offsetWidth
const boxHeight = box.offsetHeight
if (e.pageX < (windowWidth / 2)) { // 说明鼠标在左边放不下提示框
this.tooltip.x = e.pageX + 15
} else {
this.tooltip.x = e.pageX - boxWidth - 15
}
if (e.pageY + 50 + boxHeight < windowHeight) { // 说明鼠标上面放不下提示框
this.tooltip.y = e.pageY + 15
} else {
this.tooltip.y = e.pageY - boxHeight - 10
}
} else {
this.tooltip.x = e.pageX + 15
this.tooltip.y = e.pageY + 15
}
})
},
toolboxPosition (e) { toolboxPosition (e) {
const windowWidth = window.innerWidth// 窗口宽度 const windowWidth = window.innerWidth// 窗口宽度
const windowHeight = window.innerHeight// 窗口高度 const windowHeight = window.innerHeight// 窗口高度
@@ -248,18 +695,18 @@ export default {
} }
}) })
}, },
chartClick (params) { chartClick (e, data) {
getChart(this.chartId).dispatchAction({ if (this.dataLink.length || this.chartInfo.datasource === 'metrics' || this.chartInfo.datasource === 'logs') {
type: 'hideTip' this.tooltip.show = false
})
this.toolbox.title = params.data.alias this.toolbox.title = data.alias
this.toolbox.value = params.data.mapping && params.data.mapping.display ? this.handleDisplay(params.data.mapping.display, { ...params.data.labels, value: params.data.showValue }) : params.data.showValue this.toolbox.value = data.mapping && data.mapping.display ? this.handleDisplay(data.mapping.display, { ...data.labels, value: data.showValue }) : data.showValue
this.toolbox.mapping = params.data.mapping this.toolbox.mapping = data.mapping
this.toolbox.show = true this.toolbox.show = true
this.toolbox.metric.labels = params.data.labels this.toolbox.metric.labels = data.labels
this.toolbox.metric.expressionIndex = params.data.expressionIndex this.toolbox.metric.expressionIndex = data.expressionIndex
const e = params.event.event
this.toolboxPosition(e) this.toolboxPosition(e)
}
}, },
clickout () { clickout () {
if ((this.dataLink.length || this.chartInfo.datasource === 'metrics' || this.chartInfo.datasource === 'logs') && this.toolbox.show) { if ((this.dataLink.length || this.chartInfo.datasource === 'metrics' || this.chartInfo.datasource === 'logs') && this.toolbox.show) {

View File

@@ -122,13 +122,13 @@ export default {
colorIndex++ colorIndex++
}) })
}) })
this.maxValue = this.calcMax() this.maxValue = this.calcMax(this.gaugeData)
this.$emit('chartIsNoData', this.isNoData) this.$emit('chartIsNoData', this.isNoData)
resolve() resolve()
}) })
}, },
calcMax () { calcMax (arr) {
let maxNum = this.gaugeData.reduce((maxValue, obj) => { let maxNum = arr.reduce((maxValue, obj) => {
return Math.max(maxValue, obj.value) return Math.max(maxValue, obj.value)
}, 0) }, 0)
@@ -199,7 +199,7 @@ export default {
}) })
}, },
// 计算字体大小 // 计算字体大小
calculateFontSize (text, width, height, maxSize, lineHeight = 1.2) { calculateFontSize (text, width, height, lineHeight = 1.2, maxSize) {
const el = this.$refs['temp-dom'] const el = this.$refs['temp-dom']
el.innerText = text el.innerText = text
const elWidth = el.offsetWidth const elWidth = el.offsetWidth
@@ -224,7 +224,7 @@ export default {
showValue = this.handleDisplay(data.mapping.display, { ...data.label, value: showValue }) showValue = this.handleDisplay(data.mapping.display, { ...data.label, value: showValue })
} }
const textWidth = dimension - (gaugeWidth * 4) const textWidth = dimension - (gaugeWidth * 4)
const valueFontSize = this.calculateFontSize(showValue, textWidth, textWidth / 2, gaugeWidth * 2, 1) const valueFontSize = this.calculateFontSize(showValue, textWidth, textWidth / 2, 1, gaugeWidth * 2)
return { return {
gaugeWidth, gaugeWidth,
valueFontSize valueFontSize

View File

@@ -332,8 +332,8 @@ export default {
} else { } else {
valueFont = item.showValue valueFont = item.showValue
} }
titleFontSize = this.calculateFontSize(titleFont, (item.width - padding * 2), (item.height - padding * 2) * 0.4, 30) titleFontSize = this.calculateFontSize(titleFont, (item.width - padding * 2), (item.height - padding * 2) * 0.4, 1.2, 30)
valueFontSize = this.calculateFontSize(valueFont, (item.width - padding * 2), (item.height - padding * 2) * 0.4) valueFontSize = this.calculateFontSize(valueFont, (item.width - padding * 2), (item.height - padding * 2) * 0.4, 1.2)
titleFontSize = Math.min(valueFontSize * 0.7, titleFontSize) titleFontSize = Math.min(valueFontSize * 0.7, titleFontSize)
break break
case 'legend': case 'legend':
@@ -343,7 +343,7 @@ export default {
titleFont += 'AA' titleFont += 'AA'
} }
} }
valueFontSize = this.calculateFontSize(titleFont, (item.width - padding * 2), (item.height - padding * 2) * 0.4) valueFontSize = this.calculateFontSize(titleFont, (item.width - padding * 2), (item.height - padding * 2) * 0.4, 1.2)
break break
case 'none': case 'none':
titleFontSize = '' titleFontSize = ''
@@ -359,7 +359,7 @@ export default {
} else { } else {
valueFont = item.showValue valueFont = item.showValue
} }
valueFontSize = this.calculateFontSize(valueFont, (item.width - padding * 2), (item.height - padding * 2) * 0.4) valueFontSize = this.calculateFontSize(valueFont, (item.width - padding * 2), (item.height - padding * 2) * 0.4, 1.2)
break break
} }
item.titleFontSize = titleFontSize item.titleFontSize = titleFontSize
@@ -391,7 +391,7 @@ export default {
} }
}, },
// 计算字体大小 // 计算字体大小
calculateFontSize (text, width, height, maxSize, lineHeight = 1.2) { calculateFontSize (text, width, height, lineHeight = 1.2, maxSize) {
const el = this.$refs['temp-dom'] const el = this.$refs['temp-dom']
el.innerText = text el.innerText = text
const elWidth = el.offsetWidth const elWidth = el.offsetWidth

View File

@@ -725,7 +725,7 @@ export default {
handler () { handler () {
setTimeout(() => { setTimeout(() => {
this.resize() this.resize()
}, 200) }, 300)
} }
}, },
// 监听查看模式变化 // 监听查看模式变化

View File

@@ -390,9 +390,6 @@ export default {
varValue: '', varValue: '',
result: 'show' result: 'show'
}, },
sparklineMode: 'line',
comparison: 'none',
colorMode: 'value',
dataLink: [] dataLink: []
} }
break break

View File

@@ -444,6 +444,32 @@
<el-option :label="$t('project.topology.bac')" value="background"></el-option> <el-option :label="$t('project.topology.bac')" value="background"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- Layout orientation -->
<el-form-item :label="$t('dashboard.chartForm.orientation')" class="form-item--half-width" v-if="chartConfig.type==='bar'">
<el-select
v-model="chartConfig.param.orientation"
placeholder=""
popper-class="right-box-select-top prevent-clickoutside"
size="small"
@change="change"
>
<el-option :label="$t('dashboard.chartForm.vertical')" value="vertical"></el-option>
<el-option :label="$t('dashboard.chartForm.horizontal')" value="horizontal"></el-option>
</el-select>
</el-form-item>
<!-- Display mode -->
<el-form-item :label="$t('dashboard.chartForm.displayMode')" class="form-item--half-width" v-if="chartConfig.type==='bar'">
<el-select
v-model="chartConfig.param.displayMode"
placeholder=""
popper-class="right-box-select-top prevent-clickoutside"
size="small"
@change="change"
>
<el-option :label="$t('dashboard.chartForm.basic')" value="basic"></el-option>
<el-option :label="$t('dashboard.chartForm.retro')" value="led"></el-option>
</el-select>
</el-form-item>
</div> </div>
<!-- Right Y Axis --> <!-- Right Y Axis -->
@@ -1540,6 +1566,8 @@ export default {
varValue: '', varValue: '',
result: 'show' result: 'show'
}, },
orientation: 'vertical',
displayMode: 'basic',
dataLink: this.chartConfig.param.dataLink dataLink: this.chartConfig.param.dataLink
} }
break break

View File

@@ -260,6 +260,10 @@ export default {
delete params.param.comparison delete params.param.comparison
delete params.param.colorMode delete params.param.colorMode
} }
if (params.type !== 'bar') {
delete params.param.orientation
delete params.param.displayMode
}
if (!params.x && !params.y && this.from === 'endpointQuery') { // endpointQuery 新增 放在最后 if (!params.x && !params.y && this.from === 'endpointQuery') { // endpointQuery 新增 放在最后
params.x = 0 params.x = 0
params.y = 999 params.y = 999
@@ -736,6 +740,10 @@ export default {
if (obj.type == 'text' && !obj.param.editorType) { if (obj.type == 'text' && !obj.param.editorType) {
obj.param.editorType = 'richText' obj.param.editorType = 'richText'
} }
if (obj.type === 'bar') {
if (!obj.param.orientation) { obj.param.orientation = 'vertical' }
if (!obj.param.displayMode) { obj.param.displayMode = 'basic' }
}
} }
if (obj.elements && obj.elements.length) { if (obj.elements && obj.elements.length) {
obj.elements.forEach((item, index) => { obj.elements.forEach((item, index) => {

View File

@@ -93,8 +93,8 @@ export default {
case 'pie': case 'pie':
case 'doughnut': case 'doughnut':
case 'rose': case 'rose':
case 'bar':
return true return true
case 'bar':
case 'table': case 'table':
case 'stat': case 'stat':
case 'hexagon': case 'hexagon':

View File

@@ -283,6 +283,32 @@
<el-option :label="$t('project.topology.bac')" value="background"></el-option> <el-option :label="$t('project.topology.bac')" value="background"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- Layout orientation -->
<el-form-item :label="$t('dashboard.chartForm.orientation')" class="form-item--half-width" v-if="chartConfig.type==='bar'">
<el-select
v-model="chartConfig.param.orientation"
placeholder=""
popper-class="right-box-select-top prevent-clickoutside"
size="small"
@change="change"
>
<el-option :label="$t('dashboard.chartForm.vertical')" value="vertical"></el-option>
<el-option :label="$t('dashboard.chartForm.horizontal')" value="horizontal"></el-option>
</el-select>
</el-form-item>
<!-- Display mode -->
<el-form-item :label="$t('dashboard.chartForm.displayMode')" class="form-item--half-width" v-if="chartConfig.type==='bar'">
<el-select
v-model="chartConfig.param.displayMode"
placeholder=""
popper-class="right-box-select-top prevent-clickoutside"
size="small"
@change="change"
>
<el-option :label="$t('dashboard.chartForm.basic')" value="basic"></el-option>
<el-option :label="$t('dashboard.chartForm.retro')" value="led"></el-option>
</el-select>
</el-form-item>
</div> </div>
<!--legend--> <!--legend-->
<div v-if="isShowLegend(chartConfig.type)"> <div v-if="isShowLegend(chartConfig.type)">
@@ -1176,6 +1202,8 @@ export default {
varValue: '', varValue: '',
result: 'show' result: 'show'
}, },
orientation: 'vertical',
displayMode: 'basic',
dataLink: this.chartConfig.param.dataLink dataLink: this.chartConfig.param.dataLink
} }
break break