map+echart+vue 大数据多次渲染卡顿问题
尤其是地图组件比较庞大,再导入大批量数据最容易造成卡顿的出现。
尤其是渲染次数多,交互多的情况。
所以每次重新加载数据一定要先销毁之前的。
不然越越卡直到卡死
父组件:
<div class="mapchart">
<amap :key="mapkey" ref='mymap'></amap>
</div>
chosethismaptab(ind){ // 每次重新交互接受新数据
this.tabind = ind
this.$refs.mymap.destr() // 每次重新加载 通知子组件先销毁再渲染
this.mapkey+=1
},
子组件
destr(){ this.myChart.dispose(); },
这样就不会再卡顿了,不管怎么交互,点击多少次重新渲染都没问题了。
卡顿容易造成渲染等待越来越长:

不卡顿状态:

就算下钻再深也没关系
完整代码:
<!-- 大区服务 --> <template> <div class="main"> <headbreak style="background: #fff" :titlearr="titlearr"></headbreak> <div style="display:none;"> <el-button @click="toPage('1')">30分钟及时预约率</el-button> <el-button @click="toPage('2')">预约准时上门率</el-button> <el-button @click="toPage('3')">TAT平均服务完成时长</el-button> <el-button @click="toPage('4')">投诉7天解决率</el-button> <el-button @click="toPage('5')">一次修复率</el-button> <el-button @click="toPage('6')">2天维修达成率</el-button> <el-button @click="toPage('7')">N+1投诉解决方案提交率</el-button> </div> <!-- 搜索栏 --> <div style="width:100%;display:flex;margin:10px;"> <!-- <amap></amap> --> <el-button @click="qucklysearch('近3天')">近3天</el-button> <el-button @click="qucklysearch('近7天')">近7天</el-button> <el-button @click="qucklysearch('近30天')">近30天</el-button> <el-button @click="qucklysearch('本周')">本周</el-button> <el-button @click="qucklysearch('本月')">本月</el-button> <el-button @click="qucklysearch('自定义')">自定义</el-button> <div style="width:300px;margin-left:10px;"> <mytimer :timerrange.sync = "timerangedate"></mytimer> </div> <span> <el-select v-model="pinleitype"> <el-option value="1">1</el-option> <el-option value="2">2</el-option> </el-select> </span> <span> <el-button v-popover:popover2>默认 <i class="el-icon-arrow-down"></i></el-button> <el-popover ref="popover2" placement="bottom" title="标题" width="200" trigger="click" content="这是一段内容,这是一段内容,这是一段内容,这是一段内容。"> <p> <span>洗衣机品类近七日数据</span> <span>首选项</span> <span><i class="el-icon-delete"></i></span> </p> <p>21212112</p> <p>21212112</p> </el-popover> </span> </div> <!-- 折现图表看板 --> <div class="boardbox1"> <ul> <li> <cardyuyuelv></cardyuyuelv> </li> <li> <cardyuyuelv id="chart_category_2" title="预约准时上门率" wanchenglv="80%" ></cardyuyuelv> </li> <li> <cardyuyuelv id="chart_category_3" title="改约率" wanchenglv="10%" ></cardyuyuelv> </li> <li> <cardyuyuelv id="chart_category_4" title="TAT平均服务完成时长" wanchenglv="1天3小时" ></cardyuyuelv> </li> </ul> </div> <!-- 地图 --> <div class="mapbox"> <ul> <li @click="chosethismaptab(1)"> <p>换件维修TAT</p> <p :class="this.tabind == 1 ? 'active' : ''">1天7小时</p> </li> <li @click="chosethismaptab(2)"> <p>不换件维修TAT</p> <p :class="this.tabind == 2 ? 'active' : ''">1天7小时</p> </li> <li @click="chosethismaptab(3)"> <p>快速安装TAT</p> <p :class="this.tabind == 3 ? 'active' : ''">1天7小时</p> </li> <li @click="chosethismaptab(4)"> <p>鉴定TAT</p> <p :class="this.tabind == 4 ? 'active' : ''">1天7小时</p> </li> <li @click="chosethismaptab(5)"> <p>非上门服务TAT</p> <p :class="this.tabind == 5 ? 'active' : ''">1天7小时</p> </li> </ul> <div class="mapcontent"> <div class="mapchart"> <amap :key="mapkey" ref='mymap'></amap> </div> <div class="zhibiao"> <p> <span></span> <span>2小时</span> <span>36%</span> </p> <p> <span></span> <span>1~3天</span> <span>24%</span> </p> <p> <span></span> <span>4~7天</span> <span>20%</span> </p> <p> <span></span> <span>7天以上</span> <span>20%</span> </p> </div> <div class="maptabs"> <el-tabs style="margin-top:20px;height:400px;" type="border-card"> <el-tab-pane label="大区排名"> <p @click="fnchosethislist"> <span>1</span> <span>所属大区</span> <span>1天4小时</span> </p> <p> <span>2</span> <span>所属大区</span> <span>1天4小时</span> </p> </el-tab-pane> <el-tab-pane label="门店排名"> <p> <span>1</span> <span>所属门店</span> <span>1天4小时</span> </p> <p> <span>2</span> <span>所属门店</span> <span>1天4小时</span> </p> </el-tab-pane> </el-tabs> </div> </div> </div> </div> </template> <script> export default { data() { return { timerangedate:"", pinleitype:"", titlearr:["数据看板","大区服务"], tabind:1, mapkey:1, } }, components:{ headbreak: () => import("../partsManagement/components/headbreak.vue"), amap:()=>import('./charts/mapchart.vue'), mytimer: ()=>import('../settlementMgt/components/mypickerange.vue'), cardyuyuelv: ()=>import('./components/cardyuyuelv.vue') }, methods: { fnchosethislist(){ }, chosethismaptab(ind){ // 每次重新交互接受新数据 this.tabind = ind this.$refs.mymap.destr() // 每次重新加载 通知子组件先销毁再渲染 this.mapkey+=1 }, toPage(val) { switch (val) { case'1': this.$router.push({ name:'timelyRatw'}); break; case '2': this.$router.push({ name:'punctualRate'}); break; case '3': this.$router.push({ name:'avgTime'}); break; case '4': this.$router.push({ name:'resolutionRate'}); break; case '5': this.$router.push({ name:'repairRate'}); break; case '6': this.$router.push({ name:'maintenanceRate'}); break; case '7': this.$router.push({ name:'reductionRate'}); break; } return type; }, qucklysearch(timer){ console.log(timer) }, } } </script> <style lang="less" scoped> .main{ width:99.8%; // background:pink; } .mapbox{ margin:10px; ul{ display: flex;justify-content:space-between;cursor:pointer;border:1px solid #ccc; li{ p{position:relative;bottom:-11px;} p:nth-child(1){ font-size:16px; } p:nth-child(2){ font-size:30px; &.active{ border-bottom:2px solid blue; } } } } .mapcontent{ border:1px solid #ccc;padding-bottom:20px; display: flex;justify-content:flex-start; .mapchart{width:50%; height:430px; background:#fff; } .zhibiao{width:12%; position:relative;top:300px; >p{ color:rgb(57, 53, 53);font-size:14px; span:nth-child(1){display: inline-block;width:10px;height:10px;border-radius:10px;background:rgb(45, 151, 44);} span:nth-child(2){margin-left:10px;} span:nth-child(3){margin-left:10px;} } } .maptabs{width:38%;padding:10px; p{ cursor:pointer; span:nth-child(1){display: inline-block;width:20px;height:20px;border-radius:10px;background:red;text-align: center;} span:nth-child(2){margin-left:30px;font-size:20px;} span:nth-child(3){margin-left:30px;font-size:20px;} } } } } .boardbox1{ ul{ display: flex;justify-content:space-between; li{ width:25%; // border:1px solid #ccc; // box-shadow: 2px 4px 8px #ccc; } } } </style>
<template>
<div>
<div id="container">
<div class="back" @click="backfn">返 回</div>
<div :id="root" class="rootCls"></div>
</div>
</div>
</template>
<script>
import AMapLoader from "@amap/amap-jsapi-loader";
import * as echarts from "echarts";
var geoJson = {
features: [],
};
var parentInfo = [
{
cityName: "全国",
level: "china",
code: 100000,
},
];
var timeTitle = ["2022"];
export default {
name: "",
props: {
root:{
type:String,
default:'root'
}
},
data() {
return {
myChart:null,
};
},
components: {},
methods: {
// 渲染地图echarts
initEchartMap(mapData, sum, pointData) {
//这里做个切换,全国的时候才显示南海诸岛 只有当注册的名字为china的时候才会显示南海诸岛
if (parentInfo.length === 1) {
echarts.registerMap("china", geoJson); //注册
} else {
echarts.registerMap("map", geoJson); //注册
}
var option = {
timeline: {
show:false, // 底部时间线关闭
data: timeTitle,
axisType: "category",
autoPlay: true,
playInterval: 3000,
left: "10%",
right: "10%",
bottom: "3%",
width: "80%",
label: {
normal: {
textStyle: {
color: "rgb(179, 239, 255)",
},
},
emphasis: {
textStyle: {
color: "#fff",
},
},
},
symbolSize: 10,
lineStyle: {
color: "#8df4f4",
},
checkpointStyle: {
borderColor: "#8df4f4",
color: "#53D9FF",
borderWidth: 2,
},
controlStyle: {
showNextBtn: true,
showPrevBtn: true,
normal: {
color: "#53D9FF",
borderColor: "#53D9FF",
},
emphasis: {
color: "rgb(58,115,192)",
borderColor: "rgb(58,115,192)",
},
},
},
baseOption: {
animation: true,
animationDuration: 1000,
animationEasing: "cubicInOut",
animationDurationUpdate: 1000,
animationEasingUpdate: "cubicInOut",
tooltip: {
trigger: "item",
},
toolbox: {
show:false, // 转为table 展示 关闭
feature: {
restore: {
show: false,
},
dataView: {
optionToContent: function (opt) {
let series = opt.series[0].data; //折线图数据
let tdHeads =
'<th style="padding: 0 20px">所在地区</th><th style="padding: 0 20px">销售额</th>'; //表头
let tdBodys = ""; //数据
let table = `<table border="1" style="margin-left:20px;border-collapse:collapse;font-size:14px;text-align:left;"><tbody><tr>${tdHeads} </tr>`;
for (let i = 0; i < series.length; i++) {
table += `<tr>
<td style="padding: 0 50px">${series[i].name}</td>
<td style="padding: 0 50px">${series[
i
].value.toFixed(2)}万</td>
</tr>`;
}
table += "</tbody></table>";
return table;
},
},
saveAsImage: {
show:false, // 下载地图功能
name:
parentInfo[parentInfo.length - 1].cityName + "销售额统计图",
},
dataZoom: {
show: false,
},
magicType: {
show: false,
},
},
iconStyle: {
normal: {
borderColor: "#1990DA",
},
},
top: 15,
right: 35,
},
geo: {
map: parentInfo.length === 1 ? "china" : "map",
zoom: 1.2, // 初始化大小
top:50,
roam: true,
tooltip: {
trigger: "item",
formatter: (p) => {
let val = p.value[2];
if (window.isNaN(val)) {
val = 0;
}
let txtCon =
"<div style='text-align:left'>" +
p.name +
":<br />销售额:" +
val.toFixed(2) +
"万</div>";
return txtCon;
},
},
label: {
normal: {
show: true,
color: "rgb(249, 249, 249)", //省份标签字体颜色
formatter: (p) => {
switch (
p.name // 地图上的标名
) {
case "内蒙古自治区":
p.name = "内蒙古";
break;
case "西藏自治区":
p.name = "西藏";
break;
case "新疆维吾尔自治区":
p.name = "新疆";
break;
case "宁夏回族自治区":
p.name = "宁夏";
break;
case "广西壮族自治区":
p.name = "广西";
break;
case "香港特别行政区":
p.name = "香港";
break;
case "澳门特别行政区":
p.name = "澳门";
break;
default:
break;
}
return p.name;
},
},
emphasis: {
show: true,
color: "#f75a00",
},
},
itemStyle: {
normal: {
areaColor: "#24CFF4",
borderColor: "#53D9FF",
borderWidth: 1.3,
shadowBlur: 15,
shadowColor: "rgb(58,115,192)",
shadowOffsetX: 7,
shadowOffsetY: 6,
},
emphasis: {
areaColor: "#8dd7fc",
borderWidth: 1.6,
shadowBlur: 25,
},
},
},
},
options: [],
};
timeTitle.forEach((item) => {
var min = mapData[item][mapData[item].length - 1].value;
var max = mapData[item][0].value;
if (mapData[item].length === 1) {
min = 0;
}
option.options.push({
// backgroundColor: "#012248",// 背景色
title: [ // 头部标题
// {
// left: "center",
// top: 10,
// text:
// item +
// "年" +
// parentInfo[parentInfo.length - 1].cityName +
// "销售额统计图(可点击)",
// textStyle: {
// color: "red",
// fontSize: 12,
// },
// },
// {
// text: "年度销售总额:" + sum[item].toFixed(2) + "万",
// left: "center",
// top: "6.5%",
// textStyle: {
// color: "#FFAC50",
// fontSize: 26,
// },
// },
],
visualMap: {
min: min,
max: max,
left: "-13%", // 隐掉左下脚本条
bottom: "5%",
calculable: true,
seriesIndex: [0],
inRange: {
color: ["#24CFF4", "#2E98CA", "#1E62AC"], // 地图颜色配置 -原色
// color: ["#010824", "#010824", "#010824"], // 地图颜色配置
},
textStyle: {
color: "#24CFF4",
},
},
series: [
{
name: item + "销售额度",
type: "map",
geoIndex: 0,
map: parentInfo.length === 1 ? "china" : "map",
roam: true,
zoom: 1.3,
tooltip: {
trigger: "item",
formatter: (p) => {
let val = p.value;
if (p.name == "南海诸岛") return;
if (window.isNaN(val)) {
val = 0;
}
let txtCon =
"<div style='text-align:left'>" +
p.name +
":<br />销售额:" +
val.toFixed(2) +
"万</div>";
return txtCon;
},
},
label: {
normal: {
show: false,
},
emphasis: {
show: false,
},
},
data: mapData[item],
},
{
name: "散点",
type: "effectScatter",
coordinateSystem: "geo",
rippleEffect: {
// brushType: "fill",
brushType: 'stroke'
},
showEffectOn: 'render', //'emphasis' 高亮(hover)的时候显示特效
hoverAnimation: true,//图形上的文本标签,可用于说明图形的一些数据信息,比如值,名称等
itemStyle: {
normal: {
// color: 'purple',
color: "#F4E925", // 地图散点配置色 - 原色
// color: "#6034fd", // 地图散点配置色
shadowBlur: 10,
shadowColor: "#333",
},
},
data: pointData[item],
// symbolSize: 8,
symbolSize: function (val) {
let value = val[1];
if (value == max) {
return 11;
}
return 10;
},
showEffectOn: "render", //加载完毕显示特效
},
],
});
});
console.log(this.root,'---')
var myChart = echarts.init(document.getElementById(this.root));
this.myChart = echarts.init(document.getElementById(this.root));
myChart.setOption(option, true); // 第二个参数尤其重要,如果不设置地图一点击就会跑偏甚至跑出content,设置后会原始坐标上渲染显示
//点击前解绑,防止点击事件触发多次
myChart.off("click");
myChart.on("click", this.echartsMapClick);
},
// 监听echarts 的点击事件
echartsMapClick(params) {
if (!params.data) {
return;
} else {
//如果当前是最后一级,那就直接return
if (parentInfo[parentInfo.length - 1].code == params.data.cityCode) {
return;
}
let data = params.data;
parentInfo.push({
cityName: data.name,
level: data.level,
code: data.cityCode,
});
this.getGeoJson(data.cityCode);
}
},
// 获取地图数据
getMapData() {
let mapData = {},
pointData = {},
sum = {};
for (let i = 0; i < timeTitle.length; i++) {
mapData[timeTitle[i]] = [];
pointData[timeTitle[i]] = [];
sum[timeTitle[i]] = 0;
for (let j = 0; j < geoJson.features.length; j++) {
let value = 1;
mapData[timeTitle[i]].push({
name: geoJson.features[j].properties.name,
value: value,
level: geoJson.features[j].properties.level,
cityCode: geoJson.features[j].properties.adcode,
});
pointData[timeTitle[i]].push({
name: geoJson.features[j].properties.name,
value: [
geoJson.features[j].properties.center[0],
geoJson.features[j].properties.center[1],
value,
],
cityCode: geoJson.features[j].properties.adcode,
});
sum[timeTitle[i]] += value;
}
mapData[timeTitle[i]] = mapData[timeTitle[i]].sort(function (a, b) {
return b.value - a.value;
});
}
this.initEchartMap(mapData, sum, pointData);
},
// 获取地图初始化json数据
getGeoJson(adcode) {
// console.log(this) // vue
let that = this;
console.log(adcode, AMapUI, "------");
AMapUI.loadUI(["geo/DistrictExplorer"], (DistrictExplorer) => {
var districtExplorer = new DistrictExplorer();
districtExplorer.loadAreaNode(adcode, function (error, areaNode) {
if (error) {
console.error(error);
return;
}
let Json = areaNode.getSubFeatures();
if (Json.length > 0) {
geoJson.features = Json;
} else if (Json.length === 0) {
geoJson.features = geoJson.features.filter(
(item) => item.properties.adcode == adcode
);
if (geoJson.features.length === 0) return;
}
// console.log(this) // DistrictExplorer
that.getMapData();
});
});
},
// 返回上一级
backfn() {
if (parentInfo.length === 1) {
return;
}
parentInfo.pop();
this.getGeoJson(parentInfo[parentInfo.length - 1].code);
},
// 初始化地图 必须
initAMap() {
AMapLoader.load({
key: mykey,
version: "2.0",
plugins: ["AMap.ToolBar", "AMap.Driving"],
AMapUI: {
version: "1.1",
plugins: [],
},
Loca: {
version: "2.0",
},
})
.then((AMap) => {
// 注释的内容是高德官网渲染其它地图的组件 测试用例 这里不再演示
// this.map = new AMap.Map("container", {
// viewMode: "3D",
// zoom: 5,
// zooms: [2, 22],
// center: [105.602725, 37.076636],
// });
// this.my_init(); // 高德测试用例用来调用高德ui组件
this.getGeoJson(100000);
})
.catch((e) => {
console.log(e);
});
},
my_init() {
// 高德测试用例用来调用高德ui组件
// initAMapUI(); //这里调用initAMapUI前先初始化
// //其他逻辑
// AMapUI.loadUI(
// [
// "overlay/SimpleMarker", //SimpleMarker
// "overlay/SimpleInfoWindow", //SimpleInfoWindow
// ],
// (SimpleMarker, SimpleInfoWindow) => {
// //....引用加载的UI....
// console.log(SimpleMarker, SimpleInfoWindow);
// this.initPage(SimpleMarker);
// }
// );
},
destr(){
console.log('dispose')
this.myChart.dispose();
},
//初始化到页面
initPage(SimpleMarker) {
//创建SimpleMarker实例
new SimpleMarker({
//前景文字
iconLabel: "点",
//图标主题
iconTheme: "default",
//背景图标样式
iconStyle: "pink",
//...其他Marker选项...,不包括content
map: this.map,
position: [117.405285, 39.904989],
});
//创建SimpleMarker实例
new SimpleMarker({
//前景文字
iconLabel: {
innerHTML: "<i>B</i>", //设置文字内容
style: {
color: "#fff", //设置文字颜色
},
},
//图标主题
iconTheme: "fresh",
//背景图标样式
iconStyle: "black",
//...其他Marker选项...,不包括content
map: this.map,
position: [110.315285, 39.924989],
});
},
},
mounted() {
// this.getGeoJson(100000);
this.initAMap();
},
};
</script>
<style scoped lang="less">
.rootCls {
width: 600px;
height: 430px;
// border: 1px solid rgb(161, 153, 153);
position: fixed;
top: 0px;
z-index: 10;
}
.back {
text-align: center;
width: 60px;
height: 30px;
line-height: 30px;
border: 1px solid #fff;
border-radius: 4px;
position: absolute;
left: 20px;
top: 30px;
color: rgb(32, 43, 46);
font-size: 16px;
cursor: pointer;
z-index: 100000;
}
#container {
padding: 0px;
margin: 0px;
// width: 100%;
// height: 100%;
position: absolute;
z-index: 9;
}
</style>

浙公网安备 33010602011771号