前言
效果图
主要问题点
1、柱状图重叠stack
2、数据过大显示处理log(对数坐标)
3、对数(极)坐标0数据处理
4、0数据项显示为灰色柱状条
5、窗口变化自适应
6、侧边导航展开收起对echarts图的影响
-
柱状图重叠stack
yAxis: {
type: 'category',
boundaryGap: true,
show: true,
axisLine: {
lineStyle: {
opacity: 0,
},
},
axisTick: { show: false },
data: ['网络风险', '应用风险', '攻击威胁', '数据泄漏', 'APP风险'],
},
xAxis: {
type: 'value',
show: true,
axisLine: {
show: false,
},
},
series: [
{
name: '已认证企业',
data: ['1', '2', '700', '10000', '80'],
type: 'bar',
smooth: true,
stack: 'total',
emphasis: {
focus: 'series',
},
itemStyle: {
color: 'rgba(15, 175, 255, 1)',
borderRadius: [7, 0, 0, 7],
borderWidth: 0,
},
barWidth: 15,
barMinHeight: 10,
},
{
name: '三方供应商',
data: ['100', '200000', '700', '200', '800000'],
type: 'bar',
stack: 'total',
smooth: true,
emphasis: {
focus: 'series',
},
itemStyle: {
color: 'rgba(36, 207, 241, 1)',
borderRadius: [0, 7, 7, 0],
borderWidth: 0,
},
barWidth: 15,
barMinHeight: 10,
},
],
以上数据展示效果图:发现超大数据影响柱状图大小 -
数据过大显示处理log(对数坐标轴) xAxis: {
++ type: 'log',
--
show: true,
axisLine: {
show: false,
},
},
数值坐标系改为:log对数坐标系
啥是对数坐标轴:n=log(a)(b);看下图,这样我们就能理解为什么图可以展示全了
现在效果图 发现:鼠标移上去其他数据被淡出 处理:series->emphasis:disabled: true, series: [
{
name: '已认证企业',
data: ['1', '2', '700', '10000', '80'],
type: 'bar',
smooth: true,
stack: 'total',
emphasis: {
focus: 'series',
++ disabled: true,
},
startValue: 100,
lineStyle: {
color: '#2878FF',
width: 1,
},
itemStyle: {
color: (params:any) => {
if (params.value === 105.05) {
return '#ccc'
}
return 'rgba(15, 175, 255, 1)'
},
borderRadius: [7, 0, 0, 7],
borderWidth: 0,
},
barWidth: 15,
barMinHeight: 10,
},
-
极坐标0数据处理 根据对数坐标系n=log(a)(b) a>0;那么后台返回数据项如果有0的话,会显示如下 0项数据置为undefined 这里的data是获取后台返回的接口数据,然后进行遍历操作
const arr = data.map((item:any) => ({
risk_network: item.risk_network || undefined,
risk_app: item.risk_app || undefined,
risk_attack: item.risk_attack || undefined,
risk_data_leak: item.risk_data_leak || undefined,
risk_mobile_app: item.risk_mobile_app || undefined,
}))
initContainer(arr)
提示判断是否有undefined
tooltip: {
padding: 0,
backgroundColor: 'rgba(255,255,255,0)',
trigger: 'axis',
axisPointer: {
type: 'shadow',
axis: 'y',
},
formatter(params:any) {
let tipHtml = '';
tipHtml = `${'<div style="box-sizing: border-box;box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.08), 0px 0px 12px rgba(0, 0, 0, 0.08);border-radius: 4px;width:160px;linear-gradient(180deg, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0) 100%);font-size: 9px;padding:1px 8px;">'
+ '<div style="width:86%;height:30px;line-height:30px;">'
+ '<span style="color:#181818;font-size:12px;">'}${params[0]?.axisValue}</span>`
+ '</div>'
+ '<div style="width: 144px;padding-bottom:4px">'
+ '<p style="color:#666666;border-radius: 2px;font-size:12px;line-height:30px;background: #FFFFFF;margin-bottom:6px;padding:0 6px">'
+ `<span style="width: 6px;height: 6px;display:inline-block;vertical-align: middle;border-radius: 6px; background:${params[0]?.color};margin:0 3px;"></span>`
+ `${params[0]?.seriesName}`
+ `<span style="color:#000;margin:0 6px;float: right;">${params[0]?.value === undefined ? 0 : params[0]?.value}</span>`
+ '</p>'
+ `<p style="color:#666666;border-radius: 2px;line-height:30px;background: #FFFFFF;font-size:12px; display:${params[1] ? 'block' : 'none'}">`
+ `<span style="width: 6px;height: 6px;display:inline-block;vertical-align: middle;border-radius: 6px; background:${params[1]?.color};margin:0 3px;"></span>`
+ `${params[1]?.seriesName}`
+ `<span style="color:#000;margin:0 6px;float: right;">${params[1]?.value === undefined ? 0 : params[1]?.value}</span>`
+ '</p>'
+ '</div>'
return tipHtml;
},
-
0数据项显示为灰色柱状条 如果好几项为空,页面显示就会很空荡
处理:因为数据都是整数,所以当数据返回为0时,我们给他一个小数105.5;在设置颜色的时候,
为其加一个灰色
1、undefined改成105.5
const arr = data.map((item:any) => ({
risk_network: item.risk_network || 105.05,
risk_app: item.risk_app || 105.05,
risk_attack: item.risk_attack || 105.05,
risk_data_leak: item.risk_data_leak || 105.05,
risk_mobile_app: item.risk_mobile_app || 105.05,
}))
initContainer(arr)
2、tooltip->formatter中的undefined改成105.5
${params[0]?.value === undefined ? 0 : params[0]?.value}
3、series->itemStyle->color使用回调函数形式
series: [
{
name: '已认证企业',
data: ['0', '0', '0', '10000', '80'],
type: 'bar',
smooth: true,
stack: 'total',
emphasis: {
focus: 'series',
disabled: true,
},
startValue: 100,
lineStyle: {
color: '#2878FF',
width: 1,
},
itemStyle: {
++ color: (params:any) => {
++ if (params.value === 105.05) {
++ return '#ccc'
++ }
++ return 'rgba(15, 175, 255, 1)'
++ },
borderRadius: [7, 0, 0, 7],
borderWidth: 0,
},
barWidth: 15,
barMinHeight: 10,
},
-
窗口变化自适应 只需要在执行函数时进行窗口监听视图大小变化,进而重绘就可以了
resize()方法
import { ECharts } from 'echarts';
const scatterChart = ref<ECharts | null>(null);
onMounted(() => {
window.onresize = () => {
scatterChart.value?.resize();
};
})
-
侧边导航展开收起对echarts图的影响 当侧边栏有展开收起时,echarts图不会自适应,这个时候需要重绘我们的echarts图,
我想的是既然窗口能监听,那么dom元素是否也可以监听
https://developer.mozilla.org/zh-CN/docs/Web/API/ResizeObserver/observe <div
id="riskTypeColumnar"
ref="refriskTypeColumnar"
/>
const refriskTypeColumnar = ref<HTMLElement |null>(null)
const resizeObserver = new ResizeObserver((entries:any) => {
scatterChart.value?.resize();
});
onMounted(() => {
++ resizeObserver.observe(refriskTypeColumnar.value);
window.onresize = () => {
scatterChart.value?.resize();
};
})
注意:需要给它父级dom设置宽度
-
代码 <template>
<div
style="position: absolute;
left: 0px;
top: 19.5px;
font-weight: 500;
font-size: 16px;
line-height: 25px;
color: #181818;"
>
风险分布
</div>
<div
id="riskTypeColumnar"
ref="refriskTypeColumnar"
/>
</template>
<script setup lang="ts">
import {
ref, onUnmounted, markRaw, watch, onMounted,
} from 'vue';
import { ECharts } from 'echarts';
import { overviewRiskDistribution } from '@/api/user';
const scatterChart = ref<ECharts | null>(null);
const refriskTypeColumnar = ref<HTMLElement |null>(null)
const riskTotal = ref<any>(0);
const assetsTotal = ref<any>(0);
const getTableData = async () => {
const params:any = {
scope: 0,
};
const { code, msg, data } = await overviewRiskDistribution(params);
if (scatterChart.value) {
scatterChart.value?.dispose();
}
const arr = data.map((item:any) => ({
risk_network: item.risk_network || 105.05,
risk_app: item.risk_app || 105.05,
risk_attack: item.risk_attack || 105.05,
risk_data_leak: item.risk_data_leak || 105.05,
risk_mobile_app: item.risk_mobile_app || 105.05,
}))
initContainer(arr)
};
onUnmounted(() => {
scatterChart.value?.dispose();
});
const initContainer = async (data:any) => {
const echarts = await import('echarts');
scatterChart.value = markRaw(echarts.init((document as any).getElementById('riskTypeColumnar')));
renderMap(data)
};
const renderMap = (data:any) => {
const option = {
legend: {
type: 'scroll',
pageButtonItemGap: 2,
itemWidth: 10,
itemHeight: 6,
itemGap: 30,
icon: 'rect',
data: [{
name: '已认证企业',
itemStyle: { color: '#0FAFFF' },
icon: 'roundRect',
}, {
name: '三方供应商',
itemStyle: { color: '#24CFF1' },
icon: 'roundRect',
}],
pageTextStyle: {
fontSize: 6,
},
bottom: 0,
},
itemStyle: {
borderWidth: 2,
borderColor: '#fff',
},
tooltip: {
padding: 0,
backgroundColor: 'rgba(255,255,255,0)',
trigger: 'axis',
axisPointer: {
type: 'shadow',
axis: 'y',
},
formatter(params:any) {
let tipHtml = '';
tipHtml = `${'<div style="box-sizing: border-box;box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.08), 0px 0px 12px rgba(0, 0, 0, 0.08);border-radius: 4px;width:160px;linear-gradient(180deg, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0) 100%);font-size: 9px;padding:1px 8px;">'
+ '<div style="width:86%;height:30px;line-height:30px;">'
+ '<span style="color:#181818;font-size:12px;">'}${params[0]?.axisValue}</span>`
+ '</div>'
+ '<div style="width: 144px;padding-bottom:4px">'
+ '<p style="color:#666666;border-radius: 2px;font-size:12px;line-height:30px;background: #FFFFFF;margin-bottom:6px;padding:0 6px">'
+ `<span style="width: 6px;height: 6px;display:inline-block;vertical-align: middle;border-radius: 6px; background:${params[0]?.color};margin:0 3px;"></span>`
+ `${params[0]?.seriesName}`
+ `<span style="color:#000;margin:0 6px;float: right;">${params[0]?.value === 105.05 ? 0 : params[0]?.value}</span>`
+ '</p>'
+ `<p style="color:#666666;border-radius: 2px;line-height:30px;background: #FFFFFF;font-size:12px; display:${params[1] ? 'block' : 'none'}">`
+ `<span style="width: 6px;height: 6px;display:inline-block;vertical-align: middle;border-radius: 6px; background:${params[1]?.color};margin:0 3px;"></span>`
+ `${params[1]?.seriesName}`
+ `<span style="color:#000;margin:0 6px;float: right;">${params[1]?.value === 105.05 ? 0 : params[1]?.value}</span>`
+ '</p>'
+ '</div>'
return tipHtml;
},
},
grid: {
left: '3%',
right: '4%',
top: '0%',
bottom: '10%',
containLabel: true,
},
yAxis: {
type: 'category',
boundaryGap: true,
show: true,
axisLine: {
lineStyle: {
opacity: 0,
},
},
axisTick: { show: false },
data: ['网络风险', '应用风险', '攻击威胁', '数据泄漏', 'APP风险'],
},
xAxis: {
type: 'log',
show: true,
axisLine: {
show: false,
},
},
series: [
{
name: '已认证企业',
data: data && [data[0]?.risk_network, data[0]?.risk_app, data[0]?.risk_attack, data[0]?.risk_data_leak, data[0]?.risk_mobile_app],
type: 'bar',
smooth: true,
stack: 'total',
emphasis: {
focus: 'series',
disabled: true,
},
startValue: 100,
lineStyle: {
color: '#2878FF',
width: 1,
},
itemStyle: {
color: (params:any) => {
if (params.value === 105.05) {
return '#ccc'
}
return 'rgba(15, 175, 255, 1)'
},
borderRadius: [7, 0, 0, 7],
borderWidth: 0,
},
barWidth: 15,
barMinHeight: 10,
},
{
name: '三方供应商',
data: data && [data[1]?.risk_network, data[1]?.risk_app, data[1]?.risk_attack, data[1]?.risk_data_leak, data[1]?.risk_mobile_app],
type: 'bar',
stack: 'total',
smooth: true,
emphasis: {
focus: 'series',
disabled: true,
},
startValue: 10,
lineStyle: {
color: '#FF5A5A',
width: 1,
},
itemStyle: {
color: (params:any) => {
if (params.value === 105.05) {
return '#ccc'
}
return 'rgba(36, 207, 241, 1)'
},
borderRadius: [0, 7, 7, 0],
borderWidth: 0,
},
barWidth: 15,
barMinHeight: 10,
},
],
};
scatterChart.value?.setOption(option as any, true);
};
const resizeObserver = new ResizeObserver((entries:any) => {
scatterChart.value?.resize();
});
getTableData()
defineExpose({
initContainer,
});
onMounted(() => {
console.log('refriskTypeColumnar.value', refriskTypeColumnar.value)
resizeObserver.observe(refriskTypeColumnar.value);
window.onresize = () => {
scatterChart.value?.resize();
};
})
</script>
<style scoped>
#riskTypeColumnar {
width: 100%;
margin-top: 55px;
height: calc(100% - 55px);
justify-content: center;
position: relative;
}
</style>
|