Commit 17f9d32c authored by ninglx's avatar ninglx

创建data-vision-new子项目 json参数化配置地图

parent 6bd5d21f
.DS_Store
node_modules/
dist/
../.idea/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
yarn.lock
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
],
};
{
"name": "app",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "vue-cli-service serve",
"dev": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^4.5.7",
"@vue/cli-plugin-router": "^4.5.7",
"@vue/cli-plugin-vuex": "^4.5.7",
"@vue/cli-service": "^4.5.7",
"chalk": "^3.0.0",
"commander": "^4.1.1",
"core-js": "^3.6.5",
"fs-extra": "^8.1.0",
"less": "^3.12.2",
"less-loader": "^7.0.2",
"postcss-px-to-viewport": "^1.1.1",
"shelljs": "^0.8.4",
"vue-template-compiler": "^2.6.11"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"Chrome 49"
],
"dependencies": {
"axios": "^1.5.0",
"codemirror": "^5.46.0",
"compression-webpack-plugin": "^3.1.0",
"driver.js": "^0.9.8",
"highcharts": "^10.3.3",
"highcharts-vue": "^1.4.0",
"js-cookie": "^3.0.0",
"js-export-excel": "^1.1.4",
"sortablejs": "^1.13.0",
"vue": "^2.6.11",
"vue-clipboard2": "^0.3.1",
"vue-router": "^3.4.6",
"vue-seamless-scroll": "^1.1.23",
"vuedraggable": "^2.24.3",
"vuex": "^3.5.1"
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>logo.png" />
<title></title>
<link href="/cdn/libs/element-ui/element-ui.css" rel="stylesheet" />
<link href="/cdn/font/font.css" rel="stylesheet" />
<link href="/cdn/libs/mapbox-gl/mapbox-gl.css" rel="stylesheet" />
<link href="/cdn/libs/threebox/threebox.css" rel="stylesheet" />
<link href="/cdn/libs/Cesium/Widgets/widgets.css" rel="stylesheet" />
<link
rel="stylesheet"
href="/cdn/libs/mapbox-gl-draw/mapbox-gl-draw.css"
type="text/css"
/>
<script src="/cdn/libs/vue/vue.js"></script>
<script src="/cdn/libs/element-ui/element-ui.js"></script>
<script src="/cdn/libs/echarts/echarts.min.js"></script>
<script src="/cdn/libs/mapbox-gl-draw/mapbox-gl-draw.js"></script>
<script
src="/cdn/libs/mapbox-gl/mapbox-gl.js"
type="text/javascript"
></script>
<script
src="/cdn/libs/threebox/threebox.js"
type="text/javascript"
></script>
<script src="/cdn/libs/turf/turf.min.js" type="text/javascript"></script>
<script src="/cdn/libs/flvjs/flv.min.js" type="text/javascript"></script>
<script src="/cdn/libs/cesium/Cesium.js" type="text/javascript"></script>
<script
src="/cdn/libs/reconnecting-websocket/reconnecting-websocket.js"
type="text/javascript"
></script>
<style>
#app {
height: 100%;
width: 100%;
}
*{
box-sizing: border-box;
}
.full-w-h{
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<noscript>
<strong>We're sorry but app doesn't work properly without JavaScript enabled. Please enable it to
continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
\ No newline at end of file
<template>
<div class="full-w full-h situation-main">
<vision-header/>
<router-view class="router-view-main" />
</div>
</template>
<script>
import VisionHeader from "./components/visionHeader.vue";
export default {
components: { VisionHeader },
data() {
return {
showEditor: false,
};
},
computed: {},
created() {},
mounted() {
window.onkeydown = () => {
// alt + e 编辑
if (event.ctrlKey && event.altKey && event.keyCode === 69) {
this.$store.commit("troggleEditorStatus");
}
// alt + 1
if (event.altKey && event.keyCode === 49) {
this.$router.push("/home");
}
// alt + 2
if (event.altKey && event.keyCode === 50) {
this.$router.push("/situation1");
}
// alt + 3
if (event.altKey && event.keyCode === 51) {
this.$router.push("/situation2");
}
// alt + 4
if (event.altKey && event.keyCode === 52) {
this.$router.push("/situation3");
}
};
},
methods: {},
};
</script>
<style lang="less" scoped>
#app {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
height: 100%;
width: 100%;
}
.full-h {
height: 100vh;
}
.full-w {
width: 100%;
}
.situation-main {
position: relative;
.router-view-main {
height: 100%;
}
}
</style>
<template>
<div class="cameraVideo">
<div class="loading-mask" v-show="cameraLoading">
<div style="position: relative">
<div class="el-icon-loading"></div>
<div>加载中...</div>
</div>
</div>
<!-- <div class="videoControl" @loadstart="loadstart($event)"-->
<!-- @canplay="canplay($event)" :id="videoData"></div>-->
<video
@loadstart="loadstart($event)"
@canplay="canplay($event)"
class="videoControl"
muted
:id="videoData"
ref="myCameraVideoPlayer"
></video>
</div>
</template>
<script>
export default {
name: "cameraVideo",
props: ["videoData"],
watch: {},
methods: {
canplay() {
this.cameraLoading = false;
this.$emit("vidCanplay", this.videoData);
},
loadstart() {
// this.cameraLoading = true;
},
loadVideo() {
console.log("video init...", this.videoData);
// let el = document.getElementById(this.videoData)
this.player && this.destroy();
this.player = flvjs.createPlayer({
type: "flv",
isLive: true,
url: this.videoData,
hasAudio: false,
hasVideo: true,
cors: true, // 是否跨域
});
// console.log('el', el, this.player)
let el = document.getElementById(this.videoData);
this.player?.attachMediaElement(el);
this.player?.load();
this.bindEvents();
if (this.player) {
this.player?.play();
}
},
bindEvents() {
//视频出错后销毁重新创建
this.player.on(flvjs.Events.ERROR, this.handleErr);
this.player.on(flvjs.Events.LOADING_COMPLETE, this.handleErr);
},
handleErr() {
if (this.player) {
try {
this.player?.pause();
this.player?.unload();
this.player?.detachMediaElement();
this.player?.destroy();
this.player = null;
} catch (error) {}
this.$nextTick(() => {
this.loadVideo();
});
}
},
pause() {
this.player && this.player?.pause();
},
destroy() {
if (this.player) {
this.player?.pause();
this.player?.unload();
this.player?.detachMediaElement();
this.player?.destroy();
this.player = null;
}
if (this.interval) {
clearInterval(this.interval);
}
},
},
data() {
return {
cameraLoading: true,
supported: false,
player: null,
interval: null,
};
},
mounted() {
// console.log('videoData', this.videoData);
this.player = null;
this.supported = flvjs.isSupported();
this.$nextTick(() => {
this.loadVideo();
});
this.interval = setInterval(() => {
if (this.cameraLoading) {
this.handleErr();
} else {
clearInterval(this.interval);
}
}, 5000);
},
beforeDestroy() {
this.destroy();
},
};
</script>
<style lang="less" scoped>
.loading-mask {
border-radius: 4px;
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(8, 22, 35, 0.7);
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
color: #fcfcfc;
}
.cameraVideo {
position: relative;
.videoControl {
// display: none;
position: absolute;
height: 100%;
width: 100%;
}
// .vIsVisible {
// display: unset;
// }
}
::v-deep .el-icon-loading {
position: absolute;
left: 30%;
top: -14px;
transform: translateX(-50%);
}
</style>
<template>
<div class="track3D-container" :id="cId"></div>
</template>
<script>
let viewer = null;
export default {
name: "cesiumMap",
props: ["cId", "loadTileset"],
components: {},
watch: {},
data() {
return {};
},
computed: {},
methods: {
loadMap() {
Cesium.Ion.defaultAccessToken =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1OWE5YzQ2ZS05NDYzLTQ3NTEtYTZhOC0yNDhmMmIyY2I5ZTAiLCJpZCI6MTA0MTAyLCJpYXQiOjE2ODU2MTMxOTB9.JxmgXnf8_-V1eM9we2W8VfiP37vyGMJJDSWF4Br6hKU";
// Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkZWVjYmI0Mi0wMzk2LTQ2YjMtYjYwOC04MmMyNzEyYjJiOTMiLCJpZCI6MTA0MTAyLCJpYXQiOjE2NjAwMDQ5MjR9.YwCrjFrZrgYYxxxqAXlDWB_eQdmPh8kgj8ZRfLcEOT4';
viewer = new Cesium.Viewer(this.cId, {
targetFrameRate: 30, // 目标帧率
animation: false, // 隐藏动画控件
baseLayerPicker: false, // 隐藏图层选择控件
fullscreenButton: false, // 隐藏全屏按钮
vrButton: false, // 隐藏VR按钮,默认false
geocoder: false, // 隐藏地名查找控件
homeButton: false, // 隐藏Home按钮
// infoBox: true, // 隐藏点击要素之后显示的信息窗口
infoBox: false, // 隐藏点击要素之后显示的信息窗口
sceneModePicker: false, // 隐藏场景模式选择控件
selectionIndicator: true, // 显示实体对象选择框,默认true
timeline: true, // 时间线控件
navigationHelpButton: false, // 隐藏帮助按钮
scene3DOnly: true, // 每个几何实例将只在3D中呈现,以节省GPU内存
shouldAnimate: true, // 开启动画自动播放
sceneMode: 3, // 初始场景模式 1:2D 2:2D循环 3:3D,默认3
requestRenderMode: true, // 减少Cesium渲染新帧总时间并减少Cesium在应用程序中总体CPU使用率
// msaaSamples: 0,
imageryProvider: false,
// 如场景中的元素没有随仿真时间变化,请考虑将设置maximumRenderTimeChange为较高的值,例如Infinity
// maximumRenderTimeChange: Infinity,
// shadows: true, // 阴影
// imageryProvider: new Cesium.UrlTemplateImageryProvider({
// url: cesium_config.baseMap,
// fileExtension: "png"
// }),
// imageryProvider:new Cesium.ArcGisMapServerImageryProvider({
// url: 'https://server.arcgisonline.com/arcgis/rest/services/World_Terrain_Base/MapServer'
// }),
// terrainProvider: new Cesium.EllipsoidTerrainProvider({}), // 地形
});
// viewer.scene.globe.baseColor = Cesium.Color.BLACK; //改变空球的颜色
viewer.scene.globe.baseColor = Cesium.Color.fromCssColorString("#53514c"); //改变空球的颜色
viewer.scene.globe.enableLighting = true;
viewer.scene.light = new Cesium.DirectionalLight({
direction: new Cesium.Cartesian3(0.354925, -0.890918, -0.283358),
});
if (this.loadTileset) {
// tileset三维场景
const tileset = new Cesium.Cesium3DTileset({
url: cesium_config.tile3dURL,
// show: true,
// maximumMemoryUsage: 128, // 内存分配变小有利于倾斜摄影数据回收,提升性能体验
// maximumScreenSpaceError: 256, // 数值加大,能让最终成像变模糊
// skipLevelOfDetail: true, //通过优化,树的级别可以完全跳过,子树可以与父树一起呈现 内存显著减少
// loadSiblings: true, // 如果为true则不会在已加载完概况房屋后,自动从中心开始超清化房屋
// progressiveResolutionHeightFraction: 0.5, // 数值偏于0能够让初始加载变得模糊0
// cullRequestsWhileMovingMultiplier: 10, // 值越小能够更快的剔除
// dynamicScreenSpaceError: true,
// projectTo2D: true,
});
// const tileset1 = new Cesium.Cesium3DTileset({
// // url: "changsha123/tileset.json",
// url: "jinan/tileset.json",
// });
// viewer.scene.primitives.add(tileset1);
// // viewer.scene.globe.depthTestAgainstTerrain = true;
// // viewer.extend(Cesium.viewerCesium3DTilesInspectorMixin);
// tileset1.readyPromise.then(() => {
// window.tileset = tileset1;
// tileset1.colorBlendMode = Cesium.Cesium3DTileColorBlendMode.REPLACE;
// tileset1.style = new Cesium.Cesium3DTileStyle({
// color: {
// conditions: [
// ['${name} === "HLD_CSZ_001_R002"', 'color("red")'],
// ["true", 'color("white")'],
// ],
// },
// });
// });
// tileset1.debugColorizeTiles = true;
// viewer.zoomTo(tileset1);
tileset.readyPromise.then(() => {
window.tileset = tileset;
tileset.colorBlendMode = Cesium.Cesium3DTileColorBlendMode.REPLACE;
this.$emit("tilesetReady");
//计算tileset的绑定范围
let boundingSphere = tileset.boundingSphere;
//计算中心点位置
let cartographic = Cesium.Cartographic.fromCartesian(
boundingSphere.center
);
//计算中心点位置的地表坐标
let surface = Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
0.0
);
console.log("tileset自身center : ", [
Cesium.Math.toDegrees(cartographic.longitude),
Cesium.Math.toDegrees(cartographic.latitude),
]);
// 偏移tileset到指定位置
let heightOffset = cesium_config.tilesetHeightOffset;
let x = Cesium.Math.toDegrees(cartographic.longitude);
let y = Cesium.Math.toDegrees(cartographic.latitude);
let offset = Cesium.Cartesian3.fromRadians(
cesium_config.tilesetOffsetX
? Cesium.Math.toRadians(x + cesium_config.tilesetOffsetX)
: cartographic.longitude,
cesium_config.tilesetOffsetY
? Cesium.Math.toRadians(y + cesium_config.tilesetOffsetY)
: cartographic.latitude,
heightOffset
);
let translation = Cesium.Cartesian3.subtract(
offset,
surface,
new Cesium.Cartesian3()
);
//tileset.modelMatrix转换
tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
});
// viewer.scene.globe.show = false
// viewer.imageryLayers.removeAll()
viewer.scene.primitives.add(tileset);
}
// viewer.scene.globe.enableLighting = false
// viewer.scene.sun.show = false
// viewer.zoomTo(tileset);
viewer.cesiumWidget.creditContainer.style.display = "none";
// 以下设置为了使cesium地图鼠标控制符合mapbox习惯
// 关闭双击事件
viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(
Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
);
// 鼠标右键旋转
viewer.scene.screenSpaceCameraController.tiltEventTypes = [
Cesium.CameraEventType.RIGHT_DRAG,
];
// 中键滚动缩放
viewer.scene.screenSpaceCameraController.zoomEventTypes = [
Cesium.CameraEventType.WHEEL,
];
// 鼠标左键平移
viewer.scene.screenSpaceCameraController.rotateEventTypes = [
Cesium.CameraEventType.LEFT_DRAG,
];
viewer.scene.globe.enableLighting = true;
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(
...cesium_config.center,
cesium_config.zoomHeight
),
orientation: {
heading: Cesium.Math.toRadians(0), // 旋转角度
pitch: Cesium.Math.toRadians(-90.0), // 相机方向
},
duration: 0.5,
});
return viewer;
},
},
mounted() {},
created() {},
beforeDestroy() {
viewer?.destroy();
},
};
</script>
<style>
.cesium-viewer-timelineContainer {
z-index: 15;
left: 625px !important;
width: 1025px !important;
bottom: 35px !important;
height: 40px !important;
}
.cesium-timeline-bar {
height: 30px;
}
.cesium-timeline-main {
height: inherit;
}
.cesium-timeline-trackContainer {
height: unset !important;
}
/* pop框css*/
.cesium-popup-panel {
opacity: 0.8;
position: absolute;
z-index: 0;
}
.cesium-popup-close-btn > svg:hover {
color: #00fcf9 !important;
}
.cesium-popup-close-btn > svg {
display: none;
user-select: auto;
color: #4674d6;
cursor: pointer;
width: 15px;
}
.cesium-viewer {
position: unset;
}
</style>
<template>
<div
class="loopVideo"
v-loading="videoLoading"
element-loading-text="加载中..."
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)"
>
<video
controls
class="videoControl"
ref="player"
@loadstart="handleLoadStart"
@loadedmetadata="handleLoadedMetadata"
@ended="handleEnded"
@canplay="videoCanPlay"
></video>
</div>
</template>
<script>
let intervalTimer = null
import { getEventVideo } from "../../dao/track";
export default {
name: "loopVideo",
props: {
channel: String,
timeModel: Object,
autoplay: Boolean,
},
data() {
return {
videoLoading: true,
videoUrl: null,
flvPlayer: null,
intervalTimer: null,
};
},
mounted() {
this.$EventBus.$on("startPlay", this.startPlay());
if (this.channel) {
console.log(
"loopVideo start and end",
this.replaceTimeGap(this.timeModel.startTime),
this.replaceTimeGap(this.timeModel.endTime)
);
this.getVideoStreamUrl();
intervalTimer = setInterval(() => {
if (this.videoLoading) {
this.handleErr();
} else {
this.clearCurrentInterval();
}
}, 5000);
}
},
methods: {
handleErr() {
this.destroyPlayer();
this.$nextTick(() => {
this.getVideoStreamUrl();
});
},
videoCanPlay() {
this.videoLoading = false;
this.$emit("videoReady", this.channel);
},
replaceTimeGap(time) {
return time
.toString()
.replaceAll("-", "_")
.replaceAll(" ", "_")
.replaceAll(":", "_");
},
async getVideoStreamUrl() {
if (this.timeModel.startTime && this.timeModel.endTime) {
try {
const response = await getEventVideo({
channel: this.channel,
subtype: 0,
starttime: this.replaceTimeGap(this.timeModel.startTime),
endtime: this.replaceTimeGap(this.timeModel.endTime),
});
this.videoUrl = response.content;
this.playVideo();
} catch (error) {
console.error(`Failed to get video stream url: ${error}`);
}
} else {
console.log("no timerange...");
}
},
playVideo() {
if (!flvjs.isSupported()) {
console.error("FLV is not supported by this browser!");
return;
}
this.flvPlayer = flvjs.createPlayer({
type: "flv",
url: this.videoUrl,
isLive: true,
hasAudio: false,
hasVideo: true,
cors: true, // 是否跨域
});
this.flvPlayer.attachMediaElement(this.$refs.player);
this.flvPlayer.load();
if (this.flvPlayer && this.autoplay) {
this.startPlay();
}
},
clearCurrentInterval() {
if (intervalTimer) {
clearInterval(intervalTimer);
intervalTimer = null;
}
},
startPlay() {
// 已经触发播放但当前视频仍未加载出来则清除定时拉取请求
if (this.videoLoading) {
this.clearCurrentInterval();
}
this.flvPlayer?.play();
},
handleMediaDetached() {},
handleLoadStart() {},
handleLoadedMetadata() {},
handleEnded() {
if (this.flvPlayer) {
this.flvPlayer.play();
}
},
destroyPlayer() {
if (this.flvPlayer) {
this.flvPlayer?.pause();
this.flvPlayer?.unload();
this.flvPlayer?.detachMediaElement();
this.flvPlayer?.destroy();
this.flvPlayer = null;
}
},
},
watch: {
timeModel(val) {
this.handleErr();
},
},
beforeDestroy() {
this.clearCurrentInterval();
this.destroyPlayer();
this.$EventBus.$off("startPlay");
},
};
</script>
<style lang="less" scoped>
.loopVideo {
height: 100%;
width: 100%;
.videoControl {
// position: absolute;
height: 100%;
width: 100%;
}
}
.loading-mask {
// position: absolute;
width: 100%;
height: 100%;
background-color: rgba(8, 22, 35, 0.7);
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
color: #fcfcfc;
}
// ::v-deep .el-icon-loading {
// position: absolute;
// left: 30%;
// top: -14px;
// transform: translateX(-50%);
// }
</style>
<template>
<div :id="mapId" class="w_map" style="height: 100%; width: 100%"></div>
</template>
<script>
let map;
// Point MultiPoint LineString MultiLineString Polygon MultiPolygon
export default {
name: "wMap",
props: ["mapId", "mapOptions"],
components: {},
data() {
return {};
},
mounted() {},
methods: {
initMap() {
let options = {
container: this.mapId,
style: 'http://106.120.201.126:14724/style.json',
center: this.mapOptions.center,
zoom: this.mapOptions.zoom,
maxZoom: this.mapOptions.maxZoom,
pitch: this.mapOptions.pitch,
bearing: this.mapOptions.bearing,
};
map = new mapboxgl.Map(options);
return map;
},
destroyMap() {
map?.remove;
map = null;
},
},
computed: {},
beforeDestroy() {},
};
</script>
<style scoped>
.w_map{
position: absolute;
left: 0;
top: 0;
}
</style>>
<style>
.mapboxgl-ctrl-attrib {
display: none;
}
</style>
<template>
<div class="msg-card">
<div class="msg-card-title" v-if="!noHeader">
<div class="text">
{{ title }}
</div>
</div>
<div :class="[noHeader ? 'msg-card-content-noHeader' : 'msg-card-content']">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: "msg-card",
props: {
long: Boolean,
title: String,
subhead: String,
noHeader: Boolean,
},
computed: {},
};
</script>
<style lang="less" scoped>
.text {
padding: 4px 0 4px 20px;
position: relative;
display: flex;
align-items: center;
font-size: 21px;
font-weight: 700;
color: white;
}
.right-text {
margin-left: 25px;
}
.msg-card {
width: 100%;
height: 100%;
.msg-card-title {
height: 45px;
font-weight: bold;
line-height: 38px;
padding-left: 6px;
width: 100%;
text-align: left;
background-image: url("../assets/images/msgCardHead.png");
background-size: 100% 100%;
}
.msg-card-content {
overflow-y: hidden;
padding: 10px 10px 0 10px;
background-size: 100% 100%;
height: calc(100% - 45px);
}
.msg-card-content-noHeader {
height: 100%;
overflow-y: hidden;
padding: 10px 10px 0 10px;
background-size: 100% 100%;
}
}
/*滚动条样式:谷歌浏览器下*/
::-webkit-scrollbar {
width: 10px;
height: 10px;
background-color: #2e5e99;
}
/*滚动条的轨道*/
::-webkit-scrollbar-track {
background-color: #0f2645;
}
/*滚动条的滑块按钮*/
::-webkit-scrollbar-thumb {
border-radius: 10px;
background-color: #2e5e99;
}
/*滚动条的上下两端的按钮*/
::-webkit-scrollbar-button {
height: 0;
width: 0;
}
</style>
<template>
<div
v-loading="loading"
element-loading-text="数据加载中.."
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)"
>
<div style="position: relative; width: 100%; height: 100%">
<path-map
@mapLoad="mapLoad"
mapId="pathMapCtrl"
ref="pathMap"
:eventTargetVehicleGlobalId="eventTargetVehicleGlobalId"
@timeChange="timeChange"
:speed="speedValue"
:filterType="filterType"
></path-map>
<div v-show="showTimeStamp" class="pathMapTimestamp">
{{ currentTimestamp }}
</div>
<div v-show="showSlider" class="slider-box">
<i
class="slider-icon"
@click="playClick"
:style="`cursor:${duration == 0 ? 'not-allowed' : 'pointer'}`"
:class="{ active: playFlag }"
></i>
<el-slider
ref="sliderRef"
v-model="currentTime"
:disabled="duration === 0"
:min="0"
:max="duration"
:show-tooltip="false"
@mousedown="handleMouseDown"
@mouseup="handleMouseUp"
@change="handleTimeChange"
></el-slider>
<div class="timeDurationSpend">
{{ formatTime(Math.round(currentTime / 1000)) }} /
{{ formatTime(Math.round(duration / 1000)) }}
</div>
<el-select v-model="speedValue" class="ctrl-select">
<el-option
v-for="item of speeds"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</div>
<div class="point-check" @click="heatMapAnalysis" v-if="showFilter">
{{ this.heatShow ? "隐藏交织点" : "交织点分析" }}
</div>
<div class="type-check" v-if="showFilter">
<div class="filter-title">轨迹回放</div>
<div class="type-check-inner">
<el-checkbox
:indeterminate="isIndeterminate"
v-model="checkAll"
@change="checkAllChange"
>全选</el-checkbox
>
<el-checkbox-group
v-model="checkedTypes"
@change="handleCheckedChange"
style="display: flex; flex-direction: column"
>
<el-checkbox v-for="item in types" :label="item" :key="item">{{
item
}}</el-checkbox>
</el-checkbox-group>
</div>
</div>
</div>
</div>
</template>
<script>
let pMap = null;
let protobuf = require("protobufjs");
let pako = require("pako");
let FrameList;
protobuf.load("../wj-manage-web/RealtimeCarInfo.proto", (event, root) => {
FrameList = root.lookupType("com.wanji.holo.proto.FrameList");
});
const typeOptions = ["不流畅车", "减速车", "直行汇车", "右转汇车"];
import pathMap from "./pathMap.vue";
import * as trackApi from "../../../../dao/historyTrack";
import * as mapTools from "../../../../utils/mapboxTools";
import { orgConflict } from "../../../../dao/organization";
export default {
name: "pathMapCtrl",
components: { pathMap },
data() {
return {
filterType: null,
checkAll: true,
checkedTypes: typeOptions,
types: typeOptions,
isIndeterminate: false,
speeds: [
{ value: 0.25, label: "x 0.25" },
{ value: 0.5, label: "x 0.5" },
{ value: 1, label: "正常速度" },
{ value: 1.5, label: "x 1.5" },
{ value: 2, label: "x 2" },
],
speedValue: 1,
currentTime: 0,
playFlag: false,
duration: 0,
playTimer: null,
loading: false,
timeShouldChange: true,
heatShow: false,
currentLocalTime: 0,
currentRequestTime: {
startTime: 0,
endTime: 0,
},
lastRequestParams: {},
lastRequestApi: "",
currentPage: 1,
totalPage: 0,
trackStartTime: "",
};
},
computed: {
showSlider() {
return this.showCtrl && this.duration;
},
currentTimestamp() {
return new Date(this.currentLocalTime).toLocaleString();
},
showTimeStamp() {
if (this.showTime) {
if (this.currentLocalTime != 0) {
return true;
}
}
},
mainParam() {
return {
startTime: this.params.startTime,
endTime: this.params.endTime,
crossId: this.params.crossId,
};
},
},
props: {
showTime: {
type: Boolean,
default: () => {
return false;
},
},
showFilter: {
type: Boolean,
default: () => {
return false;
},
},
showCtrl: {
type: Boolean,
default: () => {
return true;
},
},
// 接口请求参数
params: {
type: Object,
default: () => {
return {
startTime: "2023-10-19 07:00:00",
endTime: "2023-10-19 07:15:00",
crossId: "13NGH0B5RC0",
};
},
},
// 接口名称 对应 dao/historyTrack.js
apiName: {
type: String,
default: () => {
return "vehicleTrack";
},
},
eventTargetVehicleGlobalId: {
type: String,
default: () => {
return "";
},
},
},
mounted() {
const sliderElement = this.$refs.sliderRef.$el;
sliderElement.addEventListener("mouseup", this.handleMouseUp);
sliderElement.addEventListener("mousedown", this.handleMouseDown);
this.$EventBus.$on("refreshHeatMap", (data) => this.refreshHeatMap(data));
this.$EventBus.$on("removeHeatMap", () => this.refreshHeatMap([]));
},
beforeDestroy() {
this.$EventBus.$off("refreshHeatMap");
this.$EventBus.$off("removeHeatMap");
},
watch: {
apiName(val) {
if (val !== "vehicleTrack") {
this.filterType = null;
}
},
},
methods: {
locate(center) {
pMap?.flyTo({
center: center,
zoom: 17.1,
});
},
mapLoad(map) {
pMap = map;
},
removeLayers(name) {
if (pMap && pMap.getLayer(name)) {
pMap.removeLayer(name);
}
},
//=====================================================================交织点相关逻辑
// 更新交织点热力图
refreshHeatMap(tableData) {
let features = [];
for (let item of tableData) {
features.push(turf.point([item.lng, item.lat], item));
}
mapTools.addOrUpdateEventHeat(pMap, turf.featureCollection(features));
},
// 交织点分析
heatMapAnalysis() {
// 如果请求展示热力图
if (!this.heatShow) {
this.loading = true;
orgConflict({
crossId: this.params.crossId,
startDate: this.params.startTime,
endDate: this.params.endTime,
}).then((res) => {
this.loading = false;
console.log("交织点分析", res);
if (res.content && res.content.length) {
this.heatShow = true;
let geo = this.convertPointJsonToGeo(res.content);
mapTools.addOrUpdateEventHeat(pMap, geo);
} else {
ELEMENT.Message("当前时段暂无交织点信息");
}
});
} else {
this.removeLayers("eventHeat");
this.heatShow = false;
}
},
convertPointJsonToGeo(array) {
let features = [];
for (let item of array) {
features.push(turf.point([item.lng, item.lat], item));
}
return turf.featureCollection(features);
},
//====================================================================================
//=====================================================================车辆筛选相关逻辑
checkAllChange(val) {
this.checkedTypes = val ? typeOptions : [];
this.isIndeterminate = false;
this.refreshPathFilter();
},
handleCheckedChange(value) {
let checkedCount = value.length;
this.checkAll = checkedCount === this.types.length;
this.isIndeterminate =
checkedCount > 0 && checkedCount < this.types.length;
this.refreshPathFilter();
},
// 更新 轨迹的 filter
refreshPathFilter() {
let typeMap = {
不流畅车: "1",
减速车: "2",
右转汇车: "3",
直行汇车: "4",
};
console.log(this.checkedTypes);
let types = [];
for (let item of this.checkedTypes) {
types.push(typeMap[item]);
}
this.filterType = types;
},
//====================================================================================
handleMouseDown() {
this.timeShouldChange = false;
},
handleMouseUp() {
// 鼠标按下拖动时暂停currentTime改变
this.timeShouldChange = true;
},
timeChange(getTime) {
this.currentLocalTime = getTime;
let spend = getTime - new Date(this.trackStartTime).getTime();
// timeShouldChange 防止拖动 slider 时 时间改变导致覆盖了拖动赋值
this.timeShouldChange && (this.currentTime = spend);
},
clearMap() {
this.$refs.pathMap?.clearAll();
this.$nextTick(() => {
this.duration = 0;
this.currentTime = 0;
});
},
questTrack(isReplay) {
trackApi[this.apiName]({
pageNum: isReplay ? 1 : this.currentPage,
pageSize: 1,
...this.params,
}).then((res) => {
this.loading = false;
this.lastRequestParams = this.params;
this.lastRequestApi = this.apiName;
if (res) {
let data = pako.inflate(res); // pako解压为protobuf
if (data) {
let realData = FrameList.decode(data); // protobuf根据定义类型转json
let page = realData.pageInfo;
this.totalPage = page.totalPageNum;
let frameInfo = realData.frameInfo;
if (isReplay) {
this.trackStartTime = new Date(frameInfo[0].frameTime).getTime();
this.$refs.pathMap?.clearAll();
}
this.$EventBus.$emit("fullTrack", frameInfo);
console.log("frameInfo...", frameInfo);
// 计算返回数据的总时长
let pStartTime = isReplay
? new Date(frameInfo[0].frameTime).getTime()
: this.trackStartTime;
let pEndTime = new Date(
frameInfo[frameInfo.length - 1].frameTime
).getTime();
this.duration = pEndTime - pStartTime;
console.log("this.duration", this.duration);
if (isReplay) {
this.$nextTick(() => {
this.$refs.pathMap.render();
this.playFlag = true;
});
}
setTimeout(() => {
this.getTracksAndPlay();
}, 1000);
} else {
// this.$message("暂无轨迹数据");
}
} else {
// this.$message("暂无轨迹数据");
}
});
},
getTracksAndPlay() {
// 如果上次请求的startTime endTime crossId apiName 与本次请求的相同,则 pageNum++继续请求
if (
JSON.stringify(this.lastRequestParams) == JSON.stringify(this.params) &&
this.lastRequestApi == this.apiName
) {
console.log(this.currentPage, this.totalPage, "继续请求");
// 如果pageNum 小于 total 则继续请求
if (this.currentPage < this.totalPage) {
this.currentPage = this.currentPage + 1;
this.questTrack();
}
// 否则 表示已经分页请求完成
}
// 否则 clear, 重新请求
else {
console.log("api || 时间不符, 清理重新请求");
this.clearMap();
this.currentPage = 1;
setTimeout(() => {
this.loading = true;
this.questTrack(true);
}, 100);
}
},
playClick() {
if (this.duration == 0) return;
this.playFlag = !this.playFlag;
this.playFlag
? this.$refs.pathMap.continuePlay()
: this.$refs.pathMap.pause();
},
handleTimeChange(value) {
if (value) {
const time = Math.floor(value);
this.currentTime = time;
this.$refs.pathMap.timeTo(value);
}
},
formatTime(time) {
// 格式化时间的方法,将时间转换成分钟和秒钟的形式
const minutes = Math.floor(time / 60);
const seconds = Math.floor(time % 60);
return `${minutes < 10 ? "0" + minutes : minutes}:${
seconds < 10 ? "0" + seconds : seconds
}`;
},
},
};
</script>
<style>
.mapboxgl-ctrl-attrib {
display: none;
}
</style>
<style lang="less" scoped>
.point-check {
position: absolute;
right: 112px;
top: 15px;
font-size: 12px;
border-radius: 1px;
background: rgba(24, 144, 255, 0.15);
width: 92px;
height: 28px;
border: 1px solid rgba(24, 144, 255, 1);
display: flex;
justify-content: center;
cursor: pointer;
align-items: center;
color: rgba(24, 144, 255, 1);
font-size: 14px;
font-weight: 700;
}
.point-check:hover {
background: rgba(24, 144, 255, 0.5);
}
.type-check {
position: absolute;
right: 15px;
top: 15px;
font-size: 12px;
margin-left: 14px;
width: 92px;
::v-deep .el-checkbox__label {
font-size: 12px;
color: rgba(255, 255, 255, 0.7);
line-height: 17px;
}
::v-deep .el-checkbox__input.is-checked + .el-checkbox__label {
color: rgba(255, 255, 255, 0.8);
}
.type-check-inner {
width: 100%;
padding: 10px;
background-color: rgba(10, 33, 69, 0.67);
}
.filter-title {
display: flex;
align-items: center;
justify-content: center;
border-radius: 1px;
margin-bottom: 8px;
width: 92px;
height: 28px;
background: rgba(255, 120, 23, 0.15);
color: rgba(179, 100, 77, 1);
border: 1px solid rgba(179, 100, 77, 1);
font-size: 14px;
font-weight: 700;
}
}
.pathMapTimestamp {
position: absolute;
left: 12px;
top: 8px;
color: white;
z-index: 9;
}
.slider-box {
position: absolute;
display: flex;
align-items: center;
width: 95%;
height: 48px;
left: 50%;
bottom: 15px;
padding: 0 16px;
transform: translateX(-50%);
background: url("../../../../assets/images/signal/map-slider-bg.png")
no-repeat;
background-size: 100% 100%;
.el-slider {
padding: 0 8px;
width: calc(100% - 95px - 95px - 24px - 12px - 6px);
margin: 0 6px;
}
.timeDurationSpend {
color: white;
width: 95px;
white-space: nowrap;
margin-right: 6px;
}
.slider-icon {
display: inline-block;
width: 24px;
height: 24px;
cursor: pointer;
background: url("../../../../assets/images/signal/play-icon.svg") no-repeat;
background-size: 100% 100%;
&.active {
background: url("../../../../assets/images/signal/play-icon-active.svg")
no-repeat;
background-size: 100% 100%;
}
}
.ctrl-select {
height: 24px;
width: 95px;
::v-deep .el-input__inner {
height: 24px;
line-height: 24px;
padding: 0 8px;
}
::v-deep .el-input__icon {
height: 24px;
line-height: 24px;
}
}
}
</style>
\ No newline at end of file
<template>
<div style="height: 100%" :id="mapId"></div>
</template>
<script>
let map;
let vehicleModelTypes = {},
fullTrack = [],
dillPath = [],
lastFrame = { carInfo: [] };
import dict from "../../../../config/holo/dictionary";
// Point MultiPoint LineString MultiLineString Polygon MultiPolygon
export default {
name: "wMap",
props: ["mapId", "speed", "filterType", "eventTargetVehicleGlobalId"],
components: {},
data() {
return {
playStatus: false,
vehicleModelTypeNumber: 0,
};
},
mounted() {
this.initMap();
this.$EventBus.$on("fullTrack", (data) => {
fullTrack = [...fullTrack, ...data];
dillPath = [...dillPath, ...data];
});
},
methods: {
// 从轨迹数组中找到 离点击拖动获取的时间最近的那一项 的下标
getClosestIndex(arr, targetTime) {
let closestIndex = 0;
let closestDiff = Math.abs(
targetTime - new Date(arr[0].frameTime).getTime()
);
for (let i = 1; i < arr.length; i++) {
const diff = Math.abs(
targetTime - new Date(arr[i].frameTime).getTime()
);
if (diff < closestDiff) {
closestDiff = diff;
closestIndex = i;
}
}
return closestIndex;
},
// 定位到某一个时间
timeTo(timestamp) {
let realTimeStamp =
new Date(fullTrack[0].frameTime).getTime() + timestamp;
let index = this.getClosestIndex(fullTrack, realTimeStamp);
dillPath = fullTrack.slice(index);
// this.continuePlay();
},
pause() {
this.playStatus = false;
},
continuePlay() {
this.playStatus = true;
this.renderPath();
},
// 取到数据 fullTrack 开始渲染
render() {
dillPath = JSON.parse(JSON.stringify(fullTrack));
if (dillPath[0]?.carInfo?.length) {
this.playStatus = true;
map.flyTo({
center: [
dillPath[0].carInfo[0].longitude,
dillPath[0].carInfo[0].latitude,
],
zoom: 19,
});
// 等待模型加载完成
let interval = setInterval(() => {
if (this.vehicleModelTypeNumber === 41) {
this.renderPath(true);
clearInterval(interval);
}
}, 500);
} else {
this.$message("暂无历史轨迹数据");
}
},
initMap() {
map = new mapboxgl.Map({
container: this.mapId,
style: map_config.MAP_STYLE,
center: map_config.MAP_CENTER,
zoom: map_config.MAP_ZOOM,
maxZoom: map_config.MAX_ZOOM,
pitch: map_config.MAP_PITCH,
});
map.on("style.load", () => {
this.$emit("mapLoad", map);
map.addLayer({
id: "vehicle3D",
type: "custom",
renderingMode: "3d",
onAdd: (map, mbxContext) => {
window.tb = new Threebox(map, mbxContext, {
defaultLights: true,
enableSelectingObjects: false,
});
let needDicts = ["CarType"];
let queue = [];
for (let item of needDicts) {
queue.push(
this.$store.dispatch("QUERY_DICT", {
type: item,
})
);
}
Promise.all(queue).then(() => {
for (let item of this.$store.state.dicts.CarType) {
let targetType = dict.carTypeGlbMap[item.code];
let options = {
obj: `gltf/car${targetType}.glb`,
type: "gltf",
units: "meters",
scale: 0.8,
adjustment: { x: 0.5, y: 1, z: -0.5 },
bbox: true,
};
window.tb.loadObj(options, (model) => {
vehicleModelTypes[`car${item.code}`] = model;
this.vehicleModelTypeNumber += 1;
});
}
});
},
render: function (gl, matrix) {
window.tb.update();
},
});
});
return map;
},
getMap() {
return map;
},
clearAll() {
this.playStatus = false;
window.tb?.clear(null, true);
dillPath = [];
fullTrack = [];
window.tb?.update();
},
hasDuplicate(arr1, arr2) {
const set1 = new Set(arr1);
return arr2.some((item) => set1.has(item));
},
renderPath(begin) {
if (this.playStatus) {
if (dillPath.length) {
// 筛选符合条件的车
let currentFrame = dillPath[0].carInfo;
if (this.filterType) {
currentFrame = [];
for (let item of dillPath[0].carInfo) {
let currentCarType = item.extendAttribute.motorBehaviorType
.slice(0, item.extendAttribute.motorBehaviorType.length - 1)
.split(",");
let haveDup = this.hasDuplicate(this.filterType, currentCarType);
if (haveDup) {
currentFrame.push(item);
}
}
}
let allData = this.diff(lastFrame.carInfo || [], currentFrame || []);
this.$emit("timeChange", new Date(dillPath[0].frameTime).getTime());
let timeDiff = 0;
if (!begin) {
timeDiff =
new Date(dillPath[0].frameTime).getTime() -
new Date(lastFrame.frameTime).getTime() >
1000
? 100
: new Date(dillPath[0].frameTime).getTime() -
new Date(lastFrame.frameTime).getTime();
}
timeDiff = timeDiff / this.speed;
lastFrame = Object.assign({}, dillPath[0], {
carInfo: currentFrame,
});
this.addDelUpdateVehicleModels(allData, timeDiff);
dillPath.shift();
if (!dillPath.length) {
window.tb.clear(null, true);
lastFrame = { carInfo: [] };
dillPath = JSON.parse(JSON.stringify(fullTrack));
}
setTimeout(() => {
this.renderPath(false);
}, timeDiff);
}
}
},
setModel(model, options, timeDiff) {
// 如果有目标车辆 则把目标车辆设置为红色
if (
this.eventTargetVehicleGlobalId &&
options.id == this.eventTargetVehicleGlobalId
) {
model.traverse((child) => {
if (child.isMesh && child.name.includes("_19")) {
child.material = child.material.clone();
child.material.color.set("#e43f32");
}
});
}
if (timeDiff === 0) {
model.setCoords([options.longitude, options.latitude]);
} else {
let timeDiffInterval = timeDiff / 4;
let midPoint = turf.midpoint(
[model.userData.data.longitude, model.userData.data.latitude],
[options.longitude, options.latitude]
);
let midBePoint = turf.midpoint(
[model.userData.data.longitude, model.userData.data.latitude],
midPoint.geometry.coordinates
);
let midAfPoint = turf.midpoint(midPoint.geometry.coordinates, [
options.longitude,
options.latitude,
]);
setTimeout(() => {
model.setCoords(midBePoint.geometry.coordinates);
}, timeDiffInterval);
// setTimeout(() => {
// model.setCoords(midPoint.geometry.coordinates);
// }, timeDiffInterval * 2);
setTimeout(() => {
model.setCoords(midAfPoint.geometry.coordinates);
}, timeDiffInterval * 3);
// setTimeout(() => {
// model.setCoords([options.longitude, options.latitude]);
// }, timeDiffInterval * 4);
}
model.userData.data = options;
// 闯红灯的非机动车高亮显示
if (
options.eventType == 25 &&
(options.originalType == 5 || options.originalType == 6)
) {
model.traverse((child) => {
if (child.isMesh && child.name.includes("_19")) {
child.material = child.material.clone();
child.material.color.set("#eb1c27");
}
});
}
model.setRotation({ x: 90, y: 360 - options.courseAngle - 90, z: 0 });
},
addDelUpdateVehicleModels(allData, timeDiff) {
if (window.tb && window.tb.world?.children) {
for (let item of allData) {
// 新增
if (item.dill === "add") {
let model =
vehicleModelTypes[`car${item.originalType}`].duplicate();
// 如果有目标车辆 则把目标车辆设置为红色
if (
this.eventTargetVehicleGlobalId &&
item.id == this.eventTargetVehicleGlobalId
) {
map?.setCenter([item.longitude, item.latitude]);
model.traverse((child) => {
if (child.isMesh && child.name.includes("_19")) {
child.material = child.material.clone();
child.material.color.set("#e43f32");
}
});
}
model.setCoords([item.longitude, item.latitude]);
model.userData.data = item;
model.setRotation({ x: 90, y: 360 - item.courseAngle - 90, z: 0 });
window.tb.add(model, item.id);
}
if (item.dill === "del") {
window.tb.clear(item.id, true);
}
if (item.dill === "com") {
for (let model of window.tb.world.children) {
if (model.userData.data?.id === item.id) {
this.setModel(model, item, timeDiff);
}
}
}
}
}
},
diff(oldData, newData) {
const add = newData.filter((e) => !oldData.find((c) => c.id === e.id));
const del = oldData.filter((e) => !newData.find((c) => c.id === e.id));
const com = newData.filter((e) => oldData.find((c) => c.id === e.id));
add.forEach((a) => (a.dill = "add"));
del.forEach((d) => (d.dill = "del"));
com.forEach((c) => (c.dill = "com"));
return [...add, ...del, ...com];
},
destroyMap() {
map?.remove();
map = null;
},
},
computed: {},
beforeDestroy() {
this.$EventBus.$off("fullTrack");
window.tb.dispose();
map?.remove;
map = null;
},
};
</script>
<template>
<div class="headerTitle">
{{ title }}
</div>
</template>
<script>
export default {
data() {
return {
title: "蒙自交通运行态势监测",
};
},
};
</script>
<style lang="less" scoped>
.headerTitle {
position: absolute;
left: 0;
height: 240px;
top: 0;
z-index: 2;
font-size: 54px;
font-weight: 600;
color: white;
line-height: 100px;
letter-spacing: 4px;
text-align: center;
width: 100%;
background-image: url("../assets/images/topIndex.png");
background-size: 100% 100%;
}
</style>
<template>
<msg-card-vue :title="title">
<div class="cardInner">
<div class="optionItem" v-for="item of dangerOptions" :key="item">
<div class="leftIcon" :class="`danger${item.objType}`"></div>
<div class="rightDetail">
<div class="rightInner">
<div class="license">{{ item.eventName }}</div>
<div
v-for="(objValue, key) of item"
:key="key"
v-show="!['objType', 'eventName'].includes(key)"
>
{{ keyMapTranslate(key) }} : {{ objValue }}
</div>
</div>
</div>
</div>
</div>
</msg-card-vue>
</template>
<script>
import msgCardVue from "../msg-card.vue";
export default {
name: "leftBottom",
components: {
msgCardVue,
},
data() {
return {
ownChartData: [],
dangerOptions: [ // objType eventName 必须字段
{
objType: 2, // 1非机动车 2行人
eventName: "不走斑马线、斜穿路口",
timeBetween: "2023-04-10 01:22:12 ~ 01:24:28",
duration: "40 s",
location: "湘江中路与劳动西路交叉口",
},
{
objType: 1,
eventName: "斜穿路口",
timeBetween: "2023-04-10 10:22:12 ~ 10:22:20",
duration: "8 s",
location: "湘江中路与人民西路交叉口",
},
{
objType: 2,
eventName: "不走斑马线",
timeBetween: "2023-04-09 12:22:12 ~ 12:22:26",
duration: "14 s",
location: "湘江中路与人民西路交叉口",
},
],
};
},
computed: {},
watch: {},
props: {
title: {
type: String,
default: () => {
return "默认标题";
},
},
chartData: {
type: Array,
default: () => {
return [
{ a: "1", b: "2" },
{ a: "1", b: "2" },
];
},
},
colors: {
type: Array,
default: () => {
return ["#ffffff"];
},
},
keyMap: {
type: Object,
default: () => {
return {};
},
},
},
mounted() {
this.ownChartData = this.chartData;
},
created() {},
methods: {
keyMapTranslate(key) {
if(this.keyMap[key]) return this.keyMap[key]
return key;
},
convertKvToArray(obj) {
let array = [];
for (let key in obj) {
array.push({ name: this.keyMapTranslate(key), value: obj[key] });
}
return array;
},
},
};
</script>
<style lang="less" scoped>
.cardInner {
display: flex;
flex-direction: column;
padding: 10px;
.optionItem {
display: flex;
justify-content: space-around;
margin-bottom: 20px;
min-height: 120px;
align-items: center;
/* 四个角 */
.leftIcon {
width: 83px;
height: 84px;
background-size: 100% 100%;
}
.rightDetail {
width: calc(100% - 83px);
height: 100%;
}
.danger1 {
background-image: url("../../assets/images/danger_bicycle.png");
}
.danger2 {
background-image: url("../../assets/images/danger_passerby.png");
}
.rightInner {
padding: 8px 23px;
font-size: 16px;
color: rgba(222, 234, 255, 1);
// background-image: url("../../../assets/images/sc/textBorderBack.png");
background-size: 100% 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
}
span {
display: inline-block;
margin-right: 25px;
}
.license {
color: white;
font-size: 18px;
font-weight: 500;
margin-bottom: 8px;
}
}
}
</style>
<template>
<msg-card-vue :title="title">
<div class="totalTitle">
总量
<div class="titleNum">{{ totalNum }}</div>
</div>
<div
style="position: relative; width: 100%; height: 100%"
:id="chartId"
></div>
</msg-card-vue>
</template>
<script>
import msgCardVue from "../msg-card.vue";
export default {
name: "leftBottom",
components: {
msgCardVue,
},
data() {
return {
chart: null,
chartId: "",
ownChartData:[]
};
},
computed: {
totalNum() {
let total = 0;
for (let key in this.chartData) {
total += this.chartData[key];
}
return total;
},
},
watch: {
colors() {
this.refreshChart();
},
},
props: {
title: {
type: String,
default: () => {
return "默认标题";
},
},
chartData: {
type: Array,
default: () => {
return {
"rose1": 30,
"rose2": 30,
"rose3": 30,
"rose4": 30,
"rose5": 30,
"rose6": 30,
"rose7": 30,
"rose8": 30,
};
},
},
colors: {
type: Array,
default: () => {
return ["#ffffff"];
},
},
keyMap:{
type: Object,
default: () => {
return {}
}
}
},
mounted() {
this.ownChartData = this.chartData
this.$nextTick(() => {
this.refreshChart();
});
},
created() {
this.chartId = this.buildShortUUID();
},
methods: {
keyMapTranslate(key) {
return this.keyMap[key] || key;
},
buildShortUUID() {
const time = Date.now();
const random = Math.floor(Math.random() * 1000000000);
return random + String(time);
},
convertKvToArray(obj){
let array = []
for(let key in obj){
array.push({name: this.keyMapTranslate(key), value: obj[key]})
}
return array
},
refreshChart() {
if (this.chart) {
this.chart.clear();
} else {
let chartDom = document.getElementById(this.chartId);
this.chart = echarts.init(chartDom);
const watchChartWc = new ResizeObserver(() => {
this.chart?.resize();
});
// 使用observe开启监听, onObserve可以取消监听
watchChartWc.observe(document.getElementById(this.chartId));
window.addEventListener("resize", () => this.chart?.resize());
}
let chartSeries = this.convertKvToArray(this.ownChartData)
this.chart.setOption(
{
color: this.colors,
legend: {
show: false,
},
toolbox: {
show: false,
},
series: [
{
name: "Nightingale Chart",
type: "pie",
radius: ["50%", "80%"],
center: ["50%", "50%"],
roseType: "radius",
label: {
color: "inherit",
borderWidth: 0,
rich: {
num: {
align: "center",
height: 20,
},
},
formatter: "{c} ({d}%)\n {num|{b}}",
},
// labelLine: {
// length: 10,
// length2: 0,
// },
data: chartSeries,
},
],
},
true
);
},
},
};
</script>
<style lang="less" scoped>
.totalTitle {
position: absolute;
left: 50%;
top: 50%;
color: white;
transform: translate(-50%);
z-index: 3;
text-align: center;
.titleNum {
font-size: 20px;
font-weight: 700;
color: #80ffff;
}
}
</style>
<template>
<msg-card-vue :title="title">
<div style="height: 60px" class="listData">
<div class="listDataItem" v-for="item of chartData.listData" :key="item">
{{ item.key }} {{ item.value }}
</div>
</div>
<div style="width: 100%; height: calc(100% - 60px)" :id="chartId"></div>
</msg-card-vue>
</template>
<script>
import msgCardVue from "../msg-card.vue";
export default {
name: "leftCenter",
components: {
msgCardVue,
},
data() {
return {
chart: null,
chartId: "",
ownChartData: {},
};
},
props: {
api: {
type: String,
default: () => {
return "";
},
},
chartType: {
type: String,
default: () => {
return "bar";
},
},
title: {
type: String,
default: () => {
return "默认标题";
},
},
keyMap: {
type: Object,
default: () => {
return {};
},
},
chartData: {
type: Object,
default: () => {
return {
headerCount: 500,
keyList: ["picNumber", "VictType", "CrossNumber"],
list: [
{ picNumber: "云G12345", VictType: 2, CrossNumber: 26 },
{ picNumber: "云G12345", VictType: 2, CrossNumber: 26 },
{ picNumber: "云G12345", VictType: 2, CrossNumber: 26 },
],
dataList: {
serialList: ["time1", "time2", "time3", "time4", "time5"],
valueList: [
{ key1: 1, key2: 3 },
{ key1: 2, key2: 4 },
{ key1: 1, key2: 3 },
{ key1: 2, key2: 4 },
{ key1: 1, key2: 3 },
],
},
};
},
},
colors: {
type: Array,
default: () => {
return ["#ffffff"];
},
},
legend: {
type: Object,
default: () => {
return {
align: "center",
show: true,
};
},
},
},
watch: {
colors() {
this.refreshChart();
},
chartType() {
this.refreshChart();
},
legend: {
handler() {
this.refreshChart();
},
deep: true,
},
keyMap: {
handler() {
this.refreshChart();
},
deep: true,
},
},
mounted() {
this.ownChartData = this.chartData;
this.$nextTick(() => {
if (this.api) {
setTimeout(() => {
this.refreshChart();
}, 500);
} else {
this.refreshChart();
}
});
},
created() {
this.chartId = this.buildShortUUID();
},
methods: {
keyMapTranslate(key) {
return this.keyMap[key] || key;
},
hexToRgb(hex) {
let r = parseInt(hex.substring(1, 3), 16);
let g = parseInt(hex.substring(3, 5), 16);
let b = parseInt(hex.substring(5, 7), 16);
return { r, g, b };
},
buildShortUUID() {
const time = Date.now();
const random = Math.floor(Math.random() * 1000000000);
return random + String(time);
},
refreshChart() {
if (this.chart) {
this.chart.clear();
} else {
let chartDom = document.getElementById(this.chartId);
this.chart = echarts.init(chartDom);
const watchChartWc = new ResizeObserver(() => {
this.chart?.resize();
});
// 使用observe开启监听, onObserve可以取消监听
watchChartWc.observe(document.getElementById(this.chartId));
window.addEventListener("resize", () => this.chart?.resize());
}
let legendData = [];
// 聚合组装数据
let seriesData = [];
let chartData = this.ownChartData.dataList;
let xData = chartData.serialList;
for (let key in chartData.valueList[0]) {
legendData.push(key);
seriesData.push({ name: key, values: [] });
}
for (let valueItem of chartData.valueList) {
for (let valueKey in valueItem) {
for (let series of seriesData) {
if (series.name == valueKey) {
series.values.push(valueItem[valueKey]);
}
}
}
}
// 转 echarts series
let chartSeries = [];
for (let i = 0; i < seriesData.length; i++) {
let itemColor = this.colors[i] || "#ffffff";
let rgb = this.hexToRgb(itemColor);
chartSeries.push({
name: seriesData[i].name,
data: seriesData[i].values,
type: this.chartType,
areaStyle: {
color: {
//线性渐变
type: "linear",
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.4)`, // 0% 处的颜色
},
{
offset: 0.6,
color: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b},0)`, // 100% 处的颜色
},
],
global: false, // 缺省为 false
},
},
smooth: true,
symbol: "none",
});
}
// setOption
this.chart.setOption(
{
legend: {
data: legendData,
show: this.legend.show,
left: this.legend.align,
textStyle: {
color: "#fff",
},
},
tooltip: {},
color: this.colors,
grid: {
top: 40,
bottom: 10,
left: 10,
right: 20,
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: true,
data: xData,
},
yAxis: {
name: "",
axisTick: {
show: false,
},
axisLine: {
show: false,
},
splitLine: {
show: true,
lineStyle: {
type: "dashed",
color: "rgba(255, 255, 255, 0.2)",
},
},
type: "value",
axisLabel: {
color: "rgba(126, 139, 166, 1)",
},
},
series: chartSeries,
},
true
);
},
},
};
</script>
<style lang="less" scoped>
.listData {
display: flex;
justify-content: space-between;
.listDataItem {
flex: 0 0 30%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-image: url("../../assets/images/indexBack.png");
background-position-y: center;
background-size: 100% 100%;
background-repeat: no-repeat;
font-size: 16px;
font-weight: 700;
color: rgba(255, 255, 255, 0.8);
}
}
</style>
<template>
<msg-card-vue :title="title">
<div style="height: 60px" class="listData">
<div class="listDataItem" v-for="item of chartData.listData" :key="item">
{{ item.key }} {{ item.value }}
</div>
</div>
<div style="width: 100%; height: calc(100% - 60px)" :id="chartId"></div>
</msg-card-vue>
</template>
<script>
import msgCardVue from "../msg-card.vue";
export default {
name: "leftTop",
components: {
msgCardVue,
},
data() {
return {
chart: null,
chartId: "",
ownChartData: {},
};
},
props: {
api: {
type: String,
default: () => {
return "";
},
},
chartType: {
type: String,
default: () => {
return "bar";
},
},
title: {
type: String,
default: () => {
return "默认标题";
},
},
keyMap: {
type: Object,
default: () => {
return {};
},
},
chartData: {
type: Object,
default: () => {
return {
headerCount: 500,
keyList: ["picNumber", "VictType", "CrossNumber"],
list: [
{ picNumber: "云G12345", VictType: 2, CrossNumber: 26 },
{ picNumber: "云G12345", VictType: 2, CrossNumber: 26 },
{ picNumber: "云G12345", VictType: 2, CrossNumber: 26 },
],
dataList: {
serialList: ["time1", "time2", "time3", "time4", "time5"],
valueList: [
{ key1: 1, key2: 3 },
{ key1: 2, key2: 4 },
{ key1: 1, key2: 3 },
{ key1: 2, key2: 4 },
{ key1: 1, key2: 3 },
],
},
};
},
},
colors: {
type: Array,
default: () => {
return ["#ffffff"];
},
},
legend: {
type: Object,
default: () => {
return {
align: "center",
show: true,
};
},
},
},
watch: {
colors() {
this.refreshChart();
},
chartType() {
this.refreshChart();
},
legend: {
handler() {
this.refreshChart();
},
deep: true,
},
keyMap: {
handler() {
this.refreshChart();
},
deep: true,
},
},
mounted() {
this.ownChartData = this.chartData;
this.$nextTick(() => {
if (this.api) {
setTimeout(() => {
this.refreshChart();
}, 500);
} else {
this.refreshChart();
}
});
},
created() {
this.chartId = this.buildShortUUID();
},
methods: {
keyMapTranslate(key) {
return this.keyMap[key] || key;
},
hexToRgb(hex) {
let r = parseInt(hex.substring(1, 3), 16);
let g = parseInt(hex.substring(3, 5), 16);
let b = parseInt(hex.substring(5, 7), 16);
return { r, g, b };
},
buildShortUUID() {
const time = Date.now();
const random = Math.floor(Math.random() * 1000000000);
return random + String(time);
},
refreshChart() {
if (this.chart) {
this.chart.clear();
} else {
let chartDom = document.getElementById(this.chartId);
this.chart = echarts.init(chartDom);
const watchChartWc = new ResizeObserver(() => {
this.chart?.resize();
});
// 使用observe开启监听, onObserve可以取消监听
watchChartWc.observe(document.getElementById(this.chartId));
window.addEventListener("resize", () => this.chart?.resize());
}
let legendData = [];
// 聚合组装数据
let seriesData = [];
let chartData = this.ownChartData.dataList;
let xData = chartData.serialList;
for (let key in chartData.valueList[0]) {
legendData.push(this.keyMapTranslate(key));
seriesData.push({ name: this.keyMapTranslate(key), values: [] });
}
for (let valueItem of chartData.valueList) {
for (let valueKey in valueItem) {
for (let series of seriesData) {
if (series.name == valueKey) {
series.values.push(valueItem[valueKey]);
}
}
}
}
// 转 echarts series
let chartSeries = [];
for (let se of seriesData) {
chartSeries.push({
name: se.name,
data: se.values,
type: this.chartType,
});
}
// setOption
this.chart.setOption(
{
legend: {
data: legendData,
show: this.legend.show,
left: this.legend.align,
textStyle: {
color: "#fff",
},
},
tooltip: {},
color: this.colors,
grid: {
top: 40,
bottom: 10,
left: 10,
right: 10,
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: true,
data: xData,
},
yAxis: {
name: "",
axisTick: {
show: false,
},
axisLine: {
show: false,
},
splitLine: {
show: true,
lineStyle: {
type: "dashed",
color: "rgba(255, 255, 255, 0.2)",
},
},
type: "value",
axisLabel: {
color: "rgba(126, 139, 166, 1)",
},
},
series: chartSeries,
},
true
);
},
},
};
</script>
<style lang="less" scoped>
.listData {
display: flex;
justify-content: space-between;
.listDataItem {
flex: 0 0 30%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-image: url("../../assets/images/indexBack.png");
background-position-y: center;
background-size: 100% 100%;
background-repeat: no-repeat;
font-size: 16px;
font-weight: 700;
color: rgba(255, 255, 255, 0.8);
}
}
</style>
<template>
<msg-card-vue :title="title">
<div style="height: 30px" class="listData">
<div class="listDataItem" v-for="item of topList" :key="item">
{{ item.title }}
</div>
</div>
<div style="width: 100%; height: calc(100% - 30px)" :id="chartId"></div>
</msg-card-vue>
</template>
<script>
import msgCardVue from "../msg-card.vue";
export default {
name: "rightBottom",
components: {
msgCardVue,
},
computed: {
topList() {
return this.ownChartData.dataLists;
},
},
data() {
return {
chart: null,
chartId: "",
ownChartData: {},
};
},
props: {
api: {
type: String,
default: () => {
return "";
},
},
chartType: {
type: String,
default: () => {
return "bar";
},
},
title: {
type: String,
default: () => {
return "默认标题";
},
},
keyMap: {
type: Object,
default: () => {
return {};
},
},
chartData: {
type: Object,
default: () => {
return {
dataLists: [
{
title: 'qytop5',
serialList: ["企业1", "企业2"],
valueList: [{ wfsl: 1 }, { wfsl: 2 }],
},
{
title:'vehicletop5',
serialList: ["车辆1", "车辆2"],
valueList: [{ wfsl: 1 }, { wfsl: 2 }],
},
],
};
},
},
colors: {
type: Array,
default: () => {
return ["#fff"];
},
},
legend: {
type: Object,
default: () => {
return {
align: "center",
show: true,
};
},
},
},
watch: {
colors() {
this.refreshChart();
},
chartType() {
this.refreshChart();
},
legend: {
handler() {
this.refreshChart();
},
deep: true,
},
keyMap: {
handler() {
this.refreshChart();
},
deep: true,
},
},
mounted() {
this.ownChartData = this.chartData;
this.$nextTick(() => {
if (this.api) {
setTimeout(() => {
this.refreshChart();
}, 500);
} else {
this.refreshChart();
}
});
},
created() {
this.chartId = this.buildShortUUID();
},
methods: {
keyMapTranslate(key) {
return this.keyMap[key] || key;
},
convertKvToArray(obj) {
let array = [];
for (let key in obj) {
array.push({ name: key, value: obj[key] });
}
return array;
},
buildShortUUID() {
const time = Date.now();
const random = Math.floor(Math.random() * 1000000000);
return random + String(time);
},
refreshChart() {
if (this.chart) {
this.chart.clear();
} else {
let chartDom = document.getElementById(this.chartId);
this.chart = echarts.init(chartDom);
const watchChartWc = new ResizeObserver(() => {
this.chart?.resize();
});
// 使用observe开启监听, onObserve可以取消监听
watchChartWc.observe(document.getElementById(this.chartId));
window.addEventListener("resize", () => this.chart?.resize());
}
// 聚合组装数据
let xData = [];
let serialL = JSON.parse(
JSON.stringify(this.ownChartData.dataLists[0].serialList)
);
let serialR = JSON.parse(
JSON.stringify(this.ownChartData.dataLists[1].serialList)
);
for (
let i = 0;
i <
[
...this.ownChartData.dataLists[0].serialList,
...this.ownChartData.dataLists[1].serialList,
].length;
i++
) {
// debugger
if (i % 2 === 0) {
xData.push(serialL[0]);
serialL.shift();
} else {
xData.push(serialR[0]);
serialR.shift();
}
}
let seriesData = [];
let valueL = JSON.parse(
JSON.stringify(this.ownChartData.dataLists[0].valueList)
);
let valueR = JSON.parse(
JSON.stringify(this.ownChartData.dataLists[1].valueList)
);
let key = Object.keys(this.ownChartData.dataLists[0].valueList[0])[0];
for (
let i = 0;
i <
[
...this.ownChartData.dataLists[0].valueList,
...this.ownChartData.dataLists[1].valueList,
].length;
i++
) {
if (i % 2 === 0) {
seriesData.push(valueL[0][key] * -1);
valueL.shift();
} else {
seriesData.push(valueR[0][key]);
valueR.shift();
}
}
// 转 echarts series
let chartSeries = [
{
name: "",
data: seriesData,
type: "bar",
itemStyle: {
color: (params) => {
if (params.dataIndex % 2 === 0) {
return this.colors[0];
} else {
return this.colors[1];
}
},
},
label: {
show: true,
position: "inside",
formatter: (params) => {
return `${params.name} ${
params.data * (params.data < 0 ? -1 : 1)
}`;
},
},
},
];
// setOption
this.chart.setOption(
{
legend: {
show: false,
},
tooltip: {
show: false,
},
grid: {
top: 40,
bottom: 10,
left: 10,
right: 10,
containLabel: true,
},
xAxis: {
type: "value",
axisLine: {
show: false,
},
splitLine: {
show: true,
lineStyle: {
type: "dashed",
color: "rgba(255, 255, 255, 0.2)",
},
},
},
yAxis: {
boundaryGap: true,
data: xData,
type: "category",
name: "",
axisTick: {
show: false,
},
axisLabel: {
show: false,
color: "rgba(126, 139, 166, 1)",
},
},
series: chartSeries,
},
true
);
},
},
};
</script>
<style lang="less" scoped>
.listData {
display: flex;
justify-content: space-between;
.listDataItem {
flex: 0 0 50%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-image: url("../../assets/images/indexBack.png");
background-position-y: center;
background-size: 100% 100%;
background-repeat: no-repeat;
font-size: 16px;
font-weight: 700;
color: rgba(255, 255, 255, 0.8);
}
}
</style>
<template>
<msg-card-vue :title="title">
<div class="full-w-h listContainer">
<div class="child" v-for="item of ownChartData" :key="item">
<div class="childTitle">{{ keyMapTranslate(item.title) }}</div>
<div class="childContainer">
<div
class="sChild"
v-for="(objValue, key, index) of item.list"
:key="index"
>
<div>{{ objValue }}</div>
<div>{{ keyMapTranslate(key) }}</div>
</div>
</div>
</div>
</div>
</msg-card-vue>
</template>
<script>
import msgCardVue from "../msg-card.vue";
export default {
name: "rightCenter",
components: {
msgCardVue,
},
data() {
return {
ownChartData: [],
};
},
props: {
title: {
type: String,
default: () => {
return "默认标题";
},
},
keyMap: {
type: Object,
default: () => {
return {};
},
},
chartData: {
type: Object,
default: () => {
return [
{
title: "lkzt",
list: { ydlk: 1, shlk: 2, yclk: 1, yclkk: 2 },
},
{
title: "ldzt",
list: { ydld: 1, cxld: 2 },
},
];
},
},
},
watch: {
},
mounted() {
this.ownChartData = this.chartData;
},
created() {},
methods: {
keyMapTranslate(key) {
return this.keyMap[key] || key;
},
},
};
</script>
<style lang="less" scoped>
.listContainer {
display: flex;
justify-content: space-between;
background-color: black;
color: white;
.child {
background-color: rgba(255, 255, 255, 0.2);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 4px;
height: 100%;
.childTitle {
height: 10%;
display: flex;
justify-content: center;
align-items: center;
}
.childContainer {
display: flex;
flex-wrap: wrap;
background-color: blanchedalmond;
justify-content: center;
height: 90%;
width: 100%;
padding: 10px;
.sChild {
background-color: black;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
}
}
.child:first-child{
width: 66%;
.sChild{
width: 50%;
}
}
.child:last-child{
width: 33%;
.sChild{
width: 100%;
}
}
}
</style>
<template>
<msg-card-vue :title="title">
<div style="height: 30%" class="listData">
<div class="listDataItem" v-for="item of topList" :key="item">
{{ keyMapTranslate(item.name) }} {{ item.value }}
</div>
</div>
<div style="width: 100%; height: 70%" :id="chartId"></div>
</msg-card-vue>
</template>
<script>
import msgCardVue from "../msg-card.vue";
export default {
name: "rightTop",
components: {
msgCardVue,
},
computed: {
topList() {
return this.convertKvToArray(this.chartData.list);
},
},
data() {
return {
chart: null,
chartId: "",
ownChartData: {},
};
},
props: {
api: {
type: String,
default: () => {
return "";
},
},
chartType: {
type: String,
default: () => {
return "bar";
},
},
title: {
type: String,
default: () => {
return "默认标题";
},
},
keyMap: {
type: Object,
default: () => {
return {};
},
},
chartData: {
type: Object,
default: () => {
return {
headerCount: 500,
keyList: ["key1", "key2"],
list: { jtwf: 12, jtsg: 14 ,sgzd: 20},
dataList: {
serialList: ["time1", "time2", "time3", "time4", "time5"],
valueList: [
{ key1: 1, key2: 3 },
{ key1: 2, key2: 4 },
{ key1: 1, key2: 3 },
{ key1: 2, key2: 4 },
{ key1: 1, key2: 3 },
],
},
};
},
},
colors: {
type: Array,
default: () => {
return ["#ffffff"];
},
},
legend: {
type: Object,
default: () => {
return {
align: "center",
show: true,
};
},
},
},
watch: {
colors() {
this.refreshChart();
},
chartType() {
this.refreshChart();
},
legend: {
handler() {
this.refreshChart();
},
deep: true,
},
keyMap: {
handler() {
this.refreshChart();
},
deep: true,
},
},
mounted() {
this.ownChartData = this.chartData;
this.$nextTick(() => {
if (this.api) {
setTimeout(() => {
this.refreshChart();
}, 500);
} else {
this.refreshChart();
}
});
},
created() {
this.chartId = this.buildShortUUID();
},
methods: {
keyMapTranslate(key) {
return this.keyMap[key] || key;
},
hexToRgb(hex) {
let r = parseInt(hex.substring(1, 3), 16);
let g = parseInt(hex.substring(3, 5), 16);
let b = parseInt(hex.substring(5, 7), 16);
return { r, g, b };
},
convertKvToArray(obj) {
let array = [];
for (let key in obj) {
array.push({ name: key, value: obj[key] });
}
return array;
},
buildShortUUID() {
const time = Date.now();
const random = Math.floor(Math.random() * 1000000000);
return random + String(time);
},
refreshChart() {
if (this.chart) {
this.chart.clear();
} else {
let chartDom = document.getElementById(this.chartId);
this.chart = echarts.init(chartDom);
const watchChartWc = new ResizeObserver(() => {
this.chart?.resize();
});
// 使用observe开启监听, onObserve可以取消监听
watchChartWc.observe(document.getElementById(this.chartId));
window.addEventListener("resize", () => this.chart?.resize());
}
let legendData = [];
// 聚合组装数据
let seriesData = [];
let chartData = this.ownChartData.dataList;
let xData = chartData.serialList;
for (let key in chartData.valueList[0]) {
legendData.push(key);
seriesData.push({ name: key, values: [] });
}
for (let valueItem of chartData.valueList) {
for (let valueKey in valueItem) {
for (let series of seriesData) {
if (series.name == valueKey) {
series.values.push(valueItem[valueKey]);
}
}
}
}
// 转 echarts series
let chartSeries = [];
let rightYAxisName = seriesData[1].name;
for (let i = 0; i < seriesData.length; i++) {
let itemColor = this.colors[i] || "#ffffff";
let rgb = this.hexToRgb(itemColor);
chartSeries.push({
name: seriesData[i].name,
data: seriesData[i].values,
type: this.chartType,
areaStyle: {
color: {
//线性渐变
type: "linear",
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.4)`, // 0% 处的颜色
},
{
offset: 0.6,
color: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b},0)`, // 100% 处的颜色
},
],
global: false, // 缺省为 false
},
},
yAxisIndex: i == 0 ? 0 : 1, // 索引值为0的数据组使用左侧y轴 其他使用右侧y轴
smooth: true,
symbol: "none",
});
if (i > 1) {
rightYAxisName += `/${seriesData[i].name}`;
}
}
let yAxis = [
{
name: chartSeries[0].name,
position: "left",
axisTick: {
show: false,
},
axisLine: {
show: false,
},
splitLine: {
show: true,
lineStyle: {
type: "dashed",
color: "rgba(255, 255, 255, 0.2)",
},
},
type: "value",
axisLabel: {
color: "rgba(126, 139, 166, 1)",
},
},
{
name: rightYAxisName,
position: "right",
axisTick: {
show: false,
},
axisLine: {
show: false,
},
splitLine: {
show: true,
lineStyle: {
type: "dashed",
color: "rgba(255, 255, 255, 0.2)",
},
},
type: "value",
axisLabel: {
color: "rgba(126, 139, 166, 1)",
},
},
];
// setOption
this.chart.setOption(
{
legend: {
data: legendData,
show: this.legend.show,
left: this.legend.align,
textStyle: {
color: "#fff",
},
},
tooltip: {},
color: this.colors,
grid: {
top: 40,
bottom: 10,
left: 10,
right: 10,
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: true,
data: xData,
},
yAxis: yAxis,
series: chartSeries,
},
true
);
},
},
};
</script>
<style lang="less" scoped>
.listData {
display: flex;
justify-content: space-between;
.listDataItem {
flex: 0 0 30%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-image: url("../../assets/images/indexBack.png");
background-position-y: center;
background-size: 100% 100%;
background-repeat: no-repeat;
font-size: 16px;
font-weight: 700;
color: rgba(255, 255, 255, 0.8);
}
}
</style>
<template>
<msg-card-vue :title="title">
<el-table
:header-cell-style="{
backgroundColor: '#012b53',
color: '#cbedff',
borderBottom: 'none',
}"
:data="ownChartData"
:row-class-name="rowClassName"
stripe
style="width: 100%; background-color: transparent"
:cell-style="{
backgroundColor: 'transparent',
borderBottom: 'none',
color: '#cbe1d2',
}"
>
<el-table-column
v-for="(objValue, key) of ownChartData[0]"
:key="key"
align="center"
:prop="key"
:label="keyMapTranslate(key)"
width="auto"
>
</el-table-column>
</el-table>
</msg-card-vue>
</template>
<script>
import msgCardVue from "../msg-card.vue";
export default {
name: "leftBottom",
components: {
msgCardVue,
},
data() {
return {
ownChartData: [],
};
},
computed: {},
watch: {},
props: {
title: {
type: String,
default: () => {
return "默认标题";
},
},
chartData: {
type: Array,
default: () => {
return [
{ a: "1", b: "2" },
{ a: "1", b: "2" },
];
},
},
keyMap: {
type: Object,
default: () => {
return {};
},
},
},
mounted() {
this.ownChartData = this.chartData;
},
created() {},
methods: {
rowClassName(a) {
return a.rowIndex % 2 ? "evenRow" : "oddRow";
},
keyMapTranslate(key) {
if(this.keyMap[key]) return this.keyMap[key]
return key
},
convertKvToArray(obj) {
let array = [];
for (let key in obj) {
array.push({ name: this.keyMapTranslate(key), value: obj[key] });
}
return array;
},
},
};
</script>
<style lang="less" scoped>
::v-deep .evenRow {
background-color: rgba(53, 92, 166, 0.15);
}
::v-deep .oddRow {
background-color: transparent;
}
::v-deep .el-table::before {
height: 0;
}
</style>
<template>
<msg-card-vue :title="title">
<div class="videos_container">
<div
class="video_item"
v-for="item in videoList"
:key="item"
:id="`div${item}`"
>
<div class="video_item_inner"></div>
</div>
</div>
</msg-card-vue>
</template>
<script>
import CameraVideo from "../cameraVideo.vue";
import msgCardVue from "../msg-card.vue";
export default {
name: "leftBottom",
components: {
msgCardVue,
CameraVideo,
},
data() {
return {
};
},
computed: {
videoList() {
return this.chartData;
},
},
watch: {},
props: {
title: {
type: String,
default: () => {
return "默认标题";
},
},
chartData: {
type: Array,
default: () => {
return [1, 2];
},
},
keyMap: {
type: Object,
default: () => {
return {};
},
},
},
mounted() {},
created() {},
methods: {
destroyAll() {
if (this.$refs.videoContainer) {
for (let container of this.$refs.videoContainer) {
container.destroy();
}
}
},
keyMapTranslate(key) {
return this.keyMap[key] || key;
},
buildShortUUID() {
const time = Date.now();
const random = Math.floor(Math.random() * 1000000000);
return random + String(time);
},
convertKvToArray(obj) {
let array = [];
for (let key in obj) {
array.push({ name: this.keyMapTranslate(key), value: obj[key] });
}
return array;
},
},
beforeDestroy() {
this.destroyAll();
},
};
</script>
<style lang="less" scoped>
.videos_container {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
flex-direction: column;
.video_item {
flex: 1;
padding: 6px 0;
.video_item_inner {
border: 1px solid #0099d7;
border-radius: 6px;
height: 100%;
}
}
}
</style>
export default [
{
path: '',
redirect: '/home',
},
// {
// path: '/home',
// component: (resolve) => require(['@/views/home/index'], resolve),
// },
{
path: '/home',
component: (resolve) => require(['@/views/preview/index'], resolve),
},
{
path: '/situation1',
component: (resolve) => require(['@/views/preview/situation1'], resolve),
},
{
path: '/situation2',
component: (resolve) => require(['@/views/preview/situation2'], resolve),
},
{
path: '/situation3',
component: (resolve) => require(['@/views/preview/situation3'], resolve),
},
]
import request from "../utils/request";
export const autoLogin = () =>
request({
url: "/sso/identity/user/autoLogin",
method: "get",
});
export const logout = () =>
request({
url: "/sso/identity/user/logout",
method: "get",
});
export const queryMenu = (data) =>
request({
url: "/design/systemsetting/menu/queryMenusByParentId",
method: 'get',
params: data
});
import request from "../utils/request";
export const login = () =>
request({
url: "/sso/identity/user/login",
method: "post",
headers: {
appVersion: "1.0.0",
uniId: "100",
},
});
export const getMachine = () =>
request({
url: "/sso/identity/user/getMachineCode",
method: "get",
});
export const activation = () =>
request({
url: "/sso/identity/user/activateMachine",
method: "post",
});
Vue.directive("scroll", {
// 当绑定元素插入到 DOM 中。
inserted: function(el, binding) {
let options = binding.expression
? JSON.parse(binding.expression)
: {
axis: "yx",
theme: "dark",
scrollInertia: 300
};
}
});
Vue.directive("drag", {
bind: function(el, binding, vnode, oldVnode) {
let header = el.querySelector(".el-dialog__header") || el;
let body = el.querySelector(".el-dialog") || el;
header.onmousedown = e => {
if (e.button != 0) {
return false;
}
{
if (!body.isInitDrag) {
body.style.cssText = `transform:none;
width:${body.offsetWidth}px;
height:${body.offsetHeight}px;
left:${parseInt(body.offsetWidth / document.body.offsetWidth) + "%"};
top:${body.offsetTop}px;
bottom:"auto"`;
body.isInitDrag = true;
}
}
window.onselectstart = window.ondragstart = () => {
return false;
};
let offsetX = e.clientX;
let offsetY = e.clientY;
let domLeft = parseFloat(body.style.left);
let domTop = parseFloat(body.style.top);
window.onmousemove = _.throttle(e => {
body.style.left = e.clientX - offsetX + domLeft + "px";
body.style.top = e.clientY - offsetY + domTop + "px";
if (parseInt(body.style.top) < 0) {
body.style.top = 0;
}
}, 20);
window.onmouseup = () => {
window.onmouseup = window.onmousemove = window.onselectstart = window.ondragstart = null;
};
};
}
});
import store from './store'
import router from './router'
import './directive'
import App from './App'
import './permission'
Vue.use(ELEMENT, {
size: 'small',
})
// 关闭生产模式的提示
Vue.config.productionTip = false
new Vue({
el: '#app',
store,
router,
render: (h) => h(App),
})
import router from "./router";
import { queryMenu } from "./dao/auth";
const whiteList = [];
router.beforeEach((to, from, next) => {
if (whiteList.includes(to.path)) {
// 在免登录白名单,直接进入
next();
} else {
// const parentId = map_config.SYS_ID;
// const parentId = 'CAFC20296895433784C193457A870DFD';
// queryMenu({ parentId: parentId, isRecursion: true }).then((res) => {
// console.log('res',res);
// let routes = res.content;
// // for (let route of routes) {
// // if (route.url === to.path) {
// next();
// // }
// // }
// });
const parentId = "1783337E5E9547B6A6E99C60443B90A2";
queryMenu({ parentId: parentId, isRecursion: true }).then((res) => {
// console.log('res',res);
// let routes = res.content;
// for (let route of routes) {
// if (route.url === to.path) {
next();
// }
// }
});
}
});
import Router from 'vue-router'
import routes from '../config/routes'
// 注册路由
Vue.use(Router)
// 防止连续点击多次路由报错
let routerPush = Router.prototype.push;
Router.prototype.push = function push(location) {
return routerPush.call(this, location).catch(err => err)
}
let originalReplace = Router.prototype.replace
Router.prototype.replace = function push(location, onResolve, onReject) {
if (onResolve || onReject) return originalReplace.call(this, location, onResolve, onReject)
return originalReplace.call(this, location).catch((err) => err)
}
export default new Router({
routes: [{
path: '/404', name: '404', component: (resolve) => require(['@/views/404/index'], resolve),
}, ...routes]
})
\ No newline at end of file
import Vuex from "vuex";
import app from "./modules/app";
import dataset from "./modules/dataset";
import menudata from "./modules/menudata";
Vue.use(Vuex);
const store = new Vuex.Store({
modules: {
app,
dataset,
menudata
},
getters: {
menu: state => state.app.menu,
}
});
export default store;
const user = {
state: {
menu: []
},
actions: {
}
};
export default user;
const dataset = {
state: {
parentData: JSON.parse(localStorage.getItem("parentData")) || {},
areaHomepage: true,
menuShow: false,
currentTime: '',
showEditor: false,
},
mutations: {
troggleEditorStatus(state){
state.showEditor = !state.showEditor
},
setCurrentTime(state, time) {
state.currentTime = time
},
setParentData(state, parentData) {
localStorage.setItem("parentData", JSON.stringify(parentData));
state.parentData = parentData;
},
setMenuStatus(state, show) {
state.menuShow = show
}
}
};
export default dataset;
\ No newline at end of file
const menudata = {
state: {
pathdata: JSON.parse(localStorage.getItem("pathdata")) || {},
pathActive:localStorage.getItem("pathActive") || "",
pathList:JSON.parse(localStorage.getItem("pathList")) || [],
},
mutations:{
setPathData(state, pathdata){
localStorage.setItem("pathdata", JSON.stringify(pathdata));
state.pathdata = pathdata;
},
setPathActive(state, pathActive){
localStorage.setItem("pathActive", pathActive);
state.pathActive = pathActive;
},
setPathList(state, pathList){
localStorage.setItem("pathList", JSON.stringify(pathList));
state.pathList = pathList;
},
}
};
export default menudata;
\ No newline at end of file
const sessionCache = {
set (key, value) {
if (!sessionStorage) {
return
}
if (key != null && value != null) {
sessionStorage.setItem(key, value)
}
},
get (key) {
if (!sessionStorage) {
return null
}
if (key == null) {
return null
}
return sessionStorage.getItem(key)
},
setJSON (key, jsonValue) {
if (jsonValue != null) {
this.set(key, JSON.stringify(jsonValue))
}
},
getJSON (key) {
const value = this.get(key)
if (value != null) {
return JSON.parse(value)
}
},
remove (key) {
sessionStorage.removeItem(key);
}
}
const localCache = {
set (key, value) {
if (!localStorage) {
return
}
if (key != null && value != null) {
localStorage.setItem(key, value)
}
},
get (key) {
if (!localStorage) {
return null
}
if (key == null) {
return null
}
return localStorage.getItem(key)
},
setJSON (key, jsonValue) {
if (jsonValue != null) {
this.set(key, JSON.stringify(jsonValue))
}
},
getJSON (key) {
const value = this.get(key)
if (value != null) {
return JSON.parse(value)
}
},
remove (key) {
localStorage.removeItem(key);
}
}
export default {
/**
* 会话级缓存
*/
session: sessionCache,
/**
* 本地缓存
*/
local: localCache
}
const ExportJsonExcel = require("js-export-excel");
export function exportExcel(option){
var toExcel = new ExportJsonExcel(option); //new
toExcel.saveExcel(); //保存
}
\ No newline at end of file
// 导出页面为PDF格式
/* eslint-disable */
//不使用JQuery版的
import html2canvas from 'html2canvas';
import JsPDF from 'jspdf';
/**
* @param ele 要生成 pdf 的DOM元素(容器)
* @param padfName PDF文件生成后的文件名字
* */
function downloadPDF(ele, pdfName){
let eleW = ele.offsetWidth;// 获得该容器的宽
let eleH = ele.offsetHeight;// 获得该容器的高
let eleOffsetTop = ele.offsetTop; // 获得该容器到文档顶部的距离
let eleOffsetLeft = ele.offsetLeft; // 获得该容器到文档最左的距离
window.scrollTo(0,eleOffsetTop);
var canvas = document.createElement("canvas");
var abs = 0;
let win_in = document.documentElement.clientWidth || document.body.clientWidth; // 获得当前可视窗口的宽度(不包含滚动条)
let win_out = window.innerWidth; // 获得当前窗口的宽度(包含滚动条)
if(win_out>win_in){
// abs = (win_o - win_i)/2; // 获得滚动条长度的一半
abs = (win_out - win_in)/2; // 获得滚动条宽度的一半
// console.log(a, '新abs');
}
canvas.width = eleW * 2; // 将画布宽&&高放大两倍
canvas.height = eleH * 2;
var context = canvas.getContext("2d");
context.scale(2, 2);
context.translate(-eleOffsetLeft -abs, -eleOffsetTop);
// 这里默认横向没有滚动条的情况,因为offset.left(),有无滚动条的时候存在差值,因此
// translate的时候,要把这个差值去掉
// html2canvas(element).then( (canvas)=>{ //报错
// html2canvas(element[0]).then( (canvas)=>{
html2canvas( ele, {
dpi: 300,
scrollY:eleOffsetTop,
// allowTaint: true, //允许 canvas 污染, allowTaint参数要去掉,否则是无法通过toDataURL导出canvas数据的
useCORS:true //允许canvas画布内 可以跨域请求外部链接图片, 允许跨域请求。
} ).then( (canvas)=>{
var contentWidth = canvas.width;
var contentHeight = canvas.height;
//一页pdf显示html页面生成的canvas高度;
var pageHeight = contentWidth / 592.28 * 841.89;
//未生成pdf的html页面高度
var leftHeight = contentHeight;
//页面偏移
var position = 0;
//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高
var imgWidth = 595.28;
var imgHeight = 595.28/contentWidth * contentHeight;
var pageData = canvas.toDataURL('image/jpeg', 1.0);
var pdf = new JsPDF('', 'pt', 'a4');
//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
//当内容未超过pdf一页显示的范围,无需分页
if (leftHeight < pageHeight) {
//在pdf.addImage(pageData, 'JPEG', 左,上,宽度,高度)设置在pdf中显示;
pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
// pdf.addImage(pageData, 'JPEG', 20, 40, imgWidth, imgHeight);
} else { // 分页
while(leftHeight > 0) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);
leftHeight -= pageHeight;
position -= 841.89;
//避免添加空白页
if(leftHeight > 0) {
pdf.addPage();
}
}
}
//可动态生成
pdf.save(pdfName);
})
}
export default {
downloadPDF
}
\ No newline at end of file
/**
* Created by jiachenpan on 16/11/18.
*/
export function parseTime(time) {
if (time) {
var date = new Date(time)
var year = date.getFullYear()
/* 在日期格式中,月份是从0开始的,因此要加0
* 使用三元表达式在小于10的前面加0,以达到格式统一 如 09:11:05
* */
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate()
var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()
var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()
var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()
// 拼接
return year + '' + month + '' + day + '' + hours + ':' + minutes + ':' + seconds
} else {
return ''
}
}
export function formatTime(time, option) {
time = +time * 1000
const d = new Date(time)
const now = Date.now()
const diff = (now - d) / 1000
if (diff < 30) {
return '刚刚'
} else if (diff < 3600) {
// less 1 hour
return Math.ceil(diff / 60) + '分钟前'
} else if (diff < 3600 * 24) {
return Math.ceil(diff / 3600) + '小时前'
} else if (diff < 3600 * 24 * 2) {
return '1天前'
}
if (option) {
return parseTime(time)
} else {
return (
d.getMonth() +
1 +
'' +
d.getDate() +
'' +
d.getHours() +
'' +
d.getMinutes() +
''
)
}
}
export function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
// 上次被包装函数被调用时间间隔last小于设定时间间隔wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}
return function(...args) {
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
export function isExternal(path) {
return /^(https?:|mailto:|tel:)/.test(path)
}
// 替换邮箱字符
export function regEmail(email) {
if (String(email).indexOf('@') > 0) {
const str = email.split('@')
let _s = ''
if (str[0].length > 3) {
for (var i = 0; i < str[0].length - 3; i++) {
_s += '*'
}
}
var new_email = str[0].substr(0, 3) + _s + '@' + str[1]
}
return new_email
}
// 替换手机字符
export function regMobile(mobile) {
if (mobile.length > 7) {
var new_mobile = mobile.substr(0, 3) + '****' + mobile.substr(7)
}
return new_mobile
}
//根据表名获取id
export function getTableNameGetId(tableName,data){
var obj = {};
switch (tableName) {
case 'rid_cross':
obj = { idFeild: 'inter_id', dataType: 'cross' }
break
case 'rid_rid':
obj = { idFeild: 'rid', dataType: 'rid' }
break
case 'rid_axf_three':
obj = { idFeild: 'forwardroa', dataType: 'link' }
break
case 'rid_lane_obj':
obj = { idFeild: 'laneid', dataType: 'lane' }
break
default:
break
}
return obj
}
import store from '@/store'
/**
* @param {Array} value
* @returns {Boolean}
* @example see @/views/auth/directive.vue
*/
export default function checkPermission(value) {
if (value && value instanceof Array && value.length > 0) {
const roles = store.getters && store.getters.roles
const permissionRoles = value
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
})
if (!hasPermission) {
return false
}
return true
} else {
console.error(`need roles! Like v-permission="['admin','editor']"`)
return false
}
}
import axios from 'axios'
import { tansParams, blobValidate } from "./tools";
import cache from './cache'
import { saveAs } from 'file-saver'
let downloadLoadingInstance;
// 是否显示重新登录
export let isRelogin = { show: false };
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// 超时
timeout: 1000000
})
// request拦截器
service.interceptors.request.use(config => {
// console.log('axios request...', config);
// 是否需要防止数据重复提交
const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
// get请求映射params参数
if (config.method === 'get' && config.params) {
let url = config.url + '?' + tansParams(config.params);
url = url.slice(0, -1);
config.params = {};
config.url = url;
}
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
const requestObj = {
url: config.url,
data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
time: new Date().getTime(),
}
const sessionObj = cache.session.getJSON('sessionObj')
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
cache.session.setJSON('sessionObj', requestObj)
} else {
const s_url = sessionObj.url; // 请求地址
const s_data = sessionObj.data; // 请求数据
const s_time = sessionObj.time; // 请求时间
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
const message = '数据正在处理,请勿重复提交';
console.warn(`[${s_url}]: ` + message)
return Promise.reject(new Error(message))
} else {
cache.session.setJSON('sessionObj', requestObj)
}
}
}
return config
}, error => {
console.log(error)
Promise.reject(error)
})
// 响应拦截器
service.interceptors.response.use(res => {
console.log('axios response...', res);
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 二进制数据则直接返回
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
// 给二进制数据添加对应的请求参数信息,避免接收到不需要的数据
// return { data: res.data, params: JSON.parse(res.config.data) }
return res.data
}
if (code === 401) {
if (!ELEMENT.$_overTime) {
ELEMENT.$_overTime = ELEMENT.Notification.error({
title: "失败",
message: "code 401, 3秒后退出登录",
});
setTimeout(() => {
window.location.href = "/";
}, 3000);
}
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
}
if (code !== 200) {
ELEMENT.Message.error(res.data.message || res.data.status)
} else {
if (res.config.showMsg) {
ELEMENT.Message.success(res.data.message || res.data.status)
}
}
return res.data
})
// 通用下载方法
export function download(url, params, filename) {
downloadLoadingInstance = ELEMENT.Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
return service.get(url, params, {
transformRequest: [(params) => { return tansParams(params) }],
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob'
}).then(async (data) => {
const isLogin = await blobValidate(data);
if (isLogin) {
const blob = new Blob([data])
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
link.click();
// saveAs(blob, filename)
} else {
console.log('isNotBlob');
}
downloadLoadingInstance.close();
}).catch((r) => {
console.error(r)
downloadLoadingInstance.close();
})
}
export function getBlob(url) {
return service.get(url, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: 'blob',
}).then(async (data) => {
const blob = new Blob([data])
return blob
})
}
export default service
/**
* 通用js方法封装处理
* Copyright (c) 2019 ruoyi
*/
// 日期格式化
export function parseTime(time, pattern) {
if (arguments.length === 0 || !time) {
return null
}
const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
time = parseInt(time)
} else if (typeof time === 'string') {
time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['', '', '', '', '', '', ''][value] }
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
// 表单重置
export function resetForm(refName) {
if (this.$refs[refName]) {
this.$refs[refName].resetFields();
}
}
// 添加日期范围
export function addDateRange(params, dateRange, propName) {
let search = params;
search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
dateRange = Array.isArray(dateRange) ? dateRange : [];
if (typeof (propName) === 'undefined') {
search.params['beginTime'] = dateRange[0];
search.params['endTime'] = dateRange[1];
} else {
search.params['begin' + propName] = dateRange[0];
search.params['end' + propName] = dateRange[1];
}
return search;
}
// 回显数据字典
export function selectDictLabel(datas, value) {
if (value === undefined) {
return "";
}
var actions = [];
Object.keys(datas).some((key) => {
if (datas[key].value == ('' + value)) {
actions.push(datas[key].label);
return true;
}
})
if (actions.length === 0) {
actions.push(value);
}
return actions.join('');
}
// 回显数据字典(字符串、数组)
export function selectDictLabels(datas, value, separator) {
if (value === undefined || value.length ===0) {
return "";
}
if (Array.isArray(value)) {
value = value.join(",");
}
var actions = [];
var currentSeparator = undefined === separator ? "," : separator;
var temp = value.split(currentSeparator);
Object.keys(value.split(currentSeparator)).some((val) => {
var match = false;
Object.keys(datas).some((key) => {
if (datas[key].value == ('' + temp[val])) {
actions.push(datas[key].label + currentSeparator);
match = true;
}
})
if (!match) {
actions.push(temp[val] + currentSeparator);
}
})
return actions.join('').substring(0, actions.join('').length - 1);
}
// 字符串格式化(%s )
export function sprintf(str) {
var args = arguments, flag = true, i = 1;
str = str.replace(/%s/g, function () {
var arg = args[i++];
if (typeof arg === 'undefined') {
flag = false;
return '';
}
return arg;
});
return flag ? str : '';
}
// 转换字符串,undefined,null等转化为""
export function parseStrEmpty(str) {
if (!str || str == "undefined" || str == "null") {
return "";
}
return str;
}
// 数据合并
export function mergeRecursive(source, target) {
for (var p in target) {
try {
if (target[p].constructor == Object) {
source[p] = mergeRecursive(source[p], target[p]);
} else {
source[p] = target[p];
}
} catch (e) {
source[p] = target[p];
}
}
return source;
};
/**
* 构造树型结构数据
* @param {*} data 数据源
* @param {*} id id字段 默认 'id'
* @param {*} parentId 父节点字段 默认 'parentId'
* @param {*} children 孩子节点字段 默认 'children'
*/
export function handleTree(data, id, parentId, children) {
let config = {
id: id || 'id',
parentId: parentId || 'parentId',
childrenList: children || 'children'
};
var childrenListMap = {};
var nodeIds = {};
var tree = [];
for (let d of data) {
let parentId = d[config.parentId];
if (childrenListMap[parentId] == null) {
childrenListMap[parentId] = [];
}
nodeIds[d[config.id]] = d;
childrenListMap[parentId].push(d);
}
for (let d of data) {
let parentId = d[config.parentId];
if (nodeIds[parentId] == null) {
tree.push(d);
}
}
for (let t of tree) {
adaptToChildrenList(t);
}
function adaptToChildrenList(o) {
if (childrenListMap[o[config.id]] !== null) {
o[config.childrenList] = childrenListMap[o[config.id]];
}
if (o[config.childrenList]) {
for (let c of o[config.childrenList]) {
adaptToChildrenList(c);
}
}
}
return tree;
}
/**
* 参数处理
* @param {*} params 参数
*/
export function tansParams(params) {
let result = ''
for (const propName of Object.keys(params)) {
const value = params[propName];
var part = encodeURIComponent(propName) + "=";
if (value !== null && value !== "" && typeof (value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
let params = propName + '[' + key + ']';
var subPart = encodeURIComponent(params) + "=";
result += subPart + encodeURIComponent(value[key]) + "&";
}
}
} else {
result += part + encodeURIComponent(value) + "&";
}
}
}
return result
}
// 验证是否为blob格式
export async function blobValidate(data) {
try {
const text = await data.text();
JSON.parse(text);
return false;
} catch (error) {
return true;
}
}
\ No newline at end of file
<template>
<div id="error404">
<div class="wscn-http404">
<div class="pic-404">
<img class="pic-404__parent" :src="img_404" alt="404" />
<img class="pic-404__child left" :src="img_404_cloud" alt="404" />
<img class="pic-404__child mid" :src="img_404_cloud" alt="404" />
<img class="pic-404__child right" :src="img_404_cloud" alt="404" />
</div>
<div class="bullshit">
<div class="bullshit__headline">{{ message }}</div>
<div class="bullshit__info">
请检查您填写的网址是否正确,请点击以下按钮返回主页或者发送错误报告
</div>
<a @click="back" class="bullshit__return-home">返回上一页</a>
<a href="/#/navigation" class="bullshit__return-home">返回主页</a>
</div>
</div>
</div>
</template>
<script>
import img_404 from "@/assets/images/404/404.png";
import img_404_cloud from "@/assets/images/404/404_cloud.png";
export default {
data() {
return {
img_404,
img_404_cloud
};
},
computed: {
message() {
return "迷路了...";
}
},
methods: {
back: function() {
window.history.go(-1);
}
}
};
</script>
<style scoped>
#error404 {
background: #f0f2f5;
margin-top: -20px;
height: 100%;
}
.bullshit__return-home {
margin-right: 20px;
}
.wscn-http404 {
position: relative;
width: 1200px;
margin: 20px auto 60px;
padding: 0 100px;
overflow: hidden;
}
.pic-404 {
position: relative;
float: left;
width: 600px;
padding: 150px 0;
overflow: hidden;
}
.pic-404__parent {
width: 100%;
}
.pic-404__child {
position: absolute;
}
.pic-404.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
.pic-404.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
.pic-404.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 150px 0;
overflow: hidden;
}
.bullshit__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
.bullshit__headline {
font-size: 20px;
line-height: 24px;
color: #1482f0;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
.bullshit__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
.bullshit__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
</style>
<template>
<div class="home_main">
<!--左半部分图表-->
<div class="h_left aside">
<draggable
ref="h_left_container"
class="aside_area"
@change="(e) => sideChange('left', leftBoxes, e)"
:list="leftBoxes"
group="people"
>
<div
class="aside_item"
v-for="(element, index) in leftBoxes"
:style="singleChartHeight(leftBoxes)"
:key="element.name"
@click="editCurrent(element)"
>
<div class="aside_item_inner">
<div style="height: 100%; width: 100%"></div>
<div
class="el-icon-close"
@click="deleteCurrentEl(leftBoxes, index, element)"
></div>
</div>
</div>
</draggable>
</div>
<!--右半部分图表-->
<div class="h_right aside">
<draggable
ref="h_right_container"
class="aside_area"
@change="(e) => sideChange('right', rightBoxes, e)"
:list="rightBoxes"
group="people"
>
<div
class="aside_item"
v-for="(element, index) in rightBoxes"
:style="singleChartHeight(rightBoxes)"
:key="element.name"
@click="editCurrent(element)"
>
<div class="aside_item_inner">
<div style="height: 100%; width: 100%"></div>
<div
class="el-icon-close"
@click="deleteCurrentEl(rightBoxes, index, element)"
></div>
</div>
</div>
</draggable>
</div>
<!--组件库-->
<div class="h_center_warehouse">
<div class="warehouse_title">组件库</div>
<draggable
class="warehouse_modules"
:list="list1"
:clone="cloneWarehouse"
:group="{ name: 'people', pull: 'clone', put: false }"
>
<div class="module_item" v-for="element in list1" :key="element.name">
<div class="item_inner">
<div class="icon" :class="element.iconName"></div>
<div>{{ element.name }}</div>
</div>
</div>
</draggable>
</div>
<SettingPanel
v-if="currentEdit"
@submitColorPicker="submitColorPicker"
@submitTitleName="submitTitleName"
@submitChangeChartType="submitChangeChartType"
@submitLegend="submitLegend"
@submitKeyMap='submitKeyMap'
@replaceRealData='replaceRealData'
:chartType="currentEdit?.realType"
></SettingPanel>
<el-button class="save_button" type='primary' @click="saveConfig">保存页面配置</el-button>
</div>
</template>
<script>
import draggable from "vuedraggable";
import MsgCard from "../../components/msg-card.vue";
// import barChart from "../../components/warehouse/barChart.vue";
// import pieChart from "../../components/warehouse/pieChart.vue";
// import barChartWithList from '../../components/warehouse/barChartWithList.vue'
import SettingPanel from "./settingPanel.vue";
let warehouses = {};
// warehouses["barChart"] = Vue.extend(barChart);
// warehouses["pieChart"] = Vue.extend(pieChart);
// warehouses["barChartWithList"] = Vue.extend(barChartWithList);
let allLoadComponents = {};
let currentEditComponent = null;
let idGlobal = 8;
export default {
name: "home",
components: { MsgCard, draggable, SettingPanel },
computed: {},
data() {
return {
currentEdit: null,
list1: [
{ name: "barChart", id: 1, iconName: "el-icon-s-data" },
{ name: "barChartWithList", id: 1, iconName: "el-icon-s-data" },
{ name: "pieChart", id: 3, iconName: "el-icon-s-help" },
],
leftBoxes: [],
rightBoxes: [],
};
},
mounted() {},
methods: {
submitKeyMap(keymap){
currentEditComponent.$props.keyMap = keymap
},
// 给当前选中组件赋值接口数据
replaceRealData(data){
currentEditComponent.$props.propsData = data
},
saveConfig(){
let compList = []
for(let key in allLoadComponents){
compList.push({
type: key,
config: allLoadComponents[key].$props
})
}
let sort = [...this.leftBoxes,...this.rightBoxes]
console.log('l',this.leftBoxes, this.rightBoxes);
console.log('save...',compList,sort);
},
submitLegend(legend) {
currentEditComponent.$props.legend = legend;
},
submitColorPicker(colorArray) {
console.log(this.leftBoxes);
currentEditComponent.$props.colors = colorArray;
},
submitTitleName(title) {
currentEditComponent.$props.title = title;
},
submitChangeChartType(type) {
currentEditComponent.$props.cType = type;
},
// deep clone
cloneWarehouse(clone) {
return {
id: idGlobal++,
name: `${clone.name}_${idGlobal++}`,
};
},
// uuid
buildShortUUID() {
const time = Date.now();
const random = Math.floor(Math.random() * 1000000000);
return random + String(time);
},
// add 回调
sideChange(side, list, e) {
console.log("callback", e);
if (e.added) {
setTimeout(() => {
let componentName = e.added.element.name;
let componentType = componentName.split("_")[0];
allLoadComponents[componentName] = new warehouses[componentType]({
propsData: {},
});
allLoadComponents[componentName].$mount(
this.$refs[`h_${side}_container`].$el.childNodes[e.added.newIndex]
.childNodes[0].childNodes[0]
);
}, 0);
}
},
deleteCurrentEl(array, index, element) {
event.stopPropagation();
array.splice(index, 1);
this.$nextTick(() => {
if (element.name == this.currentEdit.name) {
this.currentEdit = null;
}
});
},
editCurrent(element) {
let elName = element.name;
currentEditComponent = allLoadComponents[elName];
for (let key in allLoadComponents) {
if (key == elName) {
allLoadComponents[key].$el.style.border = "1px solid white";
} else {
allLoadComponents[key].$el.style.border = "1px solid transparent";
}
}
console.log(element.name);
this.currentEdit = { ...element, realType: element.name.split("_")[0] };
},
singleChartHeight(list) {
if (list.length) {
return `height: ${(1 / list.length) * 100}%`;
} else {
return "height: 100%";
}
},
},
beforeDestroy() {},
};
</script>
<style lang="less" scoped>
.home_main {
position: relative;
height: 100%;
width: 100%;
background-color: #1d3245;
.save_button{
position: absolute;
bottom: 20px;
z-index: 2;
left: 50%;
transform: translateX(-50%);
}
.aside {
box-sizing: border-box;
padding: 8px;
position: absolute;
width: 400px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
top: 0;
background-color: #030b19;
.aside_area {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.aside_item {
width: 100%;
padding: 5px;
.aside_item_inner {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.el-icon-close {
position: absolute;
right: 10px;
top: 5px;
color: white;
cursor: pointer;
font-size: 20px;
line-height: 40px;
}
}
}
}
}
.h_left {
left: 0;
box-shadow: 30px 0px 30px 0px #030b19;
}
.h_right {
right: 0;
box-shadow: -30px 0px 30px 0px #030b19;
}
.h_center_warehouse {
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 0;
width: 50%;
height: 400px;
background-color: #030b19;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
.warehouse_title {
color: white;
height: 50px;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
}
.warehouse_modules {
display: flex;
flex-wrap: wrap;
width: 100%;
height: calc(100% - 50px);
padding: 10px 20px;
overflow-y: auto;
.module_item {
width: 150px;
height: 110px;
padding: 5px;
.item_inner {
border-radius: 6px;
background-color: #1d3245;
color: white;
height: 100%;
width: 100%;
justify-content: center;
align-items: center;
flex-direction: column;
display: flex;
.icon {
font-size: 32px;
}
}
}
}
}
}
</style>
<template>
<div class="h_center_edit">
<div class="options_title">图表外观配置</div>
<div v-if="showTypeSwitcher">
<el-form :inline="true" :model="chartTypeForm" class="chartType-form">
<el-form-item label="图表类型">
<el-radio-group v-model="chartTypeForm.type">
<el-radio label="bar">柱状图</el-radio>
<el-radio label="stackbar">堆叠柱状图</el-radio>
<el-radio label="line">折线图</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="chartTypeFormSubmit"
>应用</el-button
>
</el-form-item>
</el-form>
</div>
<el-form :inline="true" :model="titleForm" class="title-form">
<el-form-item label="图表标题名称">
<el-input v-model="titleForm.titleName"> </el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="titleFormSubmit">应用</el-button>
</el-form-item>
</el-form>
<el-form :inline="true" :model="legendForm" class="legend-form">
<el-form-item label="图例状态">
<el-radio-group v-model="legendForm.show">
<el-radio :label="true">展示</el-radio>
<el-radio :label="false">隐藏</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="图例对齐方式">
<el-select v-model="legendForm.left" placeholder="图例左右对齐方式">
<el-option label="左侧对齐" value="left"></el-option>
<el-option label="居中" value="center"></el-option>
<el-option label="右侧对齐" value="right"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="legendFormSubmit">应用</el-button>
</el-form-item>
</el-form>
<el-form :inline="true" class="color-form">
<el-form-item label="取色盘">
<el-color-picker
@change="colorPickerChange"
v-model="colorPickerValue"
></el-color-picker>
</el-form-item>
<el-form-item label="预览">
<el-tag
style="margin-right: 4px"
v-for="tag in colorTags"
:key="tag"
closable
@close="closeColorTag(tag)"
:color="tag"
>
</el-tag>
<span v-show="colorTags.length == 0">暂无已选颜色...</span>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="colorFormSubmit">应用</el-button>
</el-form-item>
</el-form>
<el-divider></el-divider>
<div class="options_title">数值展示映射</div>
<el-form :inline="true" :model="sourceApiForm" class="sourceApi-form">
<el-form-item label="数据源">
<el-cascader
v-model="sourceApiForm.api"
placeholder="试试搜索: holo"
:options="apiOptions"
filterable
></el-cascader>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="sourceApiFormSubmit">请求</el-button>
</el-form-item>
</el-form>
<div v-if="keyMaps.length">
<div style="margin-bottom: 10px">字段名称映射</div>
<el-row
:gutter="20"
v-for="keyMapItem of keyMaps"
:key="keyMapItem"
class="keymap_row"
>
<el-col :span="10"
><el-input v-model="keyMapItem.key"></el-input
></el-col>
<el-col :span='4'><i class="el-icon-d-arrow-right"/></el-col>
<el-col :span="10"
><el-input v-model="keyMapItem.label"></el-input
></el-col>
</el-row>
<el-button type="primary" @click="submitKeyMap">应用映射</el-button>
</div>
</div>
</template>
<script>
export default {
name: "settingPanel",
props: {
chartType: {
type: String,
default: () => {
return "";
},
},
},
data() {
return {
apiOptions: [
{
value: "zhinan",
label: "指南",
children: [
{
value: "shejiyuanze",
label: "设计原则",
children: [
{
value: "yizhi",
label: "一致",
},
{
value: "fankui",
label: "反馈",
},
{
value: "xiaolv",
label: "效率",
},
{
value: "kekong",
label: "可控",
},
],
},
{
value: "daohang",
label: "导航",
children: [
{
value: "cexiangdaohang",
label: "侧向导航",
},
{
value: "dingbudaohang",
label: "顶部导航",
},
],
},
],
},
],
sourceApiForm: { api: "", method: "" },
colorPickerValue: null,
colorTags: ["#fff"],
legendForm: {
show: true,
left: "center",
},
titleForm: { titleName: "" },
chartTypeForm: { type: "bar" },
keyMaps: [],
};
},
computed: {
showTypeSwitcher() {
return ["barChart", "barChartWithList"].includes(this.chartType);
},
},
methods: {
// 获取接口数据中所有需要映射的字段
getKey(todo) {
if (Object.prototype.toString.call(todo) == "[object Object]") {
for (let key in todo) {
if (key === "key") {
this.keyMaps.push({key: todo[key], label:''});
}
this.getKey(todo[key]);
}
} else if (Object.prototype.toString.call(todo) == "[object Array]") {
for (let arrayItem of todo) {
this.getKey(arrayItem);
}
} else {
return;
}
},
sourceApiFormSubmit() {
setTimeout(() => {
// 模拟接口数据
let data = {
xData: ["1", "2", "3", "4", "5", "6"],
yDatas: [
{ list: [2, 4, 1, 4, 5, 2], key: "111" },
{ list: [1, 5, 3, 7, 3, 7], key: "222" },
],
};
// 字段映射
this.keyMaps = [];
this.getKey(data)
this.$emit("replaceRealData", data);
}, 1000);
},
submitKeyMap(){
this.$emit('submitKeyMap', this.keyMaps)
},
legendFormSubmit() {
this.$emit("submitLegend", JSON.parse(JSON.stringify(this.legendForm)));
},
colorFormSubmit() {
this.$emit(
"submitColorPicker",
JSON.parse(JSON.stringify(this.colorTags))
);
},
chartTypeFormSubmit() {
this.$emit("submitChangeChartType", this.chartTypeForm.type);
},
titleFormSubmit() {
this.$emit("submitTitleName", this.titleForm.titleName);
},
colorPickerChange() {
this.colorTags.push(this.colorPickerValue);
},
closeColorTag(tag) {
this.colorTags.splice(this.colorTags.indexOf(tag), 1);
},
},
};
</script>
<style lang="less" scoped>
.h_center_edit {
width: 40%;
min-height: 120px;
max-height: 50%;
overflow-y: auto;
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
background-color: #030b19;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
text-align: center;
padding: 10px;
.options_title {
font-size: 20px;
font-weight: 700;
color: white;
margin-bottom: 10px;
}
.keymap_row{
display: flex;
align-items: center;
margin-bottom: 10px;
text-align: center;
}
}
::v-deep .el-form-item__label,
::v-deep .el-radio__label,
.h_center_edit {
color: rgba(255, 255, 255, 0.7);
}
::v-deep .el-form-item {
margin-right: 15px;
}
</style>
<template>
<div class="home_container">
<w-map ref="wMap" mapId="preview_map" :mapOptions="mapOptions" />
<div class="home_main">
<!--左半部分图表-->
<div class="h_left aside" v-show="leftBoxes.length">
<div ref="h_left_container" class="aside_area">
<div
class="aside_item"
v-for="(element, index) in leftBoxes"
:style="singleChartHeight(leftBoxes)"
:key="index"
>
<div class="aside_item_inner">
<div style="height: 100%; width: 100%"></div>
</div>
</div>
</div>
</div>
<!--右半部分图表-->
<div class="h_right aside" v-show="rightBoxes.length">
<div ref="h_right_container" class="aside_area">
<div
class="aside_item"
v-for="(element, index) in rightBoxes"
:style="singleChartHeight(rightBoxes)"
:key="index"
>
<div class="aside_item_inner">
<div style="height: 100%; width: 100%"></div>
</div>
</div>
</div>
</div>
</div>
<div class="textareaa" v-show="this.$store.state.dataset.showEditor">
<el-button class="title_button">页面 JSON 编辑</el-button>
<div class="el-icon-close" @click="closeEditor"></div>
<textarea ref="textarea"></textarea>
<el-button class="submit_button" type="primary" @click="applyEditorValue"
>应用</el-button
>
</div>
</div>
</template>
<script>
import wMap from "../../components/map/index.vue";
import "codemirror/lib/codemirror.css";
import "codemirror/theme/panda-syntax.css";
import "codemirror/addon/hint/javascript-hint";
import "codemirror/mode/javascript/javascript";
import "codemirror/addon/hint/show-hint";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/selection/active-line";
import "codemirror/addon/lint/lint";
const CodeMirror = require("codemirror/lib/codemirror");
let warehouses = {};
import draggable from "vuedraggable";
import MsgCard from "../../components/msg-card.vue";
//----------------------------------------------------------------------------------
import leftTop from "../../components/warehouse/leftTop.vue";
warehouses["leftTop"] = Vue.extend(leftTop);
import leftCenter from "../../components/warehouse/leftCenter.vue";
warehouses["leftCenter"] = Vue.extend(leftCenter);
import leftBottom from "../../components/warehouse/leftBottom.vue";
warehouses["leftBottom"] = Vue.extend(leftBottom);
import rightTop from "../../components/warehouse/rightTop.vue";
warehouses["rightTop"] = Vue.extend(rightTop);
import rightCenter from "../../components/warehouse/rightCenter.vue";
warehouses["rightCenter"] = Vue.extend(rightCenter);
import rightBottom from "../../components/warehouse/rightBottom.vue";
warehouses["rightBottom"] = Vue.extend(rightBottom);
//----------------------------------------------------------------------------------
let allLoadComponents = {};
let allChartJson = {
mapConfig: {
options: {
center: [117.09, 36.64],
zoom: 17,
pitch: 60,
bearing: 0,
maxZoom: 21,
},
},
left: [
{
name: "leftTop_9",
componentType: "leftTop",
index: 0,
config: {
dataSourceApi: "",
colors: ["#85d0f6", "#4777f4", "#025d26"],
keyMap: {},
legend: { align: "right", show: true },
title: "关键节点分析",
chartType: "bar",
},
},
{
name: "leftCenter_132",
componentType: "leftCenter",
index: 1,
config: {
dataSourceApi: "",
title: "承载量趋势分析",
colors: ["#35a96e", "#099cb7", "#1a5eb7"],
chartType: "line",
},
},
{
name: "leftBottom_11",
componentType: "leftBottom",
index: 2,
config: {
dataSourceApi: "",
title: "在途车辆车型分布",
colors: ["#ffda00", "#6e40ff", "#ff6700", "#ea346c", "#2966ff"],
keyMap: { rose1: "大巴车" },
},
},
],
right: [
{
name: "rightTop_115",
componentType: "rightTop",
index: 0,
config: {
dataSourceApi: "",
title: "交通事件分析",
chartType: "line",
keyMap: { jtwf: "交通违法", jtsg: "交通事故" },
colors: ["#4777f4", "#00cc39", "#85d0f6"],
},
},
{
name: "rightCenter_19",
componentType: "rightCenter",
index: 1,
config: {
dataSourceApi: "",
title: "交通状态统计",
keyMap: {
lkzt: "路口状态",
},
},
},
{
name: "rightBottom_111",
componentType: "rightBottom",
index: 2,
config: {
dataSourceApi: "",
title: "交通画像",
colors: ["#7f7ffe", "#c280ff"],
},
},
],
};
let map = null;
export default {
name: "home",
components: { MsgCard, draggable, wMap },
computed: {},
data() {
return {
leftBoxes: [],
rightBoxes: [],
coder: null,
showEditor: false,
mapOptions: {},
};
},
mounted() {
this.loadAllComponents();
this.initialize();
},
methods: {
closeEditor() {
this.$store.commit("troggleEditorStatus");
},
applyEditorValue() {
try {
allChartJson = JSON.parse(this.coder.getValue());
this.loadAllComponents();
} catch (error) {
ELEMENT.Message("json格式不正确,请检查");
}
},
initialize() {
// 初始化编辑器实例,传入需要被实例化的文本域对象和默认配置
this.coder = CodeMirror.fromTextArea(this.$refs.textarea, {
line: true,
mode: "application/json", // json数据高亮
theme: "panda-syntax", //设置主题
tabSize: 1,
lineNumbers: true, // 显示行号
cursorHeight: 1, //光标高度,默认是1
autoCloseBrackets: true,
matchBrackets: true, // 括号匹配
lineWrapping: "scroll", // 文字过长时,是换行(wrap)还是滚动(scroll)
showCursorWhenSelecting: true, // 文本选中时显示光标
smartIndent: true, // 智能缩进
completeSingle: true, // 当匹配只有一项的时候是否自动补全
});
console.log("this.coder", this.coder);
this.coder.on("inputRead", () => {
this.coder.showHint();
});
// 编辑器赋值
this.setCodeContent(JSON.stringify(allChartJson, null, 2));
// 支持双向绑定
this.coder.on("change", (coder) => {
this.code = coder.getValue();
if (this.$emit) {
this.$emit("input", this.code);
}
});
},
setCodeContent(val) {
setTimeout(() => {
if (!val) {
this.coder.setValue("");
} else {
this.coder.setValue(val);
}
}, 300);
},
// 从json初始化图表
loadAllComponents() {
map?.remove();
map = null;
this.mapOptions = allChartJson.mapConfig.options;
setTimeout(() => {
map = this.$refs.wMap.initMap();
map.on("style.load", () => {
map.setFog({
color: "#1d3244", // Lower atmosphere
"high-color": "#030b19", // Upper atmosphere
"horizon-blend": 0.2, // Atmosphere thickness (default 0.2 at low zooms)
"space-color": "rgb(11, 11, 25)", // Background color
});
});
}, 0);
this.leftBoxes = [];
this.rightBoxes = [];
allLoadComponents = {};
for (let leftC of allChartJson.left) {
this.leftBoxes.push({ ...leftC, belong: "left" });
}
for (let rightC of allChartJson.right) {
this.rightBoxes.push({ ...rightC, belong: "right" });
}
setTimeout(() => {
for (let item of [...this.leftBoxes, ...this.rightBoxes]) {
let componentName = item.name;
let type = item.componentType;
allLoadComponents[componentName] = new warehouses[type]({
propsData: item.config,
});
allLoadComponents[componentName].$mount(
this.$refs[`h_${item.belong}_container`].childNodes[item.index]
.childNodes[0].childNodes[0]
);
}
}, 0);
},
// 更新json 刷新图表
applyAndRefresh() {
this.leftBoxes = [];
this.rightBoxes = [];
for (let leftC of allChartJson.left) {
this.leftBoxes.push({ ...leftC, belong: "left" });
}
for (let rightC of allChartJson.right) {
this.rightBoxes.push({ ...rightC, belong: "right" });
}
this.$nextTick(() => {
for (let key in allLoadComponents) {
let toDestroy = true;
for (let item of [...this.leftBoxes, ...this.rightBoxes]) {
let componentName = item.name;
if (componentName == key) {
toDestroy = false;
}
let type = item.componentType;
if (allLoadComponents[componentName]) {
for (let key in item.config) {
allLoadComponents[componentName].$props[key] = item.config[key];
}
} else {
allLoadComponents[componentName] = new warehouses[type]({
propsData: item.config,
});
allLoadComponents[componentName].$mount(
this.$refs[`h_${item.belong}_container`].childNodes[item.index]
.childNodes[0].childNodes[0]
);
}
}
if (toDestroy) {
allLoadComponents[key] = null;
}
}
});
},
singleChartHeight(list) {
if (list.length) {
return `height: ${(1 / list.length) * 100}%`;
} else {
return "height: 100%";
}
},
},
beforeDestroy() {},
};
</script>
<style lang="less" scoped>
.home_container {
width: 100%;
display: flex;
::v-deep .CodeMirror {
height: -webkit-fill-available;
}
.textareaa {
z-index: 2;
height: calc(100% - 100px);
width: 400px;
font-size: 16px;
background-color: #030b19;
position: relative;
.el-icon-close {
position: absolute;
right: 10px;
top: 0;
line-height: 50px;
color: white;
font-size: 16px;
cursor: pointer;
}
.submit_button {
height: 50px;
width: 100%;
font-size: 16px;
}
.title_button {
height: 50px;
width: 100%;
font-size: 16px;
pointer-events: none;
border-radius: 0px;
background-color: #008cc8;
border-top: none;
border-left: none;
border-right: none;
border-bottom: 2px solid #0099d7;
color: rgba(255, 255, 255, 0.8);
}
}
}
.home_main {
position: relative;
height: 100%;
flex: 1;
pointer-events: none;
.save_button {
position: absolute;
bottom: 20px;
z-index: 2;
left: 50%;
transform: translateX(-50%);
}
.aside {
box-sizing: border-box;
padding: 8px;
position: absolute;
width: 668px;
height: calc(100% - 80px);
display: flex;
background-image: linear-gradient(
to bottom,
transparent,
#1e3245,
#1e3245,
#1e3245
);
flex-direction: column;
justify-content: space-between;
align-items: center;
top: 80px;
z-index: 3;
.aside_area {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.aside_item {
width: 100%;
padding: 5px;
.aside_item_inner {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
}
}
}
.h_left {
left: 0;
// box-shadow: 30px 0px 30px 0px #030b19;
}
.h_right {
right: 0;
// box-shadow: -30px 0px 30px 0px #030b19;
}
}
</style>
<template>
<div class="home_container">
<!-- <w-map ref="wMap" mapId="preview_map" :mapOptions="mapOptions" /> -->
<div class="home_main">
<!--左半部分图表-->
<div class="h_left aside" v-show="leftBoxes.length">
<div ref="h_left_container" class="aside_area">
<div
class="aside_item"
v-for="(element, index) in leftBoxes"
:style="singleChartHeight(leftBoxes)"
:key="index"
>
<div class="aside_item_inner">
<div style="height: 100%; width: 100%"></div>
</div>
</div>
</div>
</div>
<!--右半部分图表-->
<div class="h_right aside" v-show="rightBoxes.length">
<div ref="h_right_container" class="aside_area">
<div
class="aside_item"
v-for="(element, index) in rightBoxes"
:style="singleChartHeight(rightBoxes)"
:key="index"
>
<div class="aside_item_inner">
<div style="height: 100%; width: 100%"></div>
</div>
</div>
</div>
</div>
</div>
<div class="textareaa" v-show="this.$store.state.dataset.showEditor">
<el-button class="title_button">页面 JSON 编辑</el-button>
<div class="el-icon-close" @click="closeEditor"></div>
<textarea ref="textarea"></textarea>
<el-button class="submit_button" type="primary" @click="applyEditorValue"
>应用</el-button
>
</div>
</div>
</template>
<script>
import "codemirror/lib/codemirror.css";
import "codemirror/theme/panda-syntax.css";
import "codemirror/addon/hint/javascript-hint";
import "codemirror/mode/javascript/javascript";
import "codemirror/addon/hint/show-hint";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/selection/active-line";
import "codemirror/addon/lint/lint";
const CodeMirror = require("codemirror/lib/codemirror");
let warehouses = {};
import draggable from "vuedraggable";
import MsgCard from "../../components/msg-card.vue";
//----------------------------------------------------------------------------------
import leftTop from "../../components/warehouse/leftTop.vue";
warehouses["leftTop"] = Vue.extend(leftTop);
import leftCenter from "../../components/warehouse/leftCenter.vue";
warehouses["leftCenter"] = Vue.extend(leftCenter);
import leftBottom from "../../components/warehouse/leftBottom.vue";
warehouses["leftBottom"] = Vue.extend(leftBottom);
import rightTop from "../../components/warehouse/rightTop.vue";
warehouses["rightTop"] = Vue.extend(rightTop);
import rightCenter from "../../components/warehouse/rightCenter.vue";
warehouses["rightCenter"] = Vue.extend(rightCenter);
import rightBottom from "../../components/warehouse/rightBottom.vue";
warehouses["rightBottom"] = Vue.extend(rightBottom);
import videos from "../../components/warehouse/videos.vue";
warehouses["videos"] = Vue.extend(videos);
//----------------------------------------------------------------------------------
let allLoadComponents = {};
let allChartJson = {
left: [
{
name: "videos_9",
componentType: "videos",
index: 0,
config: {
keyMap: {},
title: "视频",
},
},
],
right: [
],
};
export default {
name: "home",
components: { MsgCard, draggable },
computed: {},
data() {
return {
leftBoxes: [],
rightBoxes: [],
coder: null,
};
},
mounted() {
this.loadAllComponents();
this.initialize();
},
methods: {
closeEditor(){
this.$store.commit('troggleEditorStatus')
},
applyEditorValue() {
try {
allChartJson = JSON.parse(this.coder.getValue());
this.loadAllComponents()
} catch (error) {
ELEMENT.Message("json格式不正确,请检查");
}
},
initialize() {
// 初始化编辑器实例,传入需要被实例化的文本域对象和默认配置
this.coder = CodeMirror.fromTextArea(this.$refs.textarea, {
line: true,
mode: "application/json", // json数据高亮
theme: "panda-syntax", //设置主题
tabSize: 1,
lineNumbers: true, // 显示行号
cursorHeight: 1, //光标高度,默认是1
autoCloseBrackets: true,
matchBrackets: true, // 括号匹配
lineWrapping: "scroll", // 文字过长时,是换行(wrap)还是滚动(scroll)
showCursorWhenSelecting: true, // 文本选中时显示光标
smartIndent: true, // 智能缩进
completeSingle: true, // 当匹配只有一项的时候是否自动补全
});
console.log("this.coder", this.coder);
this.coder.on("inputRead", () => {
this.coder.showHint();
});
// 编辑器赋值
this.setCodeContent(JSON.stringify(allChartJson, null, 2));
// 支持双向绑定
this.coder.on("change", (coder) => {
this.code = coder.getValue();
if (this.$emit) {
this.$emit("input", this.code);
}
});
},
setCodeContent(val) {
setTimeout(() => {
if (!val) {
this.coder.setValue("");
} else {
this.coder.setValue(val);
}
}, 300);
},
// 从json初始化图表
loadAllComponents() {
this.allLoadComponents = {}
this.leftBoxes = [];
this.rightBoxes = [];
for (let leftC of allChartJson.left) {
this.leftBoxes.push({ ...leftC, belong: "left" });
}
for (let rightC of allChartJson.right) {
this.rightBoxes.push({ ...rightC, belong: "right" });
}
setTimeout(() => {
for (let item of [...this.leftBoxes, ...this.rightBoxes]) {
let componentName = item.name;
let type = item.componentType;
allLoadComponents[componentName] = new warehouses[type]({
propsData: item.config,
});
allLoadComponents[componentName].$mount(
this.$refs[`h_${item.belong}_container`].childNodes[item.index]
.childNodes[0].childNodes[0]
);
}
}, 0);
},
singleChartHeight(list) {
if (list.length) {
return `height: ${(1 / list.length) * 100}%`;
} else {
return "height: 100%";
}
},
},
beforeDestroy() {},
};
</script>
<style lang="less" scoped>
.home_container {
width: 100%;
display: flex;
::v-deep .CodeMirror {
height: -webkit-fill-available;
}
.textareaa {
z-index: 2;
height: calc(100% - 100px);
width: 400px;
font-size: 16px;
background-color: #030b19;
position: relative;
.el-icon-close{
position: absolute;
right: 10px;
top: 0;
line-height: 50px;
color: white;
font-size: 16px;
cursor: pointer;
}
.submit_button {
height: 50px;
width: 100%;
font-size: 16px;
}
.title_button {
height: 50px;
width: 100%;
font-size: 16px;
pointer-events: none;
border-radius: 0px;
background-color: #008cc8;
border-top: none;
border-left: none;
border-right: none;
border-bottom: 2px solid #0099d7;
color: rgba(255, 255, 255, 0.8);
}
}
}
.home_main {
position: relative;
height: 100%;
flex: 1;
background-color: #1d3245;
.save_button {
position: absolute;
bottom: 20px;
z-index: 2;
left: 50%;
transform: translateX(-50%);
}
.aside {
box-sizing: border-box;
padding: 8px;
position: absolute;
width: 668px;
height: calc(100% - 100px);
display: flex;
background-image: linear-gradient(
to bottom,
transparent,
#1e3245,
#1e3245,#1e3245
);
flex-direction: column;
justify-content: space-between;
align-items: center;
top: 100px;
z-index: 3;
.aside_area {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.aside_item {
width: 100%;
padding: 5px;
.aside_item_inner {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
}
}
}
.h_left {
left: 0;
// box-shadow: 30px 0px 30px 0px #030b19;
}
.h_right {
right: 0;
// box-shadow: -30px 0px 30px 0px #030b19;
}
}
</style>
<template>
<div class="home_container">
<div class="home_main">
<!--左半部分图表-->
<div class="h_left aside" v-show="leftBoxes.length">
<div ref="h_left_container" class="aside_area">
<div
class="aside_item"
v-for="(element, index) in leftBoxes"
:style="singleChartHeight(leftBoxes)"
:key="index"
>
<div class="aside_item_inner">
<div style="height: 100%; width: 100%"></div>
</div>
</div>
</div>
</div>
<!--右半部分图表-->
<div class="h_right aside" v-show="rightBoxes.length">
<div ref="h_right_container" class="aside_area">
<div
class="aside_item"
v-for="(element, index) in rightBoxes"
:style="singleChartHeight(rightBoxes)"
:key="index"
>
<div class="aside_item_inner">
<div style="height: 100%; width: 100%"></div>
</div>
</div>
</div>
</div>
</div>
<div class="textareaa" v-show="this.$store.state.dataset.showEditor">
<el-button class="title_button">页面 JSON 编辑</el-button>
<div class="el-icon-close" @click="closeEditor"></div>
<textarea ref="textarea"></textarea>
<el-button class="submit_button" type="primary" @click="applyEditorValue"
>应用</el-button
>
</div>
</div>
</template>
<script>
import "codemirror/lib/codemirror.css";
import "codemirror/theme/panda-syntax.css";
import "codemirror/addon/hint/javascript-hint";
import "codemirror/mode/javascript/javascript";
import "codemirror/addon/hint/show-hint";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/selection/active-line";
import "codemirror/addon/lint/lint";
const CodeMirror = require("codemirror/lib/codemirror");
let warehouses = {};
import draggable from "vuedraggable";
import MsgCard from "../../components/msg-card.vue";
//----------------------------------------------------------------------------------
import leftTop from "../../components/warehouse/leftTop.vue";
warehouses["leftTop"] = Vue.extend(leftTop);
import leftCenter from "../../components/warehouse/leftCenter.vue";
warehouses["leftCenter"] = Vue.extend(leftCenter);
import leftBottom from "../../components/warehouse/leftBottom.vue";
warehouses["leftBottom"] = Vue.extend(leftBottom);
import rightTop from "../../components/warehouse/rightTop.vue";
warehouses["rightTop"] = Vue.extend(rightTop);
import rightCenter from "../../components/warehouse/rightCenter.vue";
warehouses["rightCenter"] = Vue.extend(rightCenter);
import rightBottom from "../../components/warehouse/rightBottom.vue";
warehouses["rightBottom"] = Vue.extend(rightBottom);
import videos from "../../components/warehouse/videos.vue";
warehouses["videos"] = Vue.extend(videos);
//----------------------------------------------------------------------------------
let allLoadComponents = {};
let allChartJson = {
left: [
{
name: "videos_9",
componentType: "videos",
index: 0,
config: {
keyMap: {},
title: "可移动基站",
},
},
],
right: [
{
name: "videos_91",
componentType: "videos",
index: 0,
config: {
keyMap: {},
title: "可移动基站",
},
},
],
};
export default {
name: "home",
components: { MsgCard, draggable },
computed: {},
data() {
return {
leftBoxes: [],
rightBoxes: [],
coder: null,
};
},
mounted() {
this.loadAllComponents();
this.initialize();
},
methods: {
closeEditor(){
this.$store.commit('troggleEditorStatus')
},
applyEditorValue() {
try {
allChartJson = JSON.parse(this.coder.getValue());
this.loadAllComponents()
} catch (error) {
ELEMENT.Message("json格式不正确,请检查");
}
},
initialize() {
// 初始化编辑器实例,传入需要被实例化的文本域对象和默认配置
this.coder = CodeMirror.fromTextArea(this.$refs.textarea, {
line: true,
mode: "application/json", // json数据高亮
theme: "panda-syntax", //设置主题
tabSize: 1,
lineNumbers: true, // 显示行号
cursorHeight: 1, //光标高度,默认是1
autoCloseBrackets: true,
matchBrackets: true, // 括号匹配
lineWrapping: "scroll", // 文字过长时,是换行(wrap)还是滚动(scroll)
showCursorWhenSelecting: true, // 文本选中时显示光标
smartIndent: true, // 智能缩进
completeSingle: true, // 当匹配只有一项的时候是否自动补全
});
console.log("this.coder", this.coder);
this.coder.on("inputRead", () => {
this.coder.showHint();
});
// 编辑器赋值
this.setCodeContent(JSON.stringify(allChartJson, null, 2));
// 支持双向绑定
this.coder.on("change", (coder) => {
this.code = coder.getValue();
if (this.$emit) {
this.$emit("input", this.code);
}
});
},
setCodeContent(val) {
setTimeout(() => {
if (!val) {
this.coder.setValue("");
} else {
this.coder.setValue(val);
}
}, 300);
},
// 从json初始化图表
loadAllComponents() {
this.allLoadComponents = {}
this.leftBoxes = [];
this.rightBoxes = [];
for (let leftC of allChartJson.left) {
this.leftBoxes.push({ ...leftC, belong: "left" });
}
for (let rightC of allChartJson.right) {
this.rightBoxes.push({ ...rightC, belong: "right" });
}
setTimeout(() => {
for (let item of [...this.leftBoxes, ...this.rightBoxes]) {
let componentName = item.name;
let type = item.componentType;
allLoadComponents[componentName] = new warehouses[type]({
propsData: item.config,
});
allLoadComponents[componentName].$mount(
this.$refs[`h_${item.belong}_container`].childNodes[item.index]
.childNodes[0].childNodes[0]
);
}
}, 0);
},
singleChartHeight(list) {
if (list.length) {
return `height: ${(1 / list.length) * 100}%`;
} else {
return "height: 100%";
}
},
},
beforeDestroy() {},
};
</script>
<style lang="less" scoped>
.home_container {
width: 100%;
display: flex;
::v-deep .CodeMirror {
height: -webkit-fill-available;
}
.textareaa {
z-index: 2;
height: calc(100% - 100px);
width: 400px;
font-size: 16px;
background-color: #030b19;
position: relative;
.el-icon-close{
position: absolute;
right: 10px;
top: 0;
line-height: 50px;
color: white;
font-size: 16px;
cursor: pointer;
}
.submit_button {
height: 50px;
width: 100%;
font-size: 16px;
}
.title_button {
height: 50px;
width: 100%;
font-size: 16px;
pointer-events: none;
border-radius: 0px;
background-color: #008cc8;
border-top: none;
border-left: none;
border-right: none;
border-bottom: 2px solid #0099d7;
color: rgba(255, 255, 255, 0.8);
}
}
}
.home_main {
position: relative;
height: 100%;
flex: 1;
background-color: #1d3245;
.save_button {
position: absolute;
bottom: 20px;
z-index: 2;
left: 50%;
transform: translateX(-50%);
}
.aside {
box-sizing: border-box;
padding: 8px;
position: absolute;
width: 668px;
height: calc(100% - 100px);
display: flex;
background-image: linear-gradient(
to bottom,
transparent,
#1e3245,
#1e3245,#1e3245
);
flex-direction: column;
justify-content: space-between;
align-items: center;
top: 100px;
z-index: 3;
.aside_area {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.aside_item {
width: 100%;
padding: 5px;
.aside_item_inner {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
}
}
}
.h_left {
left: 0;
// box-shadow: 30px 0px 30px 0px #030b19;
}
.h_right {
right: 0;
// box-shadow: -30px 0px 30px 0px #030b19;
}
}
</style>
<template>
<div class="home_container">
<div class="home_main">
<!--左半部分图表-->
<div class="h_left aside" v-show="leftBoxes.length">
<div ref="h_left_container" class="aside_area">
<div
class="aside_item"
v-for="(element, index) in leftBoxes"
:style="singleChartHeight(leftBoxes)"
:key="index"
>
<div class="aside_item_inner">
<div style="height: 100%; width: 100%"></div>
</div>
</div>
</div>
</div>
<!--右半部分图表-->
<div class="h_right aside" v-show="rightBoxes.length">
<div ref="h_right_container" class="aside_area">
<div
class="aside_item"
v-for="(element, index) in rightBoxes"
:style="singleChartHeight(rightBoxes)"
:key="index"
>
<div class="aside_item_inner">
<div style="height: 100%; width: 100%"></div>
</div>
</div>
</div>
</div>
</div>
<div class="textareaa" v-show="this.$store.state.dataset.showEditor">
<el-button class="title_button">页面 JSON 编辑</el-button>
<div class="el-icon-close" @click="closeEditor"></div>
<textarea ref="textarea"></textarea>
<el-button class="submit_button" type="primary" @click="applyEditorValue"
>应用</el-button
>
</div>
</div>
</template>
<script>
import "codemirror/lib/codemirror.css";
import "codemirror/theme/panda-syntax.css";
import "codemirror/addon/hint/javascript-hint";
import "codemirror/mode/javascript/javascript";
import "codemirror/addon/hint/show-hint";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/selection/active-line";
import "codemirror/addon/lint/lint";
const CodeMirror = require("codemirror/lib/codemirror");
let warehouses = {};
import draggable from "vuedraggable";
import MsgCard from "../../components/msg-card.vue";
//----------------------------------------------------------------------------------
import leftTop from "../../components/warehouse/leftTop.vue";
warehouses["leftTop"] = Vue.extend(leftTop);
import leftCenter from "../../components/warehouse/leftCenter.vue";
warehouses["leftCenter"] = Vue.extend(leftCenter);
import leftBottom from "../../components/warehouse/leftBottom.vue";
warehouses["leftBottom"] = Vue.extend(leftBottom);
import rightTop from "../../components/warehouse/rightTop.vue";
warehouses["rightTop"] = Vue.extend(rightTop);
import rightCenter from "../../components/warehouse/rightCenter.vue";
warehouses["rightCenter"] = Vue.extend(rightCenter);
import rightBottom from "../../components/warehouse/rightBottom.vue";
warehouses["rightBottom"] = Vue.extend(rightBottom);
import videos from "../../components/warehouse/videos.vue";
warehouses["videos"] = Vue.extend(videos);
import tableList from "../../components/warehouse/tableList.vue";
warehouses["tableList"] = Vue.extend(tableList);
import eventList from "../../components/warehouse/eventList.vue";
warehouses["eventList"] = Vue.extend(eventList);
//----------------------------------------------------------------------------------
let allLoadComponents = {};
let allChartJson = {
left: [
{
name: "videos_9",
componentType: "videos",
index: 0,
config: {
keyMap: {},
title: "视频",
},
},
{
name: "list_9",
componentType: "eventList",
index: 1,
config: {
keyMap: {},
title: "listData",
},
},
],
right: [
{
name: "table1",
componentType: "tableList",
index: 0,
config: {
keyMap: {},
title: "table1",
},
},
{
name: "table2",
componentType: "tableList",
index: 1,
config: {
keyMap: {},
title: "table2",
},
},
],
};
export default {
name: "home",
components: { MsgCard, draggable },
computed: {},
data() {
return {
leftBoxes: [],
rightBoxes: [],
coder: null,
};
},
mounted() {
this.loadAllComponents();
this.initialize();
},
methods: {
closeEditor(){
this.$store.commit('troggleEditorStatus')
},
applyEditorValue() {
try {
allChartJson = JSON.parse(this.coder.getValue());
this.loadAllComponents()
} catch (error) {
ELEMENT.Message("json格式不正确,请检查");
}
},
initialize() {
// 初始化编辑器实例,传入需要被实例化的文本域对象和默认配置
this.coder = CodeMirror.fromTextArea(this.$refs.textarea, {
line: true,
mode: "application/json", // json数据高亮
theme: "panda-syntax", //设置主题
tabSize: 1,
lineNumbers: true, // 显示行号
cursorHeight: 1, //光标高度,默认是1
autoCloseBrackets: true,
matchBrackets: true, // 括号匹配
lineWrapping: "scroll", // 文字过长时,是换行(wrap)还是滚动(scroll)
showCursorWhenSelecting: true, // 文本选中时显示光标
smartIndent: true, // 智能缩进
completeSingle: true, // 当匹配只有一项的时候是否自动补全
});
console.log("this.coder", this.coder);
this.coder.on("inputRead", () => {
this.coder.showHint();
});
// 编辑器赋值
this.setCodeContent(JSON.stringify(allChartJson, null, 2));
// 支持双向绑定
this.coder.on("change", (coder) => {
this.code = coder.getValue();
if (this.$emit) {
this.$emit("input", this.code);
}
});
},
setCodeContent(val) {
setTimeout(() => {
if (!val) {
this.coder.setValue("");
} else {
this.coder.setValue(val);
}
}, 300);
},
// 从json初始化图表
loadAllComponents() {
this.allLoadComponents = {}
this.leftBoxes = [];
this.rightBoxes = [];
for (let leftC of allChartJson.left) {
this.leftBoxes.push({ ...leftC, belong: "left" });
}
for (let rightC of allChartJson.right) {
this.rightBoxes.push({ ...rightC, belong: "right" });
}
setTimeout(() => {
for (let item of [...this.leftBoxes, ...this.rightBoxes]) {
let componentName = item.name;
let type = item.componentType;
allLoadComponents[componentName] = new warehouses[type]({
propsData: item.config,
});
allLoadComponents[componentName].$mount(
this.$refs[`h_${item.belong}_container`].childNodes[item.index]
.childNodes[0].childNodes[0]
);
}
}, 0);
},
singleChartHeight(list) {
if (list.length) {
return `height: ${(1 / list.length) * 100}%`;
} else {
return "height: 100%";
}
},
},
beforeDestroy() {},
};
</script>
<style lang="less" scoped>
.home_container {
width: 100%;
display: flex;
::v-deep .CodeMirror {
height: -webkit-fill-available;
}
.textareaa {
z-index: 2;
height: calc(100% - 100px);
width: 400px;
font-size: 16px;
background-color: #030b19;
position: relative;
.el-icon-close{
position: absolute;
right: 10px;
top: 0;
line-height: 50px;
color: white;
font-size: 16px;
cursor: pointer;
}
.submit_button {
height: 50px;
width: 100%;
font-size: 16px;
}
.title_button {
height: 50px;
width: 100%;
font-size: 16px;
pointer-events: none;
border-radius: 0px;
background-color: #008cc8;
border-top: none;
border-left: none;
border-right: none;
border-bottom: 2px solid #0099d7;
color: rgba(255, 255, 255, 0.8);
}
}
}
.home_main {
position: relative;
height: 100%;
flex: 1;
background-color: #1d3245;
.save_button {
position: absolute;
bottom: 20px;
z-index: 2;
left: 50%;
transform: translateX(-50%);
}
.aside {
box-sizing: border-box;
padding: 8px;
position: absolute;
width: 668px;
height: calc(100% - 100px);
display: flex;
background-image: linear-gradient(
to bottom,
transparent,
#1e3245,
#1e3245,#1e3245
);
flex-direction: column;
justify-content: space-between;
align-items: center;
top: 100px;
z-index: 3;
.aside_area {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.aside_item {
width: 100%;
padding: 5px;
.aside_item_inner {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
}
}
}
.h_left {
left: 0;
// box-shadow: 30px 0px 30px 0px #030b19;
}
.h_right {
right: 0;
// box-shadow: -30px 0px 30px 0px #030b19;
}
}
</style>
const path = require('path')
// const pxtovw = require('postcss-px-to-viewport')
const publicPath = '/wj-data-vision-new'
const port = 9300
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = ['js', 'css']
const proxy = {
'/sso': {
target: 'http://10.102.1.182:9000',
},
'/holo': {
target: 'http://10.102.1.182:9000',
},
'/web': {
target: 'http://10.102.1.182:9000',
},
'/design': {
target: 'http://10.102.1.182:9000',
},
'/develop': {
target: 'http://10.102.1.182:9000',
},
'/opt': {
target: 'http://10.102.1.182:9000',
},
'/cdn': {
target: 'http://127.0.0.1:3000',
},
}
module.exports = {
publicPath,
outputDir: 'dist',
assetsDir: 'static',
productionSourceMap: false,
devServer: {
port,
overlay: {
warnings: false,
errors: true,
},
proxy,
},
css: {
loaderOptions: {
// postcss: {
// //给postcss-loader传递选项
// plugins: [
// new pxtovw({
// unitToConvert: 'px', //需要转换的单位,默认为"px";
// viewportWidth: 1920, //设计稿的视口宽度
// unitPrecision: 5, //单位转换后保留的小数位数
// propList: ['*'], //要进行转换的属性列表,*表示匹配所有,!表示不转换
// viewportUnit: 'vw', //转换后的视口单位
// fontViewportUnit: 'vw', //转换后字体使用的视口单位
// selectorBlackList: [], //不进行转换的css选择器,继续使用原有单位
// minPixelValue: 1, //设置最小的转换数值
// mediaQuery: false, //设置媒体查询里的单位是否需要转换单位
// replace: true, //是否直接更换属性值,而不添加备用属性
// exclude: [/node_modules/], //忽略某些文件夹下的文件
// }),
// ],
// },
},
},
configureWebpack: {
resolve: {
alias: {
vue$: 'vue/dist/vue.esm.js',
'@': path.join(__dirname, 'src'),
},
},
plugins: [
new CompressionWebpackPlugin({
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 10240,
minRatio: 0.8,
}),
],
},
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment