|
|
@ -45,32 +45,74 @@ export class MarkerCluster { |
|
|
|
infoWindow; |
|
|
|
mapContainer; |
|
|
|
data = []; //标注集合
|
|
|
|
dataInfo = {}; |
|
|
|
vroot; |
|
|
|
constructor() {} |
|
|
|
|
|
|
|
getMap() { |
|
|
|
return (this.map = Vue.prototype.mapIns); |
|
|
|
} |
|
|
|
|
|
|
|
async addData(_id, _data, isDefault) { |
|
|
|
async addData(data, isDefault) { |
|
|
|
this.infoWindow?.close?.(); |
|
|
|
Vue.prototype.mapInsOpenLayers.push(_id) |
|
|
|
|
|
|
|
//data.forEach(e => {
|
|
|
|
// const lnglat = getLnglat(e.extData);
|
|
|
|
// e.extData.longitude = lnglat[0];//点坐标经度 【必传字段】
|
|
|
|
// e.extData.latitude = lnglat[1];
|
|
|
|
//});
|
|
|
|
marksAddInGraph(_data); |
|
|
|
if (!_data) return; |
|
|
|
if (!Array.isArray(_data)) _data = [_data]; |
|
|
|
marksAddInGraph(data); |
|
|
|
|
|
|
|
if (!data) return; |
|
|
|
if (!Array.isArray(data)) data = [data]; |
|
|
|
|
|
|
|
this.data.push(...data); |
|
|
|
|
|
|
|
this.refreshLayer(_id, _data); |
|
|
|
if(this.mapContainer === 'BMapContainer'){ |
|
|
|
await this.refreshLayer(); |
|
|
|
} else { |
|
|
|
// 高德地图接口----------------------
|
|
|
|
if ( |
|
|
|
!this.markerCluster || |
|
|
|
this.markerCluster.getMap() !== Vue.prototype.mapIns |
|
|
|
){ |
|
|
|
await this.setMarkerCluster(); |
|
|
|
} |
|
|
|
|
|
|
|
refreshLayer(_id,_data){ |
|
|
|
// console.log(
|
|
|
|
// "%c [ data ]-227-「map.js」",
|
|
|
|
// "font-size:15px; background:#641f14; color:#a86358;"
|
|
|
|
// );
|
|
|
|
this.markerCluster.addData(data); |
|
|
|
// console.log(
|
|
|
|
// "%c [ this.markerCluster ]-234-「map.js」",
|
|
|
|
// "font-size:15px; background:#fe94d3; color:#ffd8ff;",
|
|
|
|
// this.markerCluster
|
|
|
|
// );
|
|
|
|
const map = this.getMap(); |
|
|
|
map.setZoom(10); |
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
|
map.setFitView([...this.markerCluster.U], false, [0, 0, 0, 0], 10); //自适应. 覆盖物数组, 动画过渡到指定位置, 周围边距,上、下、左、右, 最大 zoom 级别
|
|
|
|
}, 150); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
async refreshLayer(){ |
|
|
|
const map2d = Vue.prototype.mapIns |
|
|
|
// map2d.addPointByArr(
|
|
|
|
// {pointList:[]}, // 点位数据数组(按以下规范组装)
|
|
|
|
// 'jhlayers', // 当前添加的数据 唯一标识(删除该图层时也会用到)//./事件专题/交通事故.svg
|
|
|
|
// true // 当前新加图层是否启用聚合效果
|
|
|
|
// );
|
|
|
|
const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay)) |
|
|
|
try{ |
|
|
|
console.log('执行remove') |
|
|
|
map2d.removeLayerByName('jhlayers') |
|
|
|
await sleep(2000) |
|
|
|
} catch(e){} |
|
|
|
console.log('执行remove完毕') |
|
|
|
const aryPoints = []; |
|
|
|
_data.forEach(e=>{ |
|
|
|
this.data.forEach(e=>{ |
|
|
|
if(e.lnglat){ |
|
|
|
let _name = e.config.item.title; |
|
|
|
if(e.config.item.id.indexOf('./路测设备') !== -1){ |
|
|
@ -114,35 +156,292 @@ export class MarkerCluster { |
|
|
|
}) |
|
|
|
} |
|
|
|
}) |
|
|
|
this.dataInfo[_id] = _data; |
|
|
|
this.data.push(..._data); |
|
|
|
if(aryPoints.length > 0){ |
|
|
|
Vue.prototype.mapIns.addPointByArr( |
|
|
|
|
|
|
|
console.log('执行show') |
|
|
|
map2d.addPointByArr( |
|
|
|
{pointList:aryPoints}, // 点位数据数组(按以下规范组装)
|
|
|
|
_id, // jhlayers 当前添加的数据 唯一标识(删除该图层时也会用到)//./事件专题/交通事故.svg
|
|
|
|
'jhlayers', // 当前添加的数据 唯一标识(删除该图层时也会用到)//./事件专题/交通事故.svg
|
|
|
|
true // 当前新加图层是否启用聚合效果
|
|
|
|
); |
|
|
|
console.log(aryPoints,_id,'点位数组') |
|
|
|
await sleep(2000) |
|
|
|
console.log('执行show完毕',aryPoints,'点位数组') |
|
|
|
} |
|
|
|
if(Vue.prototype.isMapStatck){ |
|
|
|
setTimeout(() => { |
|
|
|
Vue.prototype.mapIns.pileIsShow(_id, true); |
|
|
|
}, 500); |
|
|
|
Vue.prototype.mapIns.pileIsShow("jhlayers", true); |
|
|
|
} |
|
|
|
} |
|
|
|
async showInfoWindow(data) { |
|
|
|
const AMap = await loadAMap(); |
|
|
|
|
|
|
|
const map = this.getMap(); |
|
|
|
|
|
|
|
if (!this.infoWindow) |
|
|
|
this.infoWindow = new AMap.InfoWindow({ |
|
|
|
content: "", |
|
|
|
isCustom: true, |
|
|
|
anchor: "bottom-center", |
|
|
|
offset: new AMap.Pixel(0, -60), |
|
|
|
}); |
|
|
|
|
|
|
|
console.log( |
|
|
|
"%c [ this.infoWindow ]-330-「map.js」", |
|
|
|
"font-size:15px; background:#6f5757; color:#b39b9b;", |
|
|
|
this.infoWindow |
|
|
|
); |
|
|
|
//data.length 大于 5 取前5个数, 否则直接返回data
|
|
|
|
const pageSize = 6; |
|
|
|
let page = 1; |
|
|
|
const totalPages = Math.ceil(data.length / pageSize); |
|
|
|
const currentPageData = |
|
|
|
totalPages > 1 ? data.slice((page - 1) * pageSize, pageSize) : data; |
|
|
|
window.currentPageData = currentPageData; |
|
|
|
window.pageSize = pageSize; |
|
|
|
window.page = page; |
|
|
|
window.totalPages = totalPages; |
|
|
|
let num = (page - 1) * pageSize; |
|
|
|
function renderData(page) { |
|
|
|
if (!data || page < 1 || page > totalPages) return; |
|
|
|
window.page = page; |
|
|
|
const pageSize = window.pageSize; |
|
|
|
const dataContainer = document.getElementById("dataContainer"); |
|
|
|
const dataPage = document.getElementById("dataPage"); |
|
|
|
// 清空数据容器
|
|
|
|
// dataContainer.innerHTML = "";
|
|
|
|
// 计算当前页起始索引和结束索引
|
|
|
|
const startIndex = (page - 1) * pageSize; |
|
|
|
const endIndex = startIndex + pageSize; |
|
|
|
let num = (page - 1) * pageSize; |
|
|
|
if (data.length === 0 || data.length === 1) { |
|
|
|
return this.infoWindow.close(); |
|
|
|
} |
|
|
|
const { latitude: lat, longitude: lng } = data[0].extData; |
|
|
|
const lngLatStr = `${parseFloat(lng)}/${parseFloat(lat)}`; //更新清除节点后的数据
|
|
|
|
data = lngLatMap[lngLatStr]; |
|
|
|
// 截取当前页的数据
|
|
|
|
const currentPageData = data.slice(startIndex, endIndex); |
|
|
|
window.currentPageData = currentPageData; |
|
|
|
const itemsTpl = currentPageData |
|
|
|
.map((item) => { |
|
|
|
return ` |
|
|
|
<div style="cursor: pointer; padding: 3px 6px;display: flex;align-items: center; gap: 6px;" class="info-window-item"> |
|
|
|
${++num}<img style="width: 18px;" src="${getIcon(item)}" /> |
|
|
|
<span style="white-space: nowrap; word-break: break-all;">${( |
|
|
|
item.extData.deviceName || |
|
|
|
item.extData.warningTitle || |
|
|
|
item.config?.item.title |
|
|
|
).replace("G35 ", "")}</span> |
|
|
|
</div> |
|
|
|
`;
|
|
|
|
}) |
|
|
|
.join(""); |
|
|
|
// 渲染当前页数据
|
|
|
|
dataContainer.innerHTML = itemsTpl; |
|
|
|
dataPage && (dataPage.innerHTML = `第${page}页`); |
|
|
|
bindItemClick(); |
|
|
|
} |
|
|
|
|
|
|
|
window.renderData = renderData; |
|
|
|
|
|
|
|
// min-width: 240px;
|
|
|
|
// min-height: 90px;
|
|
|
|
// width: 90px;
|
|
|
|
this.infoWindow.setContent(`<div
|
|
|
|
style=" |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
position: relative; |
|
|
|
background: rgba(6,66,88,0.8); |
|
|
|
border: 1px solid rgba(42,217,253,0.6); |
|
|
|
"> |
|
|
|
<div style="height: 26px; width: 100%; display: flex;align-items: center; justify-content: space-between; padding: 0 15px; background: linear-gradient(90deg, #237E9B 0%, rgba(23,145,184,0) 100%);"> |
|
|
|
<span>重叠</span> |
|
|
|
<img class="info-close" style="width: 12px;cursor: pointer;" src="${require("@screen/images/dialog/icon-close.svg")}" /> |
|
|
|
</div> |
|
|
|
<div id="dataContainer" style="padding: 15px 9px;flex: 1; overflow: auto;" class="info-window-content"> |
|
|
|
${currentPageData |
|
|
|
.map( |
|
|
|
(item) => ` |
|
|
|
<div style="cursor: pointer; padding: 3px 6px;display: flex;align-items: center; gap: 6px;" class="info-window-item"> |
|
|
|
${++num} <img style="width: 18px;" src="${getIcon(item)}" /> |
|
|
|
<span style="white-space: nowrap; word-break: break-all;">${( |
|
|
|
item.extData.deviceName || |
|
|
|
item.extData.warningTitle || |
|
|
|
item.config?.item.title |
|
|
|
).replace("G35 ", "")}</span> |
|
|
|
</div> |
|
|
|
` |
|
|
|
) |
|
|
|
.join("")} |
|
|
|
</div> |
|
|
|
${ |
|
|
|
totalPages > 1 |
|
|
|
? ` |
|
|
|
<div id="paginationContainer" style="text-align:center;margin-bottom:5px;"> |
|
|
|
<button style="padding: 0 15px;background: #00B3CC;border-radius: 48px;cursor: pointer;opacity: 1;gap: 6px;font-size: 14px;font-weight: 500;color: #FFFFFF;border: 1px solid rgba(42, 217, 253, 0.6);" onclick="renderData(window.page-1)">上一页</button> |
|
|
|
<span id="dataPage" style="font-size: 14px;font-weight: 500;color: #FFFFFF;"> 第${page} 页</span> |
|
|
|
<button style="padding: 0 15px;background: #00B3CC;border-radius: 48px;cursor: pointer;opacity: 1;gap: 6px;font-size: 14px;font-weight: 500;color: #FFFFFF;border: 1px solid rgba(42, 217, 253, 0.6);" onclick="renderData(window.page+1)">下一页</button> |
|
|
|
</div>` |
|
|
|
: "" |
|
|
|
} |
|
|
|
</div>`); |
|
|
|
window.infoWindow = this.infoWindow; |
|
|
|
this.infoWindow.open(map, currentPageData[0].lnglat); |
|
|
|
window.openInfoWindow = true; |
|
|
|
this.infoWindow.dom.querySelector(".info-close").onclick = () => |
|
|
|
this.infoWindow.close(); |
|
|
|
|
|
|
|
this.infoWindow.dom.querySelector(".info-window-content").onmousewheel = ( |
|
|
|
e |
|
|
|
) => e.stopPropagation(); |
|
|
|
|
|
|
|
this.infoWindow.dom.querySelector(".info-window-content").onwheel = (e) => |
|
|
|
e.stopPropagation(); |
|
|
|
|
|
|
|
function bindItemClick() { |
|
|
|
window.infoWindow.dom |
|
|
|
.querySelectorAll(".info-window-item") |
|
|
|
.forEach((item, index) => { |
|
|
|
item.onclick = () => { |
|
|
|
const currentPageData = window.currentPageData; |
|
|
|
currentPageData[index].config.markerClick?.( |
|
|
|
currentPageData[index].extData, |
|
|
|
currentPageData[index].config?.item |
|
|
|
); |
|
|
|
}; |
|
|
|
}); |
|
|
|
} |
|
|
|
bindItemClick(); |
|
|
|
} |
|
|
|
removeData(_id, _data) { |
|
|
|
if (!_data) return; |
|
|
|
if (!Array.isArray(_data)) _data = [_data]; |
|
|
|
const _vpmIndex = Vue.prototype.mapInsOpenLayers.findIndex(_id); |
|
|
|
if(_vpmIndex !== -1){ |
|
|
|
Vue.prototype.mapInsOpenLayers.splice(_vpmIndex) |
|
|
|
|
|
|
|
async setMarkerCluster() { |
|
|
|
const AMap = await loadAMap(); |
|
|
|
|
|
|
|
const map = this.getMap(); |
|
|
|
|
|
|
|
let hasClick = false; |
|
|
|
|
|
|
|
const reset = () => { |
|
|
|
if (!window.openInfoWindow) { |
|
|
|
// console.log("窗口movestart中", new Date());
|
|
|
|
this.infoWindow?.close?.(); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const _dataTemp = []; |
|
|
|
if(this.dataInfo[_id]){ |
|
|
|
_dataTemp = this.dataInfo[_id] |
|
|
|
map.on("zoomstart", reset); |
|
|
|
map.on("zoomend", reset); |
|
|
|
map.on("movestart", reset); |
|
|
|
map.on("moveend", async () => { |
|
|
|
if (window.openInfoWindow) { |
|
|
|
// console.log("窗口位移结束", new Date());
|
|
|
|
window.openInfoWindow = false; |
|
|
|
// !window.infoWindow.getIsOpen() && window.infoWindow?.open?.();
|
|
|
|
} |
|
|
|
_data.forEach((item, index) => { |
|
|
|
}); |
|
|
|
|
|
|
|
const markerCluster = new AMap.MarkerCluster(map, [], { |
|
|
|
// gridSize: 15,
|
|
|
|
maxZoom: 15, |
|
|
|
// 自定义聚合点样式
|
|
|
|
renderClusterMarker(context) { |
|
|
|
// 聚合中点个数
|
|
|
|
const clusterCount = context.count; |
|
|
|
const div = document.createElement("div"); |
|
|
|
let bgColor = "204,235,197"; |
|
|
|
|
|
|
|
div.style.backgroundColor = `rgba(${bgColor}, .6)`; |
|
|
|
|
|
|
|
const size = Math.round(24 + `${clusterCount}`.length * 15); |
|
|
|
|
|
|
|
div.style.borderRadius = |
|
|
|
div.style.lineHeight = |
|
|
|
div.style.width = |
|
|
|
div.style.height = |
|
|
|
`${size}px`; |
|
|
|
div.style.border = `solid 1px rgba(${bgColor}, 1)`; |
|
|
|
div.innerHTML = context.count; |
|
|
|
div.style.color = "#ffffff"; |
|
|
|
div.style.fontSize = "24px"; |
|
|
|
div.style.textAlign = "center"; |
|
|
|
|
|
|
|
context.marker.setOffset(new AMap.Pixel(-size / 2, -size / 2)); |
|
|
|
context.marker.setContent(div); |
|
|
|
}, |
|
|
|
renderMarker: (context) => { |
|
|
|
const markerData = context.data[0]; |
|
|
|
const { |
|
|
|
extData, |
|
|
|
lnglat: { lat, lng }, |
|
|
|
} = markerData; |
|
|
|
const lngLatStr = `${lng}/${lat}`; |
|
|
|
const data = lngLatMap[lngLatStr]; |
|
|
|
if (data) { |
|
|
|
const state = getState(data); |
|
|
|
let nowTitleBg, nowTitleColor; |
|
|
|
if (state) { |
|
|
|
nowTitleBg = normalTitleBg; |
|
|
|
nowTitleColor = "#FFDB82"; |
|
|
|
} else { |
|
|
|
nowTitleBg = faultTitleBg; |
|
|
|
nowTitleColor = "#FF7575"; |
|
|
|
} |
|
|
|
const item = data[0]; |
|
|
|
const { stakeMark } = item.extData; |
|
|
|
const titleTemplate = window.showStakeText |
|
|
|
? `<p style="background-image: url('${nowTitleBg}');background-size: 100% 100%;background-repeat: no-repeat;padding: 5px;
|
|
|
|
font-family: PingFang SC, PingFang SC;font-weight: 400;font-size: 14px; line-height: 16px; color: ${nowTitleColor}; |
|
|
|
"> ${stakeMark} </p>` |
|
|
|
: ""; |
|
|
|
|
|
|
|
context.marker.setAnchor("bottom-center"); |
|
|
|
|
|
|
|
context.marker.setLabel({ |
|
|
|
direction: "bottom", |
|
|
|
// offset: new AMap.Pixel(10, 0), //设置文本标注偏移量
|
|
|
|
content: titleTemplate, //设置文本标注内容
|
|
|
|
}); |
|
|
|
|
|
|
|
context.marker.setContent(getContent(data)); |
|
|
|
// if (window.showStakeText) context.marker.setAnchor("center");
|
|
|
|
// else context.marker.setAnchor("bottom-center");
|
|
|
|
const offset = new AMap.Pixel(0, 0); |
|
|
|
context.marker.setOffset(offset); |
|
|
|
|
|
|
|
context.marker.setExtData(extData); |
|
|
|
|
|
|
|
context.marker.on("click", (e) => { |
|
|
|
hasClick = true; |
|
|
|
|
|
|
|
const data = lngLatMap[lngLatStr]; |
|
|
|
if (data.length > 1) { |
|
|
|
this.showInfoWindow(data); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
console.log( |
|
|
|
"%c [ 点击地图坐标 ]-302-「map.js」", |
|
|
|
"font-size:15px; background:#8f8c0b; color:#d3d04f;", |
|
|
|
data[0] |
|
|
|
); |
|
|
|
|
|
|
|
data[0].config.markerClick?.(data[0].extData, data[0].config?.item); |
|
|
|
}); |
|
|
|
} |
|
|
|
}, |
|
|
|
}); |
|
|
|
|
|
|
|
markerCluster.on("click", (e) => { |
|
|
|
if (hasClick) return (hasClick = false); |
|
|
|
|
|
|
|
map.setCenter(e.lnglat); |
|
|
|
map.setZoom(map.getZoom() + 3); |
|
|
|
}); |
|
|
|
|
|
|
|
this.markerCluster = markerCluster; |
|
|
|
} |
|
|
|
|
|
|
|
async removeData(data) { |
|
|
|
data.forEach((item, index) => { |
|
|
|
const findIndex = this.data.findIndex( |
|
|
|
(removeData) => removeData === item |
|
|
|
); |
|
|
@ -168,30 +467,83 @@ export class MarkerCluster { |
|
|
|
} |
|
|
|
} |
|
|
|
if (findIndex > -1) this.data.splice(findIndex, 1); |
|
|
|
|
|
|
|
const _findIndex = _dataTemp.findIndex( |
|
|
|
(removeData) => removeData === item |
|
|
|
); |
|
|
|
if (_findIndex > -1) { |
|
|
|
_dataTemp.splice(_findIndex, 1); |
|
|
|
} |
|
|
|
}); |
|
|
|
console.log('remove',_id,_data) |
|
|
|
Vue.prototype.mapIns.removeLayerByName(_id); |
|
|
|
if(_dataTemp.length > 0){ |
|
|
|
const self = this; |
|
|
|
setTimeout(() => { |
|
|
|
self.refreshLayer(_id, _dataTemp); |
|
|
|
}, 500); |
|
|
|
await this.setData() |
|
|
|
} |
|
|
|
|
|
|
|
async setData() { |
|
|
|
if(this.mapContainer === 'BMapContainer'){ |
|
|
|
await this.refreshLayer(); |
|
|
|
} else { |
|
|
|
this.markerCluster.setData(this.data); //高德地图
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
clear() { |
|
|
|
this.data = []; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
export function getContent(data) { |
|
|
|
// const faultBg = require(`@screen/images/mapBg/fault.svg`);
|
|
|
|
// const normalBg = require(`@screen/images/mapBg/active.svg`);
|
|
|
|
const state = getState(data); |
|
|
|
let nowBg, nowTitleBg, nowTitleColor; |
|
|
|
if (state) { |
|
|
|
nowBg = normalBg; |
|
|
|
nowTitleBg = normalTitleBg; |
|
|
|
nowTitleColor = "#FFDB82"; |
|
|
|
} else { |
|
|
|
nowBg = faultBg; |
|
|
|
nowTitleBg = faultTitleBg; |
|
|
|
nowTitleColor = "#FF7575"; |
|
|
|
} |
|
|
|
|
|
|
|
const item = data[0]; |
|
|
|
const { stakeMark } = item.extData; |
|
|
|
if (data.length === 1) { |
|
|
|
return ` |
|
|
|
<div style=" |
|
|
|
background-image: url(${nowBg}); |
|
|
|
background-size: 100% 100%; |
|
|
|
background-repeat: no-repeat; |
|
|
|
width: 51px; |
|
|
|
height: 51px; |
|
|
|
display: flex; |
|
|
|
align-self: center; |
|
|
|
justify-content: center; |
|
|
|
"> |
|
|
|
<img style=" |
|
|
|
min-width: 24px; |
|
|
|
min-height: 24px; |
|
|
|
width: 24px; |
|
|
|
height: 24px; |
|
|
|
margin-top: 8.1px; |
|
|
|
" src='${getIcon(item)}' |
|
|
|
> |
|
|
|
</div> |
|
|
|
`;
|
|
|
|
} else { |
|
|
|
const width = `${36 + `${data.length}`.length * 15}px`; |
|
|
|
|
|
|
|
return ` |
|
|
|
<div style=" |
|
|
|
background-image: url(${nowBg}); |
|
|
|
background-size: 100% 100%; |
|
|
|
background-repeat: no-repeat; |
|
|
|
width: ${width}; |
|
|
|
height: ${width}; |
|
|
|
display: flex; |
|
|
|
justify-content: center; |
|
|
|
align-items: center; |
|
|
|
padding-bottom: 9px; |
|
|
|
align-self: center; |
|
|
|
" onmouseover="this.parentNode.style.zIndex=13;" onmouseout="this.parentNode.style.zIndex=12;"> |
|
|
|
${data.length} |
|
|
|
</div> |
|
|
|
`;
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function getIcon({ config, extData }, type = '') { |
|
|
|
const normal = require(`@screen/images/layer${type}${config.item.id.replace( |
|
|
|
/^\.|[^/]+(?=.svg$)/g, |
|
|
@ -250,7 +602,6 @@ export function addInGraphHandle(data) { |
|
|
|
54.394) * |
|
|
|
window.canvasRatio + |
|
|
|
window.canvasWidth * window.offsetRatio; //K54+394 开始到K208+979计算的比例尺
|
|
|
|
|
|
|
|
const node = { shape: "custom-html", effect: ["data"] }; |
|
|
|
node.data = getDataConf(data, extData); |
|
|
|
const id = `${stakeMark}_${direction || ""}`; |
|
|
|