Browse Source

修改首页地图重复点问题

wangqin
Joe 10 months ago
parent
commit
0523f33a38
  1. 3
      ruoyi-ui/src/views/JiHeExpressway/components/Video/videoStream.js
  2. 3
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/AMapContainer/index.vue
  3. 130
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/buttonEvent.js
  4. 449
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/map.js
  5. 7
      ruoyi-ui/src/views/JiHeExpressway/scss/reset.scss

3
ruoyi-ui/src/views/JiHeExpressway/components/Video/videoStream.js

@ -238,7 +238,8 @@ export async function openLiveVideo(container, options) {
console.log(
"%c [ url ]-212-「videoStream.js」",
"font-size:15px; background:#6f87e8; color:#b3cbff;",
url
url,
options
);
if (!url) return;

3
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/AMapContainer/index.vue

@ -7,6 +7,7 @@
<script lang="ts">
import { onceObserver } from "@screen/utils/resizeObserver";
import { loadAMap } from "./loadAMap";
import Vue from "vue";
const acData = require("./data/ac.json");
const lcz = require("./data/lcz.json");
@ -50,6 +51,8 @@ export default {
center: [116.629514, 35.805288],
});
Vue.prototype.mapIns = this.aMapIns;
this.aMapIns.on("complete", () => {
this.loading = false;

130
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/buttonEvent.js

@ -7,7 +7,7 @@ import { delay } from "@screen/utils/common";
import { Message } from "element-ui";
import { EventTopics } from "@screen/utils/enum.js";
import { setMarkToMap } from "./map";
import { setMarkToMap, markerClusterIns } from "./map";
const cameraIcon = {
// 球机
@ -27,7 +27,7 @@ export const DeviceForMap = {
* 0 球机 可控
* 1 枪机 不可控
*/
const type = JSON.parse(item.otherConfig)?.ptzCtrl;
const type = JSON.parse(item.otherConfig || "{}")?.ptzCtrl || 1;
return cameraIcon[`${type}${+!bool}`];
},
},
@ -51,6 +51,28 @@ export function getHandleDeviceType(item) {
if (EventTopics[item.title]) return "地图事件专题/map";
}
function resolveDataOptions(data, config, component) {
return {
weight: 1,
lnglat: [data.longitude, data.latitude],
name: "",
config: {
markerClick: (extData, item) => {
this.dialogConfig = {
component,
data: {
...extData,
parseOtherConfig: JSON.parse(extData.otherConfig || "{}"),
_itemData: item,
},
};
},
...config,
},
extData: data,
};
}
export const eventMap = {
// 设备 需要在地图显示的
async "地图路测设备/map"(item) {
@ -68,37 +90,28 @@ export const eventMap = {
await delay(600);
return data;
})
.catch(() => {})
.finally(() => loadingMessage.close());
.catch(() => {});
if (!data) return;
if (!data.length) return Message.warning(`没有${item.title}事件数据!`);
eventMap[`地图路测设备/map_close`](item);
cacheRemoveFunc[`地图路测设备/${item.title}`] = await setMarkToMap.call(
this,
const options = {
item,
data,
(extData) => {
console.log(
"%c [ extData ]-85-「buttonEvent.js」",
"font-size:15px; background:#98099a; color:#dc4dde;",
{
...extData,
parseOtherConfig: JSON.parse(extData.otherConfig || "{}"),
}
);
this.dialogConfig = {
component: config.dialog,
data: {
...extData,
parseOtherConfig: JSON.parse(extData.otherConfig || "{}"),
},
};
},
config.options
...config.options,
};
let removeData = data.map((item) =>
resolveDataOptions.call(this, item, options, config.dialog)
);
markerClusterIns.addData(removeData);
loadingMessage.close();
cacheRemoveFunc[`地图路测设备/${item.title}`] = () =>
markerClusterIns.removeData(removeData);
},
"地图路测设备/map_close"(item) {
cacheRemoveFunc[`地图路测设备/${item.title}`]?.();
@ -118,8 +131,7 @@ export const eventMap = {
await delay(600);
return data;
})
.catch(() => {})
.finally(() => loadingMessage.close());
.catch(() => {});
if (!data) return;
@ -127,25 +139,21 @@ export const eventMap = {
eventMap[`地图事件专题/map_close`](item);
cacheRemoveFunc[`地图事件专题/${item.title}`] = await setMarkToMap.call(
this,
const options = {
stateCallback: () => true,
item,
data,
(extData) => {
this.dialogConfig = {
component: "TrafficIncidents",
data: {
...extData,
parseOtherConfig: JSON.parse(extData.otherConfig || "{}"),
_itemData: item,
},
};
},
{
stateCallback: () => true,
}
// config.options
};
let removeData = data.map((item) =>
resolveDataOptions.call(this, item, options, "TrafficIncidents")
);
markerClusterIns.addData(removeData);
loadingMessage.close();
cacheRemoveFunc[`地图事件专题/${item.title}`] = () =>
markerClusterIns.removeData(removeData);
},
"地图事件专题/map_close"(item) {
cacheRemoveFunc[`地图事件专题/${item.title}`]?.();
@ -165,8 +173,7 @@ export const eventMap = {
await delay(600);
return data;
})
.catch(() => {})
.finally(() => loadingMessage.close());
.catch(() => {});
if (!data) return;
@ -174,24 +181,23 @@ export const eventMap = {
eventMap[`事件专题/感知事件_close`](item);
cacheRemoveFunc[`事件专题/${item.title}`] = await setMarkToMap.call(
this,
loadingMessage.close();
const options = {
stateCallback: () => true,
item,
data,
(extData) => {
this.dialogConfig = {
component: "PerceiveEvent",
data: {
...extData,
parseOtherConfig: JSON.parse(extData.otherConfig || "{}"),
},
};
},
{
stateCallback: () => true,
}
// config.options
};
let removeData = data.map((item) =>
resolveDataOptions.call(this, item, options, "PerceiveEvent")
);
markerClusterIns.addData(removeData);
loadingMessage.close();
cacheRemoveFunc[`事件专题/${item.title}`] = () =>
markerClusterIns.removeData(removeData);
},
"事件专题/感知事件_close"(item) {
cacheRemoveFunc[`事件专题/${item.title}`]?.();

449
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/map.js

@ -1,6 +1,6 @@
import { loadAMap } from "@screen/pages/Home/components/AMapContainer/loadAMap.js";
import { Message } from "element-ui";
// import { Message } from "element-ui";
import Vue from "vue";
/**
* @typedef {Object} Point
* @property {number} weight - The weight of the item.
@ -20,163 +20,322 @@ import { Message } from "element-ui";
* @param {Function} markerFun
* @returns
*/
export async function setMarkerCluster(map, points, markerFun) {
const AMap = await loadAMap();
let hasClick = false;
/**
* 聚合点
*/
export class MarkerCluster {
/**
* @type {}
*/
map;
markerCluster;
infoWindow;
// if (!points.length) return Message.warning("未能匹配到对应坐标点");
if (!points.length) return [];
lngLatMap = {};
const markerCluster = new AMap.MarkerCluster(map, points, {
// gridSize: 15,
maxZoom: 15,
// 自定义聚合点样式
renderClusterMarker(context) {
// 聚合中点个数
const clusterCount = context.count;
const div = document.createElement("div");
let bgColor = "204,235,197";
data = [];
div.style.backgroundColor = `rgba(${bgColor}, .6)`;
constructor() {}
const size = Math.round(
25 + Math.pow(clusterCount / points.length, 1 / 5) * 36
);
getMap() {
if (this.map) return this.map;
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 { content, extData } = context.data[0];
const offset = new AMap.Pixel(0, 0);
context.marker.setContent(content);
context.marker.setAnchor("bottom-center");
// console.log(
// "%c [ context.marker ]-62-「map.js」",
// "font-size:15px; background:#7d477f; color:#c18bc3;",
// context.marker
// );
context.marker.setOffset(offset);
context.marker.setExtData(extData);
context.marker.on("click", (e) => {
hasClick = true;
markerFun(e);
});
},
});
return (this.map = Vue.prototype.mapIns);
}
markerCluster.on("click", (e) => {
if (hasClick) return (hasClick = false);
async addData(data) {
this.infoWindow?.close?.();
map.setCenter(e.lnglat);
map.setZoom(map.getZoom() + 3);
});
if (!data) return;
if (!Array.isArray(data)) data = [data];
return markerCluster;
}
this.data.push(...data);
/**
*
* @param {*} item
* @param {*} resolveMarker 处理数据 返回 { lnglat: [lng, lat], content: '' }
* @param {*} _markerClick marker 点击
* @returns
*/
export async function setMarkToMap(
item,
data,
_markerClick,
{ iconCallback, content, stateCallback } = {}
) {
const { mapIns } = this.getMap();
if (!mapIns) return Message.error("地图加载失败!");
const normal =
normal ||
require(`@screen/images/layer${item.id.replace(
if (!this.markerCluster) await this.setMarkerCluster();
const map = this.getMap();
console.log(
"%c [ data ]-227-「map.js」",
"font-size:15px; background:#641f14; color:#a86358;",
data
);
this.markerCluster.addData(data);
console.log(
"%c [ this.markerCluster ]-234-「map.js」",
"font-size:15px; background:#fe94d3; color:#ffd8ff;",
this.markerCluster
);
map.setZoom(10);
setTimeout(() => {
map.setFitView([...this.markerCluster.U], false, [0, 0, 0, 0], 10);
}, 150);
}
getState({ config, extData }) {
return typeof config.stateCallback === "function"
? config.stateCallback?.()
: extData.deviceState == 1;
}
getIcon({ config, extData }) {
const normal = require(`@screen/images/layer${config.item.id.replace(
/^\.|[^/]+(?=.svg$)/g,
(data) => (data === "." ? "" : `${data}_active`)
(str) => (str === "." ? "" : `${str}_active`)
)}`);
const fault =
fault ||
require(`@screen/images/layer${item.id.replace(
const fault = require(`@screen/images/layer${config.item.id.replace(
/^\.|[^/]+(?=.svg$)/g,
(data) => (data === "." ? `` : `${data}_fault`)
(str) => (str === "." ? `` : `${str}_fault`)
)}`);
const faultBg = require(`@screen/images/mapBg/fault.svg`);
const normalBg = require(`@screen/images/mapBg/active.svg`);
const markerClick = (e) => {
const extData = e.target.getExtData();
_markerClick(extData, e);
};
const markerCluster = await setMarkerCluster(
mapIns,
data.map((item) => {
const currentState =
typeof stateCallback === "function"
? stateCallback(item)
: item.deviceState == 1;
const deviceIcon =
typeof iconCallback === "function" && iconCallback(currentState, item);
return {
weight: 1,
lnglat: [item.longitude, item.latitude],
name: "",
extData: item,
content:
content ||
`<div style="
background-image: url(${currentState ? normalBg : faultBg});
background-size: 100% 100%;
background-repeat: no-repeat;
width: 51px;
height: 51px;
display: flex;
justify-content: center;
">
<img style="
min-width: 24px;
min-height: 24px;
width: 24px;
height: 24px;
margin-top: 8.1px;
" src='${deviceIcon ? deviceIcon : currentState ? normal : fault}'
>
</div>`,
};
}),
markerClick
);
mapIns.setZoom(10);
setTimeout(() => {
mapIns.setFitView([...markerCluster.U], false, [0, 0, 0, 0], 10);
}, 150);
return () => markerCluster.setMap(null);
const currentState = this.getState({ config, extData });
const deviceIcon =
typeof config.iconCallback === "function" &&
config.iconCallback(currentState, config.item);
return deviceIcon ? deviceIcon : currentState ? normal : fault;
}
getContent(data) {
const faultBg = require(`@screen/images/mapBg/fault.svg`);
const normalBg = require(`@screen/images/mapBg/active.svg`);
if (data.length === 1) {
return `
<div style="
background-image: url(${this.getState(data[0]) ? normalBg : faultBg});
background-size: 100% 100%;
background-repeat: no-repeat;
width: 51px;
height: 51px;
display: flex;
justify-content: center;
">
<img style="
min-width: 24px;
min-height: 24px;
width: 24px;
height: 24px;
margin-top: 8.1px;
" src='${this.getIcon(data[0])}'
>
</div>
`;
} else {
const width = `${36 + `${data.length}`.length * 15}px`;
return `
<div style="
background-image: url(${normalBg});
background-size: 100% 100%;
background-repeat: no-repeat;
width: ${width};
height: ${width};
display: flex;
justify-content: center;
align-items: center;
padding-bottom: 9px;
">
${data.length}
</div>
`;
}
}
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
);
this.infoWindow.setContent(`<div
style="
min-width: 240px;
min-height: 150px;
width: 90px;
height: 90px;
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 style="padding: 15px 9px;flex: 1; overflow: auto;" class="info-window-content">
${data
.map(
(item) => `
<div style="cursor: pointer; padding: 3px 6px;display: flex;align-items: center; gap: 6px;" class="info-window-item">
<img style="width: 18px;" src="${this.getIcon(item)}" />
<span>${
item.extData.deviceName ||
item.extData.warningTitle ||
item.config?.item.title
}</span>
</div>
`
)
.join("")}
</div>
</div>`);
this.infoWindow.open(map, data[0].lnglat);
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();
this.infoWindow.dom
.querySelectorAll(".info-window-item")
.forEach((item, index) => {
item.onclick = () =>
data[index].config.markerClick?.(
data[index].extData,
data[index].config?.item
);
});
}
async setMarkerCluster() {
const AMap = await loadAMap();
const map = this.getMap();
let hasClick = false;
const reset = () => {
this.infoWindow?.close?.();
};
map.on("zoomstart", reset);
map.on("mapmove", reset);
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 {
extData,
lnglat: { lat, lng },
} = context.data[0];
const lngLatStr = `${lng}/${lat}`;
if (this.lngLatMap[lngLatStr])
!this.lngLatMap[lngLatStr].includes(context.data[0]) &&
this.lngLatMap[lngLatStr].push(context.data[0]);
else this.lngLatMap[lngLatStr] = [context.data[0]];
context.marker.setContent(this.getContent(this.lngLatMap[lngLatStr]));
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 = this.lngLatMap[lngLatStr];
if (data.length > 1) {
this.showInfoWindow(data);
return;
}
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;
}
removeData(data) {
data.forEach((item, index) => {
const findIndex = this.data.findIndex(
(removeData) => removeData === item
);
const lngLatStr = item.lnglat.join("/");
if (this.lngLatMap[lngLatStr]) {
if (this.lngLatMap[lngLatStr].length < 2)
delete this.lngLatMap[lngLatStr];
else {
const findIndex = this.lngLatMap[lngLatStr].findIndex(
(removeData) => removeData === item
);
this.lngLatMap[lngLatStr].splice(findIndex, 1);
}
}
if (findIndex > -1) this.data.splice(findIndex, 1);
});
this.markerCluster.setData(this.data);
}
}
export const markerClusterIns = new MarkerCluster();

7
ruoyi-ui/src/views/JiHeExpressway/scss/reset.scss

@ -35,4 +35,11 @@ body {
display: none;
}
}
// 地图弹窗
.info-window-item {
&:hover {
background-color: rgba(13, 95, 121, 0.6);
}
}
}

Loading…
Cancel
Save