Commit 4b7fb010 authored by ninglx's avatar ninglx

引入videojs-flvjs处理不同类型视频直播流 flv待测试

parent b15ff918
!function e(t,r,o){function n(l,f){if(!r[l]){if(!t[l]){var u="function"==typeof require&&require;if(!f&&u)return u(l,!0);if(i)return i(l,!0);var a=new Error("Cannot find module '"+l+"'");throw a.code="MODULE_NOT_FOUND",a}var c=r[l]={exports:{}};t[l][0].call(c.exports,function(e){var r=t[l][1][e];return n(r||e)},c,c.exports,e,t,r,o)}return r[l].exports}for(var i="function"==typeof require&&require,l=0;l<o.length;l++)n(o[l]);return n}({1:[function(e,t,r){(function(e){"use strict";function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function n(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(r,"__esModule",{value:!0});var i=function(){function e(e,t){for(var r=0;r<t.length;r++){var o=t[r];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}return function(t,r,o){return r&&e(t.prototype,r),o&&e(t,o),t}}(),l=function e(t,r,o){null===t&&(t=Function.prototype);var n=Object.getOwnPropertyDescriptor(t,r);if(void 0===n){var i=Object.getPrototypeOf(t);return null===i?void 0:e(i,r,o)}if("value"in n)return n.value;var l=n.get;if(void 0!==l)return l.call(o)},f="undefined"!=typeof window?window.videojs:void 0!==e?e.videojs:null,u=function(e){return e&&e.__esModule?e:{default:e}}(f),a=u.default.getTech("Html5"),c=u.default.mergeOptions||u.default.util.mergeOptions,s={mediaDataSource:{},config:{}},p=function(e){function r(e,n){return t(this,r),e=c(s,e),o(this,(r.__proto__||Object.getPrototypeOf(r)).call(this,e,n))}return n(r,e),i(r,[{key:"setSrc",value:function(e){this.flvPlayer&&(this.flvPlayer.detachMediaElement(),this.flvPlayer.destroy());var t=this.options_.mediaDataSource,r=this.options_.config;t.type=void 0===t.type?"flv":t.type,t.url=e,this.flvPlayer=window.flvjs.createPlayer(t,r),this.flvPlayer.attachMediaElement(this.el_),this.flvPlayer.load()}},{key:"dispose",value:function(){this.flvPlayer&&(this.flvPlayer.detachMediaElement(),this.flvPlayer.destroy()),l(r.prototype.__proto__||Object.getPrototypeOf(r.prototype),"dispose",this).call(this)}}]),r}(a);p.isSupported=function(){return window.flvjs&&window.flvjs.isSupported()},p.formats={"video/flv":"FLV","video/x-flv":"FLV"},p.canPlayType=function(e){return p.isSupported()&&e in p.formats?"maybe":""},p.canPlaySource=function(e,t){return p.canPlayType(e.type)},p.VERSION="0.2.0",u.default.registerTech("Flvjs",p),r.default=p}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[1]);
\ No newline at end of file
!function e(t,r,o){function n(l,f){if(!r[l]){if(!t[l]){var u="function"==typeof require&&require;if(!f&&u)return u(l,!0);if(i)return i(l,!0);var a=new Error("Cannot find module '"+l+"'");throw a.code="MODULE_NOT_FOUND",a}var c=r[l]={exports:{}};t[l][0].call(c.exports,function(e){var r=t[l][1][e];return n(r||e)},c,c.exports,e,t,r,o)}return r[l].exports}for(var i="function"==typeof require&&require,l=0;l<o.length;l++)n(o[l]);return n}({1:[function(e,t,r){(function(e){"use strict";function t(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function n(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(r,"__esModule",{value:!0});var i=function(){function e(e,t){for(var r=0;r<t.length;r++){var o=t[r];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}return function(t,r,o){return r&&e(t.prototype,r),o&&e(t,o),t}}(),l=function e(t,r,o){null===t&&(t=Function.prototype);var n=Object.getOwnPropertyDescriptor(t,r);if(void 0===n){var i=Object.getPrototypeOf(t);return null===i?void 0:e(i,r,o)}if("value"in n)return n.value;var l=n.get;if(void 0!==l)return l.call(o)},f="undefined"!=typeof window?window.videojs:void 0!==e?e.videojs:null,u=function(e){return e&&e.__esModule?e:{default:e}}(f),a=u.default.getTech("Html5"),c=u.default.mergeOptions||u.default.util.mergeOptions,s={mediaDataSource:{},config:{}},p=function(e){function r(e,n){return t(this,r),e=c(s,e),o(this,(r.__proto__||Object.getPrototypeOf(r)).call(this,e,n))}return n(r,e),i(r,[{key:"setSrc",value:function(e){this.flvPlayer&&(this.flvPlayer.detachMediaElement(),this.flvPlayer.destroy());var t=this.options_.mediaDataSource,r=this.options_.config;t.type=void 0===t.type?"flv":t.type,t.url=e,this.flvPlayer=window.flvjs.createPlayer(t,r),this.flvPlayer.attachMediaElement(this.el_),this.flvPlayer.load()}},{key:"dispose",value:function(){this.flvPlayer&&(this.flvPlayer.detachMediaElement(),this.flvPlayer.destroy()),l(r.prototype.__proto__||Object.getPrototypeOf(r.prototype),"dispose",this).call(this)}}]),r}(a);p.isSupported=function(){return window.flvjs&&window.flvjs.isSupported()},p.formats={"video/flv":"FLV","video/x-flv":"FLV"},p.canPlayType=function(e){return p.isSupported()&&e in p.formats?"maybe":""},p.canPlaySource=function(e,t){return p.canPlayType(e.type)},p.VERSION="0.2.0",u.default.registerTech("Flvjs",p),r.default=p}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[1]);
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -15,6 +15,7 @@
type="text/css"
/>
<link href="/cdn/libs/mapbox-gl/mapbox-gl.css" rel="stylesheet" />
<link href="/cdn/libs/videojs/video-js.css" rel="stylesheet" />
<link href="/cdn/libs/threebox/threebox.css" rel="stylesheet" />
<link href="/cdn/libs/Cesium/Widgets/widgets.css" rel="stylesheet" />
<link
......@@ -108,7 +109,13 @@
type="text/javascript"
></script>
<script src="/cdn/libs/turf/turf.min.js" type="text/javascript"></script>
<!-- <script src="/cdn/libs/xgplayer/xgplayer.js" type="text/javascript"></script> -->
<script src="/cdn/libs/xgplayer/xgplayer-browser.js" type="text/javascript"></script>
<script src="/cdn/libs/xgplayer/xgplayer-flv.min.js" type="text/javascript"></script>
<script src="/cdn/libs/xgplayer/xgplayer-hls.min.js" type="text/javascript"></script>
<script src="/cdn/libs/videojs/video.min.js" type="text/javascript"></script>
<script src="/cdn/libs/flvjs/flv.min.js" type="text/javascript"></script>
<script src="/cdn/libs/videojs-flvjs/videojs-flvjs.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"
......
<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="cameraVideo">
<div class="loading-mask" v-show="cameraLoading">
<!-- <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>-->
</div> -->
<video
style="width: 100%; height: 100%"
@loadstart="loadstart($event)"
@canplay="canplay($event)"
class="videoControl"
muted
:id="videoData"
:id="vidId"
ref="myCameraVideoPlayer"
></video>
<!-- <div
style="width: 100%; height: 100%"
class="videoControl"
:id="vidId"
ref="myCameraVideoPlayer"
></div> -->
</div>
</template>
......@@ -26,65 +31,23 @@ export default {
watch: {},
methods: {
canplay() {
console.log('canplay',this.videoData);
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();
},
loadstart() {},
loadVideo() {},
bindEvents() {},
handleErr() {},
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);
// if (this.player) {
// this.player.destroy();
// }
if(this.player){
this.player.pause()
this.player.dispose()
this.player = null
}
},
},
......@@ -97,19 +60,98 @@ export default {
};
},
mounted() {
// console.log('videoData', this.videoData);
this.player = null;
this.supported = flvjs.isSupported();
this.$nextTick(() => {
this.loadVideo();
// this.$nextTick(() => {
// if (this.videoData) {
// if (this.videoData.functionType == "99") {
// this.player = new HlsPlayer({
// id: this.vidId,
// url: this.videoData.videoUrl,
// isLive: true,
// crossOrigin:true,
// autoplay: true,
// // playsinline: true,
// height: "100%",
// volume: 0,
// width: "100%",
// // controls: false,
// // ignores:['time', 'definition', 'error', 'fullscreen', 'i18n', 'loading', 'mobile', 'pc', 'play', 'poster', 'progress', 'replay', 'volume'],
// });
// this.player.once("complete", () => {
// this.$emit("vidCanplay", this.videoData.videoUrl);
// });
// this.player.on('error',e=>{
// console.error('player error hls',e);
// })
// } else {
// this.player = new FlvPlayer({
// id: this.vidId,
// url: this.videoData.videoUrl,
// autoplay: true,
// isLive: true,
// crossOrigin:true,
// volume: 0,
// // playsinline: true,
// height: "100%",
// // controls: false,
// // ignores:['time', 'definition', 'error', 'fullscreen', 'i18n', 'loading', 'mobile', 'pc', 'play', 'poster', 'progress', 'replay', 'volume'],
// width: "100%",
// });
// this.player.once("complete", () => {
// this.$emit("vidCanplay", this.videoData.videoUrl);
// });
// this.player.on('error',e=>{
// console.error('player error flv',e);
// })
// }
// }
// });
this.player = videojs(this.vidId, {
techOrder: ["html5", "flvjs"],
flvjs: {
mediaDataSource: {
cors: true,
withCredentials: false,
},
},
controls: true,
preload: "none",
});
this.interval = setInterval(() => {
if (this.cameraLoading) {
this.handleErr();
} else {
clearInterval(this.interval);
}
}, 5000);
// m3u8
if (this.videoData && this.videoData.functionType == "99") {
setTimeout(() => {
console.log('player',this.player);
this.player.src({
src: this.videoData.videoUrl,
type: "application/x-mpegURL",
});
this.player.load(this.videoData.videoUrl);
this.player.play();
}, 0);
}
// flv
else {
setTimeout(() => {
console.log('player',this.player);
this.player.src({
src: this.videoData.videoUrl,
// type: "video/x-flv",
});
this.player.load(this.videoData.videoUrl);
this.player.play();
}, 0);
}
},
computed: {
vidId() {
return this.videoData.videoUrl
.replaceAll("/", "_")
.replaceAll(".", "_")
.replaceAll("?", "_")
.replaceAll("=", "_")
.replaceAll("&", "_")
.replaceAll(":", "_");
},
},
beforeDestroy() {
this.destroy();
......@@ -132,11 +174,11 @@ export default {
}
.cameraVideo {
position: relative;
// position: relative;
.videoControl {
// display: none;
position: absolute;
// position: absolute;
height: 100%;
width: 100%;
}
......@@ -151,4 +193,13 @@ export default {
top: -14px;
transform: translateX(-50%);
}
::v-deep .vjs-control-bar {
display: none;
}
::v-deep .vjs-big-play-button {
display: none;
}
::v-deep .vjs-control-text {
display: none;
}
</style>
<template>
<div :class="['cameraVideo', border ? 'cameraVideoBorder' : '']">
<!-- <div class="lkqz_0" v-show="videoData==='lkqz_0'">组群路口2<br/>激光雷达点云</div> -->
<span v-show="border" class="top-left"></span>
<span v-show="border" class="top-right"></span>
<span v-show="border" class="bottom-left"></span>
<span v-show="border" class="bottom-right"></span>
<video
@click.prevent="changeSize()"
muted
width="100%"
loop
:class="[reverse ? 'reverse' : '']"
class="videoControl"
:id="videoData"
:ref="videoData"
>
<source :src="url" type="video/mp4" />
您的浏览器不支持 video 属性。
</video>
</div>
</template>
<script>
var scale = 1;
export default {
name: "cameraVideo",
props: ["border", "reverse", "videoData"],
watch: {},
data() {
return {
url: `/cdn/video/kydjz_1.mp4`,
};
},
methods: {
changeSize() {
document.getElementById(this.videoData).classList.add("big");
// 获取div元素
var div = document.getElementsByClassName("big")[0];
div.onwheel = function (e) {
var delta = Math.max(-1, Math.min(1, e.deltaY));
if (delta < 0) {
scale += 0.1;
} else {
scale -= 0.1;
}
div.style.transform = "scale(" + scale + ")";
};
var isDragging = false; // 标记是否处于拖动状态
var initX, initY, mouseX, mouseY; // 记录初始位置和鼠标位置
// 监听鼠标按下事件
div.addEventListener("mousedown", function (e) {
isDragging = true;
initX = div.offsetLeft;
initY = div.offsetTop;
mouseX = e.clientX;
mouseY = e.clientY;
});
// 监听鼠标移动事件
document.addEventListener("mousemove", function (e) {
if (isDragging) {
var dx = e.clientX - mouseX;
var dy = e.clientY - mouseY;
div.style.left = initX + dx + "px";
div.style.top = initY + dy + "px";
}
});
// 监听鼠标释放事件
document.addEventListener("mouseup", function (e) {
isDragging = false;
});
},
setTime(time) {
console.log("视频重置。。。", new Date().getTime() / 1000);
let el = document.getElementById(this.videoData);
// console.log('video 差值 setCurrentTime', time)
el.currentTime = 0;
el.play();
},
},
computed: {},
mounted() {
this.$nextTick(() => {});
},
beforeDestroy() {
console.log("video beforeDestroy");
},
};
</script>
<style lang="less" scoped>
.big {
width: 500px;
height: 500px;
position: absolute;
z-index: 999;
transform: scale(1);
overflow: auto;
}
.reverse {
rotate: 180deg;
}
.cameraVideo {
position: relative;
.videoControl {
position: absolute;
//width: 100%;
width: 100%;
height: 100%;
//width: calc(100% - 5px);
//height: calc(100% - 5px);
padding: 1px;
}
}
.cameraVideoBorder {
border: 1px solid #022950;
}
.top-left {
position: absolute;
left: -2px;
top: -2px;
padding: 6px;
border-style: solid;
border-color: #1889f1;
border-width: 2px 0 0 2px;
}
.top-right {
position: absolute;
right: -2px;
top: -2px;
padding: 6px;
border-style: solid;
border-color: #1889f1;
border-width: 2px 2px 0 0;
}
.bottom-left {
position: absolute;
right: -2px;
bottom: -2px;
padding: 6px;
border-style: solid;
border-color: #1889f1;
border-width: 0 2px 2px 0;
}
.bottom-right {
position: absolute;
left: -2px;
bottom: -2px;
padding: 6px;
border-style: solid;
border-color: #1889f1;
border-width: 0 0 2px 2px;
}
.lkqz_0 {
z-index: 2;
position: absolute;
font-size: 16px;
color: white;
font-weight: 700;
left: 12px;
top: 8px;
}
</style>
......@@ -455,6 +455,7 @@ export default {
align-items: center;
height: 22px;
width: 100%;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
......
......@@ -46,140 +46,147 @@ export default {
});
},
trafficFlow(content, isCross) {
let option = {
dataZoom: {
type: "inside",
startValue: content.flowList[0].list
? content.flowList[0].list.length - 6
: 0,
endValue: content.flowList[0].list.length - 1,
},
color: [
"rgba(97, 72, 255, 1)",
"rgba(83, 193, 136, 1)",
"rgba(145, 85, 255, 1)",
"rgba(2, 160, 179, 1)",
"rgba(49, 138, 245, 1)",
"rgba(78, 184, 236, 1)",
],
tooltip: {
enterable: true,
trigger: "axis",
axisPointer: {
type: "shadow",
if (content.flowList?.length) {
let option = {
dataZoom: {
type: "inside",
startValue: content.flowList[0].list
? content.flowList[0].list.length - 6
: 0,
endValue: content.flowList[0].list.length - 1,
},
formatter: function (params) {
if (params && params.length) {
var str = `<p>${params[0]?.name}</p>`;
for (var i = 0; i < params.length; i++) {
str += `<p>${params[i].seriesName}&nbsp;&nbsp;:&nbsp;&nbsp;${params[i].value}</p>`;
color: [
"rgba(97, 72, 255, 1)",
"rgba(83, 193, 136, 1)",
"rgba(145, 85, 255, 1)",
"rgba(2, 160, 179, 1)",
"rgba(49, 138, 245, 1)",
"rgba(78, 184, 236, 1)",
],
tooltip: {
enterable: true,
trigger: "axis",
axisPointer: {
type: "shadow",
},
formatter: function (params) {
if (params && params.length) {
var str = `<p>${params[0]?.name}</p>`;
for (var i = 0; i < params.length; i++) {
str += `<p>${params[i].seriesName}&nbsp;&nbsp;:&nbsp;&nbsp;${params[i].value}</p>`;
}
var tooltipHtml =
'<div style="max-height:120px;overflow-y:auto">' +
str +
"</div>";
return tooltipHtml;
}
var tooltipHtml =
'<div style="max-height:120px;overflow-y:auto">' +
str +
"</div>";
return tooltipHtml;
}
},
textStyle: {
fontSize: getFontSize(12),
},
confine: true,
},
legend: {
itemGap: getFontSize(16),
itemWidth: getFontSize(12),
itemHeight: getFontSize(12),
textStyle: {
color: "rgba(126, 139, 166, 1)",
fontSize: getFontSize(12),
lineHeight: 16,
},
pageTextStyle: {
color: "rgba(126, 139, 166, 1)",
fontSize: getFontSize(12),
},
pageIconColor: "white",
pageIconSize: getFontSize(12),
pageIconInactiveColor: "rgba(153, 153, 153, 1)",
type: "scroll",
},
grid: {
left: 0,
right: 0,
bottom: 0,
top: "22%",
containLabel: true,
},
xAxis: {
type: "category",
data: content.timeList,
axisLabel: {
fontSize: getFontSize(12),
color: "rgba(126, 139, 166, 1)",
},
textStyle: {
fontSize: getFontSize(12),
},
confine: true,
},
},
yAxis: {
axisTick: {
show: false,
legend: {
itemGap: getFontSize(16),
itemWidth: getFontSize(12),
itemHeight: getFontSize(12),
textStyle: {
color: "rgba(126, 139, 166, 1)",
fontSize: getFontSize(12),
lineHeight: 16,
},
pageTextStyle: {
color: "rgba(126, 139, 166, 1)",
fontSize: getFontSize(12),
},
pageIconColor: "white",
pageIconSize: getFontSize(12),
pageIconInactiveColor: "rgba(153, 153, 153, 1)",
type: "scroll",
},
axisLine: {
show: false,
grid: {
left: 0,
right: 0,
bottom: 0,
top: "22%",
containLabel: true,
},
splitLine: {
show: true,
lineStyle: {
type: "dashed",
color: "rgba(255, 255, 255, 0.2)",
xAxis: {
type: "category",
data: content.timeList,
axisLabel: {
fontSize: getFontSize(12),
color: "rgba(126, 139, 166, 1)",
},
},
type: "value",
axisLabel: {
fontSize: getFontSize(12),
color: "rgba(126, 139, 166, 1)",
},
},
series: (() => {
let series = [];
content.flowList.forEach((flow) => {
series.push({
name: flow.name,
type: "bar",
barWidth: getFontSize(22),
stack: "total",
label: {
show: false,
},
emphasis: {
focus: "series",
yAxis: {
axisTick: {
show: false,
},
axisLine: {
show: false,
},
splitLine: {
show: true,
lineStyle: {
type: "dashed",
color: "rgba(255, 255, 255, 0.2)",
},
data: flow.list,
},
type: "value",
axisLabel: {
fontSize: getFontSize(12),
color: "rgba(126, 139, 166, 1)",
},
},
series: (() => {
let series = [];
content.flowList.forEach((flow) => {
series.push({
name: flow.name,
type: "bar",
barWidth: getFontSize(22),
stack: "total",
label: {
show: false,
},
emphasis: {
focus: "series",
},
data: flow.list,
});
});
});
console.log("series", series);
return series;
})(),
};
if (isCross) {
this.charts.cross = echarts.init(
document.getElementById("trafficFlowCross")
);
this.charts.cross && this.charts.cross.setOption(option);
} else {
this.charts.road = echarts.init(
document.getElementById("trafficFlowRoad")
);
this.charts.road && this.charts.road.setOption(option);
console.log("series", series);
return series;
})(),
};
if (isCross) {
this.charts.cross = echarts.init(
document.getElementById("trafficFlowCross")
);
this.charts.cross && this.charts.cross.setOption(option);
} else {
this.charts.road = echarts.init(
document.getElementById("trafficFlowRoad")
);
this.charts.road && this.charts.road.setOption(option);
}
}
},
getCrossData(init) {
//路口
crossFlow().then((res) => {
console.log("路口流量", res);
this.trafficFlowData.cross = res.content;
res.content.flowList = res.content.flowList.sort((a, b) => {
return a.name.localeCompare(b.name);
});
if (res.content.flowList) {
this.trafficFlowData.cross = res.content;
res.content.flowList = res.content.flowList.sort((a, b) => {
return a.name.localeCompare(b.name);
});
} else {
this.trafficFlowData.cross = [];
}
if (init) {
this.$nextTick(() => {
this.trafficFlow(this.trafficFlowData.cross, true);
......@@ -189,10 +196,14 @@ export default {
//路段
ridFlow().then((res) => {
console.log("路段流量", res);
res.content.flowList = res.content.flowList.sort((a, b) => {
return a.name.localeCompare(b.name);
});
this.trafficFlowData.road = res.content;
if (res.content.flowList) {
res.content.flowList = res.content.flowList.sort((a, b) => {
return a.name.localeCompare(b.name);
});
this.trafficFlowData.road = res.content;
} else {
this.trafficFlowData.road = [];
}
});
},
},
......@@ -202,9 +213,9 @@ export default {
this.getCrossData();
}, 1000 * 60 * 5);
},
beforeDestroy(){
timer && clearInterval(timer)
}
beforeDestroy() {
timer && clearInterval(timer);
},
};
</script>
......
<template>
<div class="videoList" id="situation-video-list" v-show="showVideos">
<div class="videoListBorder">
<div
class="borderItem"
v-for="item of videoListData"
:key="item"
v-show="showVideoItem(item)"
></div>
</div>
<div
class="videoItem"
v-for="item of videoListData"
:key="item"
v-show="showVideoItem(item)"
:id="`div${item}`"
>
<div
class="closeButton el-icon-close"
v-show="getDelShow(item)"
@click="deleteVideo(item)"
></div>
<div
v-show="getUpShow(item)"
class="expandIcon el-icon-arrow-up"
@click="arrowUp(item)"
></div>
<div
v-show="getDownShow(item)"
class="expandIcon el-icon-arrow-down"
@click="arrowDown(item)"
></div>
<camera-video
@vidCanplay="vidCanplay"
:ref="`videoContainer`"
class="videoContainer"
:videoData="item"
/>
</div>
</div>
</template>
<script>
import CameraVideo from "../../../components/Standard/cameraVideo.vue";
export default {
components: {
CameraVideo,
},
name: "videoList",
props: ["videoListData"],
data() {
return {
videoShow: {},
scalesControl: {},
dragControl: {},
scale: 1,
offsetX: 0,
currentUp: "",
offsetY: 0,
currentEl: null,
};
},
mounted() {},
methods: {
getChannel(item) {
let array = item.split("=");
return array[array.length - 1];
},
showVideoItem(item) {
return true;
let channel = this.getChannel(item);
return this.videoShow[channel];
},
vidCanplay(item) {
let channel = this.getChannel(item);
this.videoShow[channel] = true;
},
getUpShow(item) {
return this.currentUp != item && this.currentUp == "";
},
getDelShow(item) {
return (
this.currentUp == item ||
(this.currentUp != item && this.currentUp == "")
);
},
getDownShow(item) {
return this.currentUp == item;
},
arrowUp(item) {
console.log("item", item);
this.$emit("updateCamera", item);
this.currentUp = item;
this.scale = 2;
let el = document.getElementById(`div${item}`);
this.currentEl = el;
let left = document
.getElementById(`situation-video-list`)
.getBoundingClientRect().left;
el.style.position = "absolute";
el.style.left = `${190 - left}px`;
el.style.top = "-550px";
el.style.border = "1px solid #5392bd";
el.style.borderRadius = "6px";
el.style.transform = `scale(${this.scale})`;
el.addEventListener("wheel", this.wheelListener);
this.scalesControl[item] = true;
el.addEventListener("mousedown", this.dragListener);
this.dragControl[item] = true;
},
arrowDown(item) {
if (this.currentUp == item) {
this.$emit("resetCamera");
}
this.currentUp = "";
this.scale = 1;
this.currentEl.style.transform = `scale(${this.scale})`;
this.currentEl.style.left = "unset";
this.currentEl.style.top = "unset";
this.currentEl.style.border = "none";
this.currentEl.style.position = "relative";
//如果当前为 resizable 状态 则 removeEventListener
if (this.scalesControl[item]) {
this.currentEl.removeEventListener("wheel", this.wheelListener);
this.scalesControl[item] = false;
}
// 如果当前为 draggable 状态 则 removeEventListener
if (this.dragControl[item]) {
this.currentEl.removeEventListener("mousedown", this.dragListener);
}
},
// 鼠标滚轮事件监听 控制缩放
wheelListener(e) {
e.preventDefault();
var zoomDelta = e.deltaY < 0 ? 0.1 : -0.1;
this.scale += zoomDelta;
this.scale = Math.max(0.5, this.scale); // 最小缩放为0.5
this.currentEl.style.transform = `scale(${this.scale})`;
},
dragListener(e) {
console.log("e.clientX", e.clientX, this.currentEl.offsetLeft);
this.offsetX = e.clientX - this.currentEl.offsetLeft;
this.offsetY = e.clientY - this.currentEl.offsetTop;
document.addEventListener("mousemove", this.draging);
document.addEventListener("mouseup", this.endDrag);
},
draging(e) {
this.currentEl.style.left = e.clientX - this.offsetX + "px";
this.currentEl.style.top = e.clientY - this.offsetY + "px";
},
endDrag() {
document.removeEventListener("mousemove", this.draging);
document.removeEventListener("mouseup", this.endDrag);
},
destroyAll() {
if (this.$refs.videoContainer) {
for (let container of this.$refs.videoContainer) {
container.destroy();
}
}
},
deleteVideo(item) {
if (this.currentUp == item) {
this.$emit("resetCamera");
}
setTimeout(() => {
for (let container of this.$refs.videoContainer) {
if (container.videoData === item) {
container.destroy();
}
}
this.currentUp = "";
this.$emit("delVideo", item);
}, 0);
},
},
computed: {
showVideos() {
return this.videoListData && this.videoListData.length;
},
},
beforeDestroy() {
this.videoShow = {};
},
};
</script>
<style lang="less" scoped>
/*滚动条样式:谷歌浏览器下*/
::-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;
}
::v-deep .el-icon-close:before {
color: white;
}
.videoList {
position: absolute;
padding: 2px 5px;
bottom: 12px;
width: 80%;
z-index: 13;
border-radius: 6px;
left: 50%;
display: flex;
justify-content: center;
flex-wrap: nowrap;
transform: translateX(-50%);
.videoListBorder {
border: 1px solid rgba(83, 146, 189, 1);
border-radius: 6px;
position: absolute;
left: 50%;
transform: translateX(-50%);
height: 100%;
top: 0;
display: flex;
max-width: 100%;
.borderItem {
width: 230px;
padding: 0 5px;
aspect-ratio: 180/106;
position: relative;
}
}
.videoItem {
width: 230px;
padding: 0 5px;
aspect-ratio: 180/106;
position: relative;
.videoContainer {
height: 100%;
width: 100%;
}
.closeButton {
display: none;
position: absolute;
right: 12px;
top: 8px;
border-radius: 50%;
height: 20px;
width: 20px;
z-index: 9999;
justify-content: center;
align-items: center;
cursor: pointer;
background-color: #019ef9;
}
.closeButton:hover {
background-color: rgba(20, 146, 183, 1);
}
}
.expandIcon {
display: none;
color: #019ef9;
font-size: 16px;
cursor: pointer;
position: absolute;
left: 12px;
top: 10px;
z-index: 11;
}
.videoItem:hover .expandIcon {
display: unset;
}
.videoItem:hover .closeButton {
display: flex;
}
}
</style>
<template>
<div class="videoList" id="situation-video-list" v-show="showVideos">
<div class="videoListBorder">
<div class="videoListBorder" v-show="readyVideos.length">
<div
class="borderItem"
v-for="item of videoListData"
:key="item"
v-show="showVideoItem(item)"
style="display:none"
v-for="item of showCameras"
:key="item.videoUrl"
:id="item.monitorChannel"
></div>
</div>
<div
class="videoItem"
v-for="item of videoListData"
:key="item"
v-show="showVideoItem(item)"
:id="`div${item}`"
style="display:none"
v-for="item of showCameras"
:key="item.videoUrl"
:id="`div${item.monitorChannel}`"
>
<div
class="closeButton el-icon-close"
......@@ -48,10 +49,11 @@ export default {
CameraVideo,
},
name: "videoList",
props: ["videoListData"],
props: ["showCameras"],
data() {
return {
videoShow: {},
readyVideos :[],
scalesControl: {},
dragControl: {},
scale: 1,
......@@ -67,33 +69,34 @@ export default {
let array = item.split("=");
return array[array.length - 1];
},
showVideoItem(item) {
// return true;
let channel = this.getChannel(item);
return this.videoShow[channel];
},
// showVideoItem(item) {
// // return true;
// return this.videoShow[item.monitorChannel];
// },
vidCanplay(item) {
let channel = this.getChannel(item);
this.videoShow[channel] = true;
document.getElementById(`div${item.monitorChannel}`).style.display = 'unset'
document.getElementById(`${item.monitorChannel}`).style.display = 'unset'
this.readyVideos.push(1)
// this.videoShow[item.monitorChannel] = true;
},
getUpShow(item) {
return this.currentUp != item && this.currentUp == "";
return this.currentUp != item.monitorChannel && this.currentUp == "";
},
getDelShow(item) {
return (
this.currentUp == item ||
(this.currentUp != item && this.currentUp == "")
this.currentUp == item.monitorChannel ||
(this.currentUp != item.monitorChannel && this.currentUp == "")
);
},
getDownShow(item) {
return this.currentUp == item;
return this.currentUp == item.monitorChannel;
},
arrowUp(item) {
console.log("item", item);
this.$emit("updateCamera", item);
this.currentUp = item;
this.$emit("updateCamera", item.monitorChannel);
this.currentUp = item.monitorChannel;
this.scale = 2;
let el = document.getElementById(`div${item}`);
let el = document.getElementById(`div${item.monitorChannel}`);
this.currentEl = el;
let left = document
.getElementById(`situation-video-list`)
......@@ -105,12 +108,12 @@ export default {
el.style.borderRadius = "6px";
el.style.transform = `scale(${this.scale})`;
el.addEventListener("wheel", this.wheelListener);
this.scalesControl[item] = true;
this.scalesControl[item.monitorChannel] = true;
el.addEventListener("mousedown", this.dragListener);
this.dragControl[item] = true;
this.dragControl[item.monitorChannel] = true;
},
arrowDown(item) {
if (this.currentUp == item) {
if (this.currentUp == item.monitorChannel) {
this.$emit("resetCamera");
}
this.currentUp = "";
......@@ -121,12 +124,12 @@ export default {
this.currentEl.style.border = "none";
this.currentEl.style.position = "relative";
//如果当前为 resizable 状态 则 removeEventListener
if (this.scalesControl[item]) {
if (this.scalesControl[item.monitorChannel]) {
this.currentEl.removeEventListener("wheel", this.wheelListener);
this.scalesControl[item] = false;
this.scalesControl[item.monitorChannel] = false;
}
// 如果当前为 draggable 状态 则 removeEventListener
if (this.dragControl[item]) {
if (this.dragControl[item.monitorChannel]) {
this.currentEl.removeEventListener("mousedown", this.dragListener);
}
},
......@@ -163,23 +166,25 @@ export default {
}
},
deleteVideo(item) {
if (this.currentUp == item) {
if (this.currentUp == item.monitorChannel) {
this.$emit("resetCamera");
}
setTimeout(() => {
for (let container of this.$refs.videoContainer) {
if (container.videoData === item) {
console.log(container.videoData,item);
if (container.videoData.videoUrl === item.videoUrl) {
container.destroy();
}
}
this.currentUp = "";
this.$emit("delVideo", item);
this.readyVideos.pop()
this.$emit("delVideo", item.videoUrl);
}, 0);
},
},
computed: {
showVideos() {
return this.videoListData && this.videoListData.length;
return this.showCameras && this.showCameras.length;
},
},
beforeDestroy() {
......@@ -216,6 +221,9 @@ export default {
::v-deep .el-icon-close:before {
color: white;
}
::v-deep .vjs-modal-dialog-content{
display: none;
}
.videoList {
position: absolute;
......@@ -229,8 +237,12 @@ export default {
justify-content: center;
flex-wrap: nowrap;
transform: translateX(-50%);
font-size: 14px;
.videoListBorder {
border: 1px solid rgba(83, 146, 189, 1);
background-color: #142c42;
// border-radius: 6px;
border-radius: 6px;
position: absolute;
left: 50%;
......@@ -240,6 +252,7 @@ export default {
display: flex;
max-width: 100%;
.borderItem {
width: 230px;
padding: 0 5px;
aspect-ratio: 180/106;
......@@ -284,7 +297,7 @@ export default {
position: absolute;
left: 12px;
top: 10px;
z-index: 11;
z-index: 9999;
}
.videoItem:hover .expandIcon {
......
This diff is collapsed.
......@@ -37,11 +37,11 @@
<!--视频播放组件-->
<video-list
ref="videoList"
v-if="videoListData.length"
v-if="showCameras.length"
@updateCamera="updateCameraCPB"
@resetCamera="resetCamera"
@delVideo="deleteVideo"
:videoListData="videoListData"
:showCameras="showCameras"
/>
<!--路口详情-->
<cross-detail
......@@ -189,7 +189,7 @@ export default {
dialogVisible: {
crossDetail: false,
},
videoListData: [],
showCameras: [],
timeState: false,
licenseState: true,
layers: {
......@@ -254,11 +254,11 @@ export default {
});
this.getMapParamsTimer = setInterval(() => {
if (map && map.getCenter()) {
console.log("地图当前视角信息:");
console.log("center : ", [map.getCenter().lng, map.getCenter().lat]);
console.log("zoom : ", map.getZoom());
console.log("pitch : ", map.getPitch());
console.log("bearing : ", map.getBearing());
// console.log("地图当前视角信息:");
// console.log("center : ", [map.getCenter().lng, map.getCenter().lat]);
// console.log("zoom : ", map.getZoom());
// console.log("pitch : ", map.getPitch());
// console.log("bearing : ", map.getBearing());
}
}, 5000);
let queue = [];
......@@ -365,19 +365,42 @@ export default {
map.setCenter(prop.location.split(","));
map.setZoom(19);
this.currentCameras = [];
if (prop.cameraList) {
let cameraList = JSON.parse(prop.cameraList);
setTimeout(() => {
let timeout = 0;
for (let item of cameraList) {
console.log(this.cameraData, prop);
let matchCameras = this.cameraData.filter((camera) => {
return camera.crossId == prop.id;
});
if (matchCameras.length) {
let timeout = 0;
for (let mCamera of matchCameras) {
let toAdd = true;
for (let existCamera of this.showCameras) {
if (existCamera.videoUrl == mCamera.videoUrl) {
toAdd = false;
}
}
if (toAdd) {
timeout += 500;
setTimeout(() => {
!this.videoListData.includes(item) &&
this.videoListData.push(item);
this.showCameras.push(mCamera);
}, timeout);
timeout += 500;
}
});
}
}
// if (prop.cameraList) {
// let cameraList = JSON.parse(prop.cameraList);
// setTimeout(() => {
// let timeout = 0;
// console.log('this.cameraData',this.cameraData);
// for (let item of cameraList) {
// console.log('cameras...',item);
// setTimeout(() => {
// !this.videoListData.includes(item) &&
// this.videoListData.push(item);
// }, timeout);
// timeout += 500;
// }
// });
// }
}
},
refreshBounds() {
......@@ -388,9 +411,9 @@ export default {
let mid2 = [mapBounds._ne.lng, mapBounds._sw.lat];
this.bounds = [[leftDownXy, mid1, rightUpXy, mid2, leftDownXy]];
},
updateCameraCPB(cameraUrl) {
updateCameraCPB(monitorChannel) {
let cameraItem = this.cameraData.find((item) => {
return item.videoUrl == cameraUrl;
return item.monitorChannel == monitorChannel;
});
if (cameraItem && map) {
this.cLocation = cameraItem.wkt.split(",");
......@@ -690,35 +713,40 @@ export default {
for (let i = 0; i < this.radarDatas.length; i++) {
//2 毫米波 other 激光雷达
let model =
radarModelTypes[this.radarDatas[i].sourceEquipType].duplicate();
model.setCoords(this.radarDatas[i].wkt.split(","));
model.userData.data = this.radarDatas[i];
model.setRotation({ x: 90, y: 360 - 90, z: 0 });
window.tb.add(model, `radar${i}${this.radarDatas[i].sourceEquipType}`);
radarModels[`radar${i}${this.radarDatas[i].sourceEquipType}`] = model;
if (this.radarDatas[i].sourceEquipType == 1) {
this.addRadarTimers(this.radarDatas[i], i);
} else {
// 毫米波雷达展示扇形区域 角度可配置 从map_config MilliRadarBearingMap读取
let lnglat = this.radarDatas[i].wkt.split(",");
let center = turf.point(lnglat);
let radius = 0.17; // 100米半径
// let thisRadarDir = this.radarDatas[i].equipAddr;
// let dirCode = this.translateToDirCode(thisRadarDir);
let dirCode = this.radarDatas[i].ridDir;
let bearings =
MilliRadarBearingMap[`${this.radarDatas[i].crossId}-${dirCode}`];
console.log(
"bearings",
MilliRadarBearingMap[`${this.radarDatas[i].crossId}-${dirCode}`],
`${this.radarDatas[i].crossId}-${dirCode}`
radarModelTypes[this.radarDatas[i].sourceEquipType]?.duplicate();
if (model) {
model.setCoords(this.radarDatas[i].wkt.split(","));
model.userData.data = this.radarDatas[i];
model.setRotation({ x: 90, y: 360 - 90, z: 0 });
window.tb.add(
model,
`radar${i}${this.radarDatas[i].sourceEquipType}`
);
if (bearings) {
let arc = turf.lineArc(center, radius, bearings[0], bearings[1], {
unit: "kilometers",
});
let arcScope = [[lnglat, ...arc.geometry.coordinates, lnglat]];
milliFeatures.push(turf.polygon(arcScope, this.radarDatas[i]));
radarModels[`radar${i}${this.radarDatas[i].sourceEquipType}`] = model;
if (this.radarDatas[i].sourceEquipType == 1) {
this.addRadarTimers(this.radarDatas[i], i);
} else {
// 毫米波雷达展示扇形区域 角度可配置 从map_config MilliRadarBearingMap读取
let lnglat = this.radarDatas[i].wkt.split(",");
let center = turf.point(lnglat);
let radius = 0.17; // 100米半径
// let thisRadarDir = this.radarDatas[i].equipAddr;
// let dirCode = this.translateToDirCode(thisRadarDir);
let dirCode = this.radarDatas[i].ridDir;
let bearings =
MilliRadarBearingMap[`${this.radarDatas[i].crossId}-${dirCode}`];
console.log(
"bearings",
MilliRadarBearingMap[`${this.radarDatas[i].crossId}-${dirCode}`],
`${this.radarDatas[i].crossId}-${dirCode}`
);
if (bearings) {
let arc = turf.lineArc(center, radius, bearings[0], bearings[1], {
unit: "kilometers",
});
let arcScope = [[lnglat, ...arc.geometry.coordinates, lnglat]];
milliFeatures.push(turf.polygon(arcScope, this.radarDatas[i]));
}
}
}
}
......@@ -932,7 +960,7 @@ export default {
window.tb.clear(key, null);
}
this.$refs.videoList?.destroyAll();
this.videoListData = [];
this.showCameras = [];
this.removeLightModels();
this.closeWs("callLight").then(() => {
this.currentNearestCrossId = null;
......@@ -970,7 +998,7 @@ export default {
// 如果求得最近路口不是上次的最近路口,则关闭上个ws,请求最新路口ws
if (nearestCrossId !== this.currentNearestCrossId) {
if (source === "move") {
this.videoListData = [];
this.showCameras = [];
}
this.currentNearestCrossId = nearestCrossId;
// console.log("initSignal.......");
......@@ -1250,7 +1278,7 @@ export default {
}
}
},
// 切换业务图层
changeCheck(beforeLabel, checkItem) {
this.refreshBounds();
......@@ -1329,7 +1357,7 @@ export default {
model.setRotation({ x: 90, y: 360 - options.courseAngle - 90, z: 0 });
// }
},
// 转换轨迹数据为geo 并更新轨迹图层
updateSelectVehcleTrack() {
if (
......@@ -1344,7 +1372,7 @@ export default {
mapTools.addOrUpdateSelectVehicleTrack(map, geo);
}
},
// 选中模型改变
onSelectedChangeVehicle(e) {
if (e.detail.selected) {
......@@ -1611,7 +1639,7 @@ export default {
window.tb.update();
vehicleModels = {};
this.lastLocation = [];
this.videoListData = [];
this.showCameras = [];
this.currentNearestCrossId = "";
this.modelsEmpty = true;
}
......@@ -1732,10 +1760,10 @@ export default {
this.dialogVisible[id] = false;
},
// videoList 删除单个 video
deleteVideo(item) {
for (let i = 0; i < this.videoListData.length; i++) {
if (this.videoListData[i] == item) {
this.videoListData.splice(i, 1);
deleteVideo(videoUrl) {
for (let i = 0; i < this.showCameras.length; i++) {
if (this.showCameras[i].videoUrl == videoUrl) {
this.showCameras.splice(i, 1);
}
}
},
......
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