Browse Source

Merge branch 'develop' of http://39.106.31.193:9211/mengff/jihe-hs into develop

wangqin
刘朋 9 months ago
parent
commit
9e5c1ed5eb
  1. 3
      ruoyi-ui/package.json
  2. 39
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/HomeFrame/index.vue
  3. 12
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/buttonEvent.js
  4. 200
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/RoadAndEvents/utils/map.js
  5. 331
      ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Thumbnail/index.vue
  6. 5
      ruoyi-ui/vue.config.js

3
ruoyi-ui/package.json

@ -38,6 +38,7 @@
},
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"@antv/x6": "^2.18.1",
"@jiaminghi/data-view": "^2.10.0",
"@riophae/vue-treeselect": "0.4.0",
"@vue/composition-api": "^1.7.1",
@ -152,4 +153,4 @@
"> 1%",
"last 2 versions"
]
}
}

39
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/HomeFrame/index.vue

@ -1,22 +1,17 @@
<template>
<div class="HomeFrame">
<ElPopover
trigger="manual"
:value="activeIcon === 'Frame'"
:visibleArrow="false"
placement="left"
popper-class="global-input-search-popover"
>
<Button
:class="['btn', { 'btn-active': activeIcon }]"
slot="reference"
@click.native="handleClick('Frame')"
>
<ElPopover trigger="manual" :value="activeIcon === 'Frame'" :visibleArrow="false" placement="left"
popper-class="global-input-search-popover">
<Button :class="['btn', { 'btn-active': activeIcon }]" slot="reference" @click.native="handleClick('Frame')">
<img src="@screen/images/home-Frame/Frame.svg" />
</Button>
<div class="body">
<div class="title">图标含义</div>
<span class="close" @click="() => { this.activeIcon = null; }">
<i class="el-icon-close" />
</span>
<!-- <Form labelWidth="90px" column="1" class="form" ref="FormConfigRef" :formList="formList" /> -->
<img class="image" src="@screen/images/home-Frame/logoMean.png" />
<!-- <div class="footer">
@ -67,23 +62,31 @@ div.el-popper.global-input-search-popover {
.body {
.title {
background: linear-gradient(
90deg,
#237e9b 0%,
rgba(23, 145, 184, 0) 100%
);
background: linear-gradient(90deg,
#237e9b 0%,
rgba(23, 145, 184, 0) 100%);
padding: 3px 9px;
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.close {
padding: 3px 9px;
cursor: pointer;
position: absolute;
top: 0;
right: 0;
width: fit-content;
}
}
}
</style>
<style lang="scss" scoped>
.image {
width: 60vw;
width: 50vw;
height: 65vh;
}

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

@ -9,8 +9,8 @@ import { EventTopics } from "@screen/utils/enum.js";
import { debounce } from "lodash";
import Vue from "vue";
import { markerClusterIns } from "./map";
const canvasCtx = Vue.prototype.canvasCtx;
import { markerClusterIns, getContent } from "./map";
const graphInstance = Vue.prototype.graphInstance;
const cameraIcon = {
// 球机
@ -287,11 +287,19 @@ function lngLatMapHandle(markers, cb) {
}
function addDataPreHandle(markers) {
// console.log(markers, graphInstance, "addData");
const cb = (lngLatStr, markerData) => {
if (lngLatMap[lngLatStr])
!lngLatMap[lngLatStr].includes(markerData) &&
lngLatMap[lngLatStr].push(markerData);
else lngLatMap[lngLatStr] = [markerData];
// console.log(
// getContent(lngLatMap[lngLatStr]),
// lngLatMap[lngLatStr],
// lngLatStr,
// "marker div"
// );
};
lngLatMapHandle(markers, cb);
}

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

@ -77,84 +77,6 @@ export class MarkerCluster {
}, 150);
}
getState(data) {
if (Array.isArray(data)) {
return data.every((item) => this.getStateSingle(item));
} else return this.getStateSingle(data);
}
getStateSingle({ 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,
(str) => (str === "." ? "" : `${str}_active`)
)}`);
const fault = require(`@screen/images/layer${config.item.id.replace(
/^\.|[^/]+(?=.svg$)/g,
(str) => (str === "." ? `` : `${str}_fault`)
)}`);
const currentState = this.getState({ config, extData });
const deviceIcon =
typeof config.iconCallback === "function" &&
config.iconCallback(currentState, extData, 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`);
const nowBg = this.getState(data) ? normalBg : faultBg;
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;
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(${nowBg});
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();
@ -194,7 +116,7 @@ export class MarkerCluster {
.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)}" />
<img style="width: 18px;" src="${getIcon(item)}" />
<span>${
item.extData.deviceName ||
item.extData.warningTitle ||
@ -280,32 +202,34 @@ export class MarkerCluster {
} = markerData;
const lngLatStr = `${lng}/${lat}`;
context.marker.setContent(this.getContent(lngLatMap[lngLatStr]));
if (lngLatMap[lngLatStr]) {
context.marker.setContent(getContent(lngLatMap[lngLatStr]));
context.marker.setAnchor("bottom-center");
context.marker.setAnchor("bottom-center");
const offset = new AMap.Pixel(0, 0);
context.marker.setOffset(offset);
const offset = new AMap.Pixel(0, 0);
context.marker.setOffset(offset);
context.marker.setExtData(extData);
context.marker.setExtData(extData);
context.marker.on("click", (e) => {
hasClick = true;
context.marker.on("click", (e) => {
hasClick = true;
const data = lngLatMap[lngLatStr];
if (data.length > 1) {
this.showInfoWindow(data);
return;
}
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]
);
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);
});
data[0].config.markerClick?.(data[0].extData, data[0].config?.item);
});
}
},
});
@ -332,4 +256,84 @@ export class MarkerCluster {
}
}
export function getContent(data) {
const faultBg = require(`@screen/images/mapBg/fault.svg`);
const normalBg = require(`@screen/images/mapBg/active.svg`);
const nowBg = getState(data) ? normalBg : faultBg;
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;
justify-content: center;
">
<img style="
min-width: 24px;
min-height: 24px;
width: 24px;
height: 24px;
margin-top: 8.1px;
" src='${getIcon(data[0])}'
>
</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;
">
${data.length}
</div>
`;
}
}
function getIcon({ config, extData }) {
const normal = require(`@screen/images/layer${config.item.id.replace(
/^\.|[^/]+(?=.svg$)/g,
(str) => (str === "." ? "" : `${str}_active`)
)}`);
const fault = require(`@screen/images/layer${config.item.id.replace(
/^\.|[^/]+(?=.svg$)/g,
(str) => (str === "." ? `` : `${str}_fault`)
)}`);
const currentState = getState({ config, extData });
const deviceIcon =
typeof config.iconCallback === "function" &&
config.iconCallback(currentState, extData, config.item);
return deviceIcon ? deviceIcon : currentState ? normal : fault;
}
function getState(data) {
console.log(data, Array.isArray(data), "getState");
if (Array.isArray(data)) {
return data.every((item) => getStateSingle(item));
} else return getStateSingle(data);
}
function getStateSingle({ config, extData }) {
console.log(config, "getStateSingle");
return typeof config?.stateCallback === "function"
? config.stateCallback?.()
: extData.deviceState == 1;
}
export const markerClusterIns = new MarkerCluster();

331
ruoyi-ui/src/views/JiHeExpressway/pages/Home/components/Thumbnail/index.vue

@ -3,99 +3,220 @@
</template>
<script>
import { getScaleByActualData } from "./utils"
import { onceObserver } from "@screen/utils/resizeObserver";
import Vue from "vue";
import { Graph } from '@antv/x6'
import { actualLocationList, canvasList } from "./data.js";
function setFont(size, bold = "normal", family = "微软雅黑") {
return `${bold} ${size}px ${family}`;
}
let width, height;
const actualDis = actualLocationList.reduce(
(count, item) => count + item.intervalDistance,
0
);
const rect = {
shape: "rect",
width: 0.1,
attrs: {
"body": {
stroke: "#37B5D4",
strokeWidth: 2,
strokeDasharray: '4, 7',
}
}
}
export default {
name: "Thumbnail",
data() {
return {
list: getScaleByActualData(),
list: canvasList, // *canvasItem.distance
translateX: 0,
translateY: 50,
allInstance: 0
};
},
mounted() {
onceObserver.call(this, this.$refs.ThumbnailRef, () => {
this.$refs.ThumbnailRef.innerHTML = null;
this.mapInit();
})
this.mapInit();
// onceObserver.call(this, this.$refs.ThumbnailRef, () => {
// this.$refs.ThumbnailRef.innerHTML = null;
// this.translateX = 0,
// this.translateY = 50,
// this.allInstance = 0
// this.mapInit();
// });
},
methods: {
async mapInit() {
const content = this.$refs.ThumbnailRef;
width = content.clientWidth;
height = content.clientHeight;
const baseData = {
nodes: [
{
type: "text",
label: "G2001济南绕城高速",
x: 66,
y: height / 2 - 21,
attrs: {
text: {
fontSize: 12,
fill: "#ffffff",
}
}
},
{
type: "text",
label: "G3021德上高速",
x: width - 50,
y: height / 2 - 21,
attrs: {
text: {
fontSize: 12,
fill: "#ffffff",
}
}
},
{
type: "text",
label: "项目起点",
x: 270,
y: height / 2 - 50,
attrs: {
text: {
fontSize: 12,
fontFamily: "Arial Black",
fontWeight: 900,
fontStyle: "italic",
fill: "#ddc85a",
}
}
},
{
type: "text",
label: "K55+378.7",
x: 270,
y: height / 2 - 33,
attrs: {
text: {
fontSize: 12,
fontFamily: "Arial Black",
fontWeight: 900,
fontStyle: "italic",
fill: "#ddc85a",
}
}
},
{
type: "text",
label: "项目终点",
x: width - 220,
y: height / 2 - 50,
attrs: {
text: {
fontSize: 12,
fontFamily: "Arial Black",
fontWeight: 900,
fontStyle: "italic",
fill: "#ddc85a",
}
}
},
{
type: "text",
label: "K208+153.4",
x: width - 220,
y: height / 2 - 33,
attrs: {
text: {
fontSize: 12,
fontFamily: "Arial Black",
fontWeight: 900,
fontStyle: "italic",
fill: "#ddc85a",
}
}
},
],
};
const graph = new Graph({
container: content,
width,
height,
background: {
image: require("./images/bg.png"),
size: "100% 100%"
},
interacting: function (cellView) {
if (true) return { nodeMovable: false }
// return true;
},
});
Vue.prototype.graphInstance = graph;
baseData.nodes.forEach(node => {
graph.addNode(node)
});
const width = content.clientWidth;
const height = content.clientHeight;
const zoom = 3;
const canvas = document.createElement("canvas");
canvas.style.width = width + "px";
canvas.style.height = height + "px";
const w = width * zoom;
const h = height * zoom;
canvas.width = w;
canvas.height = h;
content.appendChild(canvas);
const ctx = canvas.getContext("2d");
Vue.prototype.canvasCtx = ctx;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
var imgbg = await this.loadImage(require("./images/bg.png"));
ctx.drawImage(imgbg, 0, 0, w, h);
ctx.font = setFont(12 * zoom);
ctx.fillStyle = "#ffffff";
ctx.fillText("G2001济南绕城高速", 66 * zoom, h / 2 - 21 * zoom);
ctx.fillText("G3021德上高速", w - 50 * zoom, h / 2 - 21 * zoom);
ctx.font = setFont(12 * zoom, "italic 900", "Arial Black");
ctx.fillStyle = "#ddc85a";
ctx.fillText("项目起点", 270 * zoom, h / 2 - 50 * zoom);
ctx.fillText("K55+378.7", 270 * zoom, h / 2 - 33 * zoom);
ctx.fillText("项目终点", w - 210 * zoom, h / 2 - 50 * zoom);
ctx.fillText("K208+153.4", w - 210 * zoom, h / 2 - 33 * zoom);
ctx.font = setFont(12 * zoom);
//
ctx.translate(-25 * zoom, 208 * zoom);
this.drawCongestionAreas(ctx, 1404, 80, zoom)
// this.drawCongestionAreas(ctx, 1365, 150, zoom)
for (let i of this.list) {
await this.drawTag(i, zoom, ctx);
this.drawCongestionAreas(graph, 1404, 80);
this.list.forEach(item => this.allInstance += item.distance);
for (let index in this.list) {
const info = this.list[index];
const ratio = ((width - width * 0.08 * 2) / actualDis); //
info.distance = actualLocationList[index].intervalDistance * ratio;
await this.drawTag(info, graph);
}
//
graph.getCellById("K79+010").translate(-10, 0)
graph.getCellById("K83+885").translate(-10, 0);
graph.getCellById("K86+499").translate(10, 0);
graph.getCellById("K114+405").translate(-10, 0);
graph.getCellById("K117+878").translate(10, 0);
graph.getCellById("K159+156").translate(8, 0);
graph.getCellById("K155+652").translate(-10, 0);
graph.getCellById("K190+495").translate(10, 0);
},
async drayLine(zoom, text, ctx, isFoot = false) {
ctx.setLineDash([4 * zoom, 4 * zoom]);
ctx.strokeStyle = "#37B5D4";
ctx.lineWidth = 6;
async drayLine(text, graph, isFoot = false) {
const x = this.translateX + 141;
if (isFoot) {
ctx.translate(0, 12 * zoom);
ctx.strokeRect(0, 0, 1, 161 * zoom);
ctx.translate(0, -12 * zoom);
graph.addNode({
...rect, x: x, y: 30, height: 161
});
} else {
ctx.strokeRect(0, 0, 1, 135 * zoom);
graph.addNode({ ...rect, x: x, y: 30, height: 135, zIndex: 1 });
}
ctx.fillStyle = "#37B5D4";
ctx.translate(0, -16 * zoom);
const textNode = {
label: text,
attrs: {
label: {
fontSize: 12,
fill: "#37B5D4",
fontWeight: 600
}
}
}
if (isFoot) {
ctx.fillText(text, 0, 54);
ctx.translate(0, 226 * zoom);
ctx.fillText(text, 0, -90);
graph.addNode({ ...textNode, x: x, y: 22 })
// graph.fillText(text, 0, 54);
// graph.translate(0, 226);
graph.addNode({ ...textNode, x: x, y: 195 });
} else {
ctx.fillText(text, 0, 18);
graph.addNode({ ...textNode, x: x, y: 22 });
}
},
async drawTag(info, zoom, ctx) {
async drawTag(info, graph) {
this.translateX += info.distance;
let type = "3";
if (info.name.indexOf("枢纽") !== -1) {
type = "0";
@ -105,40 +226,38 @@ export default {
type = "1";
}
if (type === "0") {
ctx.translate(info.distance * zoom, -188 * zoom);
ctx.font = setFont(12 * zoom, "600");
await this.drayLine(zoom, info.line, ctx, info.isFoot);
if (info.isFoot) {
ctx.translate(-10 * zoom, -180 * zoom);
} else {
ctx.translate(-10 * zoom, 35 * zoom);
}
await this.drayLine(info.line, graph, info.isFoot);
if (info.name === "东平湖枢纽") {
const r = -110;
ctx.rotate((r * Math.PI) / 180);
ctx.strokeRect(-15 * zoom, 0, 1, 90 * zoom);
ctx.rotate((r * Math.PI) / -180);
ctx.fillText("S33济徐高速", 124 * zoom, -27 * zoom);
const r = -150;
const nodeLine = graph.addNode({ ...rect, shape: 'rect', x: 1205, y: 2, height: 85, zIndex: 1 });
nodeLine.rotate((r * Math.PI));
graph.addNode({
label: "S33济徐高速", x: 1275, y: 22, attrs: {
label: {
fontSize: 12,
fill: "#37B5D4",
fontWeight: 600
}
}
});
}
} else {
ctx.translate(info.distance * zoom, -169 * zoom);
}
let y = 42 - (info.name.length - 5) * 10;
ctx.fillStyle = "#ffffff";
ctx.font = setFont(12 * zoom);
var img = await this.loadImage(require(`./images/${info.icon || `tag${type}`}.png`));
ctx.drawImage(img, 0, 0, 21 * zoom, 117 * zoom);
ctx.translate(11.1 * zoom, y * zoom);
const imageNode = graph.addNode({
shape: 'image', imageUrl: require(`./images/${info.icon || `tag${type}`}.png`), x: this.translateX, y: this.translateY, width: 21, height: 117
});
imageNode.translate(width * 0.07, 0);
for (var i = 0; i < info.name.length; i++) {
ctx.fillText(info.name[i], 0, i * 15 * zoom);
const imageLabelNode = graph.addNode({ label: info.name[i], x: this.translateX, y: i * 15, attrs: { text: { fill: "#ffffff", fontSize: 12 } } });
imageLabelNode.translate(width * 0.0755, 50 + y);
}
ctx.translate(0, (169 - y) * zoom);
ctx.font = setFont(10.5 * zoom);
//
ctx.fillText(info.code, 0, -132);
const buttomLabelNode = graph.addNode({ label: info.code, x: this.translateX, y: 175, attrs: { text: { fill: "#ffffff", fontSize: 10.5 } }, id: info.code });
buttomLabelNode.translate(width * 0.0755, 0);
},
loadImage(url) {
return new Promise((resolve, reject) => {
@ -154,24 +273,30 @@ export default {
};
});
},
drawCongestionAreas(ctx, x, y, zoom) {
const { e: translateX, f: translateY } = ctx.getTransform();
x = translateX * -1 + x * zoom;
y = translateY * -1 + y * zoom;
const rectWidth = 180 * zoom;
const rectHeight = 8 * zoom;
const grad = ctx.createLinearGradient(x, 0, x + rectWidth, 0);
grad.addColorStop(0, 'rgba(217,50,8,0)');
grad.addColorStop(0.27, '#D73208');
grad.addColorStop(0.42, '#D93208');
grad.addColorStop(0.81, '#F5FF83');
grad.addColorStop(1, 'rgba(215,50,8,0)');
drawCongestionAreas(graph, x, y) {
// x = translateX * -1 + x;
// y = translateY * -1 + y;
ctx.fillStyle = grad;
const rectWidth = 160;
const rectHeight = 8;
ctx.fillRect(x, y, rectWidth, rectHeight);
graph.addNode({
...rect, x: x - 50, y: y, width: rectWidth, height: rectHeight, attrs: {
body: {
fill: {
type: 'linearGradient',
stops: [
{ offset: '0%', color: 'rgba(217,50,8,0)' },
{ offset: '27%', color: '#D73208' },
{ offset: '42%', color: '#D93208' },
{ offset: '81%', color: '#F5FF83' },
{ offset: '100%', color: 'rgba(215,50,8,0)' },
],
},
strokeWidth: 0
}
}
});
}
},
};

5
ruoyi-ui/vue.config.js

@ -26,6 +26,7 @@ module.exports = {
lintOnSave: process.env.NODE_ENV === "development",
// 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。
productionSourceMap: false,
transpileDependencies: ["ml-matrix"], //默认情况下为[] babel-loader 忽略其中的所有文件 node_modules
// webpack-dev-server 相关配置
devServer: {
host: "0.0.0.0",
@ -50,8 +51,8 @@ module.exports = {
// target: `http://10.0.81.204:8087`, //现场后台 刘文阁
// target: `http://10.168.69.255:8087`, //正晨后台 连现场物联 刘文阁
// target: `http://10.168.78.135:8087`, //王钦
target: `http://10.168.66.196:8087`, //正晨后台 连现场物联 刘文阁2
// target: `http://10.168.68.42:8087`, //王思祥
// target: `http://10.168.66.196:8087`, //正晨后台 连现场物联 刘文阁2
target: `http://10.168.68.42:8087`, //王思祥
changeOrigin: true,
pathRewrite: {
["^" + process.env.VUE_APP_BASE_API]: "",

Loading…
Cancel
Save