NEZ-3278 perf:stat chart 样式优化

This commit is contained in:
zyh
2023-11-01 15:38:35 +08:00
parent 690513cd81
commit 8d4e655093
4 changed files with 87 additions and 82 deletions

View File

@@ -22765,9 +22765,9 @@
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
}, },
"tinycolor2": { "tinycolor2": {
"version": "1.4.2", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz",
"integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==" "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
}, },
"tinyqueue": { "tinyqueue": {
"version": "2.0.3", "version": "2.0.3",

View File

@@ -92,6 +92,7 @@
"showdown": "^2.1.0", "showdown": "^2.1.0",
"spark-md5": "^3.0.2", "spark-md5": "^3.0.2",
"speed-measure-webpack-plugin": "^1.5.0", "speed-measure-webpack-plugin": "^1.5.0",
"tinycolor2": "^1.6.0",
"uplot": "^1.6.26", "uplot": "^1.6.26",
"uplot-vue": "^1.1.5", "uplot-vue": "^1.1.5",
"v-selectpage": "^2.1.4", "v-selectpage": "^2.1.4",

View File

@@ -556,6 +556,10 @@
flex-grow: 1; flex-grow: 1;
position: relative; position: relative;
border: 1px solid $--background-color-empty; border: 1px solid $--background-color-empty;
.stat-text{
text-align: center;
line-height: 1.2;
}
.sparkline{ .sparkline{
width: 100%; width: 100%;
height: 40%; height: 40%;

View File

@@ -16,43 +16,52 @@
background:item.mapping ? item.mapping.color.bac : (statData.length===1 ? '' : colorList[index]), background:item.mapping ? item.mapping.color.bac : (statData.length===1 ? '' : colorList[index]),
height:item.height+'px', height:item.height+'px',
width:item.width + 'px', width:item.width + 'px',
fontSize: item.fontSize + 'px',
}" }"
> >
<div style="maxWidth:100%;z-index: 10;"> <div style="maxWidth:100%;z-index: 10;">
<!-- all --> <!-- all -->
<template v-if="chartInfo.param.text==='all'"> <template v-if="chartInfo.param.text==='all'">
<div v-if="item.mapping" :style="{color:item.mapping.color.text}"> <div v-if="item.mapping" :style="{color:item.mapping.color.text}">
<p style="white-space: nowrap;">{{item.legend}}</p> <p class="stat-text" :style="{fontSize: item.titleFontSize + 'px'}">{{item.legend}}</p>
<template v-if="item.mapping && item.mapping.icon"> <p class="stat-text" :style="{fontSize: item.valueFontSize + 'px'}">
<i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i> <template v-if="item.mapping && item.mapping.icon">
</template> <i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
<span style="white-space: nowrap;">{{handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })}}</span> </template>
<span>{{handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })}}</span>
</p>
</div> </div>
<div v-else> <div v-else>
<p style="white-space: nowrap;">{{item.legend}}</p> <p class="stat-text" :style="{fontSize: item.titleFontSize + 'px'}">{{item.legend}}</p>
<span style="white-space: nowrap;">{{item.showValue}}</span> <p class="stat-text" :style="{fontSize: item.valueFontSize + 'px'}">{{item.showValue}}</p>
</div> </div>
</template> </template>
<!-- legend --> <!-- legend -->
<template v-if="chartInfo.param.text==='legend'"> <template v-if="chartInfo.param.text==='legend'">
<div v-if="item.mapping" :style="{color:item.mapping.color.text}"> <div v-if="item.mapping" :style="{color:item.mapping.color.text}">
<template v-if="item.mapping && item.mapping.icon"> <p class="stat-text" :style="{fontSize: item.valueFontSize + 'px'}">
<i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i> <template v-if="item.mapping && item.mapping.icon">
</template> <i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
<span style="white-space: nowrap;">{{item.legend}}</span> </template>
<span>{{item.legend}}</span>
</p>
</div>
<div v-else>
<p class="stat-text" :style="{fontSize: item.valueFontSize + 'px'}">{{item.legend}}</p>
</div> </div>
<div v-else style="white-space: nowrap;">{{item.legend}}</div>
</template> </template>
<!-- value --> <!-- value -->
<template v-if="chartInfo.param.text==='value'|| !chartInfo.param.text"> <template v-if="chartInfo.param.text==='value'|| !chartInfo.param.text">
<div v-if="item.mapping" :style="{color:item.mapping.color.text}"> <div v-if="item.mapping" :style="{color:item.mapping.color.text}">
<template v-if="item.mapping && item.mapping.icon"> <p class="stat-text" :style="{fontSize: item.valueFontSize + 'px'}">
<i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i> <template v-if="item.mapping && item.mapping.icon">
</template> <i :class="item.mapping.icon" :style="{color: item.mapping.color.icon,fontSize:'1em'}"></i>
<span style="white-space: nowrap;">{{handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })}}</span> </template>
<span>{{handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })}}</span>
</p>
</div>
<div v-else style="white-space: nowrap;">
<p class="stat-text" :style="{fontSize: item.valueFontSize + 'px'}">{{item.showValue}}</p>
</div> </div>
<div v-else style="white-space: nowrap;">{{item.showValue}}</div>
</template> </template>
<!-- none --> <!-- none -->
<template v-if="chartInfo.param.text==='none'"></template> <template v-if="chartInfo.param.text==='none'"></template>
@@ -118,7 +127,7 @@
</div> </div>
</div> </div>
</div> </div>
<span class="temp-dom--12" ref="temp-dom"></span> <span class="temp-dom" ref="temp-dom"></span>
</div> </div>
</template> </template>
@@ -133,6 +142,7 @@ import { initColor } from '@/components/chart/chart/tools'
import * as echarts from 'echarts' import * as echarts from 'echarts'
import chartSparklineOption from './options/chartSparkline' import chartSparklineOption from './options/chartSparkline'
import lodash from 'lodash' import lodash from 'lodash'
import tinycolor from 'tinycolor2'
export default { export default {
name: 'chart-stat', name: 'chart-stat',
mixins: [chartMixin, chartFormat], mixins: [chartMixin, chartFormat],
@@ -291,7 +301,6 @@ export default {
}) })
}, },
renderStat (layout) { renderStat (layout) {
let isDouble = false
const width = this.boxWidth / layout.col const width = this.boxWidth / layout.col
const height = this.boxHeight / layout.row const height = this.boxHeight / layout.row
const integer = Math.floor(this.statData.length / layout.col) const integer = Math.floor(this.statData.length / layout.col)
@@ -305,86 +314,62 @@ export default {
} else { } else {
item.width = width item.width = width
} }
let font = '' // 获取显示的字体
let display = '' let display = ''
if (item.mapping) { if (item.mapping) {
display = this.handleDisplay(item.mapping.display, { ...item.label, value: item.showValue }) display = this.handleDisplay(item.mapping.display, { ...item.label, value: item.showValue })
} }
let titleFont = ''
let valueFont = ''
let titleFontSize = ''
let valueFontSize = ''
const padding = 10
switch (this.chartInfo.param.text) { switch (this.chartInfo.param.text) {
case 'all': case 'all':
titleFont = item.legend
if (item.mapping) { if (item.mapping) {
isDouble = true valueFont = display
if (item.legend.length > item.showValue.length + 2) { if (item.mapping.icon) {
font = item.legend valueFont += 'AA'
} else {
font = display
if (item.mapping.icon) {
font += 'AA'
}
} }
} else { } else {
isDouble = true valueFont = item.showValue
if (item.legend.length > item.showValue.length) {
font = item.legend
} else {
font = item.showValue
}
} }
titleFontSize = this.calculateFontSize(titleFont, (item.width - padding * 2), (item.height - padding * 2) * 0.4, 30)
valueFontSize = this.calculateFontSize(valueFont, (item.width - padding * 2), (item.height - padding * 2) * 0.4)
titleFontSize = Math.min(valueFontSize * 0.7, titleFontSize)
break break
case 'legend': case 'legend':
titleFont = item.legend
if (item.mapping) { if (item.mapping) {
isDouble = true if (item.mapping.icon) {
if (item.legend.length > item.showValue.length + 2) { titleFont += 'AA'
font = item.legend
} else {
font = display
if (item.mapping.icon) {
font += 'AA'
}
} }
} else {
font = item.legend
} }
valueFontSize = this.calculateFontSize(titleFont, (item.width - padding * 2), (item.height - padding * 2) * 0.4)
break break
case 'none': case 'none':
font = '' titleFontSize = ''
valueFontSize = ''
break break
case 'value': case 'value':
default: default:
if (item.mapping) { if (item.mapping) {
font = display valueFont = display
if (item.mapping.icon) { if (item.mapping.icon) {
font += 'AA' valueFont += 'AA'
} }
} else { } else {
font = item.showValue valueFont = item.showValue
} }
valueFontSize = this.calculateFontSize(valueFont, (item.width - padding * 2), (item.height - padding * 2) * 0.4)
break break
} }
const el = this.$refs['temp-dom'] item.titleFontSize = titleFontSize
el.innerText = font item.valueFontSize = valueFontSize
const elWidth = el.offsetWidth
const elHeight = el.offsetHeight
const dimension = Math.min(item.width, item.height * 1.3)
const fontScale = parseInt('70%', 10) / 100
let fontSize = Math.min(dimension / 5, 100) * fontScale
let scale = item.width / elWidth
if (scale * elHeight > item.height) {
scale = item.height / elHeight
}
if (isDouble) {
if (scale * elHeight * 2.5 > item.height) {
scale = item.height / elHeight * 2.5
}
}
item.scale = scale < 1 ? 1 : parseFloat(scale)
fontSize = fontSize > 12 ? fontSize : this.minFontSzie
item.fontSize = fontSize
if (fontSize > 36) { if (item.valueFontSize > 36) {
item.comparisonFont = 16 item.comparisonFont = 16
} else if (fontSize > 24) { } else if (item.valueFontSize > 24) {
item.comparisonFont = 14 item.comparisonFont = 14
} else { } else {
item.comparisonFont = 12 item.comparisonFont = 12
@@ -392,28 +377,43 @@ export default {
}) })
this.isInit = false this.isInit = false
}, },
calculateFontSize (text, width, height, maxSize, lineHeight = 1.2) {
const el = this.$refs['temp-dom']
el.innerText = text
const elWidth = el.offsetWidth
const fontSizeBasedOnWidth = (width / (elWidth + 2)) * 14
const fontSizeBasedOnHeight = height / lineHeight
const optimalSize = Math.min(fontSizeBasedOnHeight, fontSizeBasedOnWidth)
return Math.min(optimalSize, maxSize || optimalSize)
},
// 绘制图表 // 绘制图表
drawChart () { drawChart () {
this.statData.forEach((item, index) => { this.statData.forEach((item, index) => {
const chart = this.sparkline[index] ? this.sparkline[index] : echarts.init(document.getElementById(`chart-canvas-${this.chartId}-${index}`)) const chart = this.sparkline[index] ? this.sparkline[index] : echarts.init(document.getElementById(`chart-canvas-${this.chartId}-${index}`))
const chartOption = lodash.cloneDeep(chartSparklineOption) const chartOption = lodash.cloneDeep(chartSparklineOption)
// sparkline 颜色和 字体颜色 保持一致
const theme = localStorage.getItem(`nz-user-${localStorage.getItem('nz-user-id')}-theme`) || 'light' // sparkline 颜色根据背景色计算
let color = theme === 'light' ? '#666666' : '#BEBEBE' const color = item.mapping ? item.mapping.color.bac : this.colorList[index]
if (item.mapping) { const lineColor = tinycolor(color).brighten(40).toRgbString()
color = item.mapping.color.text let areaColor = 'rgba(255,255,255,0.4)'
if (this.statData.length === 1 && !item.mapping) {
areaColor = tinycolor(color).setAlpha(0.2).toRgbString()
} }
chartOption.series = [{ chartOption.series = [{
...chartOption.series[0], ...chartOption.series[0],
data: item.data, data: item.data,
lineStyle: { lineStyle: {
color width: 1,
color: lineColor
} }
}] }]
if (this.chartInfo.param.sparklineMode === 'area') { if (this.chartInfo.param.sparklineMode === 'area') {
chartOption.series[0].areaStyle = { chartOption.series[0].areaStyle = {
color: color, color: areaColor
opacity: 0.1
} }
} }
chartOption.yAxis.max = this.maxValue chartOption.yAxis.max = this.maxValue