|
@@ -127,16 +127,16 @@
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="event-list">
|
|
<div class="event-list">
|
|
- <el-table :data="eventList" :show-header="false" :border="false" style="width: 100%; --el-table-border-color: none">
|
|
|
|
- <el-table-column prop="level" width="80">
|
|
|
|
|
|
+ <el-table :data="eventList" :border="false" :header-cell-style="{ background: '#fff!important' }" style="width: 100%">
|
|
|
|
+ <el-table-column prop="level" label="事件等级" width="80" align="center">
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
{{ event_level.filter((item) => item.value == row.level)[0]?.label }}
|
|
{{ event_level.filter((item) => item.value == row.level)[0]?.label }}
|
|
</template>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table-column>
|
|
- <el-table-column prop="ext2.lx" show-overflow-tooltip width="100" />
|
|
|
|
- <el-table-column prop="addr" show-overflow-tooltip />
|
|
|
|
- <el-table-column prop="createTimeFormat" width="100" />
|
|
|
|
- <el-table-column prop="status" width="70">
|
|
|
|
|
|
+ <el-table-column prop="ext2.lx" label="事件类型" show-overflow-tooltip width="100" align="center" />
|
|
|
|
+ <el-table-column prop="addr" label="发生地点" show-overflow-tooltip align="center" />
|
|
|
|
+ <el-table-column prop="createTimeFormat" label="发生时间" width="100" align="center" />
|
|
|
|
+ <el-table-column prop="status" label="状态" width="70" align="center">
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
<span v-if="row.status == 3" style="color: #5f86fd">已上报</span>
|
|
<span v-if="row.status == 3" style="color: #5f86fd">已上报</span>
|
|
<span v-else-if="row.status == 1" style="color: #26c768">已解决</span>
|
|
<span v-else-if="row.status == 1" style="color: #26c768">已解决</span>
|
|
@@ -144,7 +144,7 @@
|
|
<span v-else>{{ event_status.filter((item) => item.value == row.status)[0]?.label }}</span>
|
|
<span v-else>{{ event_status.filter((item) => item.value == row.status)[0]?.label }}</span>
|
|
</template>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table-column>
|
|
- <el-table-column width="60">
|
|
|
|
|
|
+ <el-table-column label="操作" width="60" align="center">
|
|
<template #default="{ row }">
|
|
<template #default="{ row }">
|
|
<el-button link type="primary" @click="showDetails(row)">查看</el-button>
|
|
<el-button link type="primary" @click="showDetails(row)">查看</el-button>
|
|
</template>
|
|
</template>
|
|
@@ -164,7 +164,7 @@
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<el-dialog v-model="dialog.visible" :title="dialog.title" width="1000px" top="0" append-to-body @close="dialogClose">
|
|
<el-dialog v-model="dialog.visible" :title="dialog.title" width="1000px" top="0" append-to-body @close="dialogClose">
|
|
- <template #title>
|
|
|
|
|
|
+ <template #header>
|
|
<div style="display: flex; justify-content: space-between; align-items: center; padding-right: 30px">
|
|
<div style="display: flex; justify-content: space-between; align-items: center; padding-right: 30px">
|
|
<span>事件详情</span>
|
|
<span>事件详情</span>
|
|
<el-button @click="saveClick">保存</el-button>
|
|
<el-button @click="saveClick">保存</el-button>
|
|
@@ -235,7 +235,7 @@
|
|
<div class="event-media-title">现场视频</div>
|
|
<div class="event-media-title">现场视频</div>
|
|
<div class="event-media-content">
|
|
<div class="event-media-content">
|
|
<div class="videos">
|
|
<div class="videos">
|
|
- <div v-for="(item, index) in form.videoList" :key="index" :id="`dplayer${index}`"></div>
|
|
|
|
|
|
+ <div v-for="(item, index) in form.videoList" :key="index" :id="`player${index}`"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@@ -304,6 +304,9 @@
|
|
</template>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</el-dialog>
|
|
</el-dialog>
|
|
|
|
+ <el-dialog v-model="showVideo" title="实时视频" width="600" @close="closeVideo" append-to-body>
|
|
|
|
+ <div id="mse" class="real-video"></div>
|
|
|
|
+ </el-dialog>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
@@ -311,12 +314,14 @@ import { Search } from '@element-plus/icons-vue';
|
|
import BaseChart from '@/components/BaseChart/index.vue';
|
|
import BaseChart from '@/components/BaseChart/index.vue';
|
|
import monitorIcon from '@/assets/images/monitor.svg';
|
|
import monitorIcon from '@/assets/images/monitor.svg';
|
|
import monitorEventIcon from '@/assets/images/monitor-event.svg';
|
|
import monitorEventIcon from '@/assets/images/monitor-event.svg';
|
|
-import DPlayer from 'dplayer';
|
|
|
|
|
|
+import Player from 'xgplayer';
|
|
|
|
+import 'xgplayer/dist/index.min.css';
|
|
|
|
+import HlsPlugin from 'xgplayer-hls';
|
|
import '@wangeditor/editor/dist/css/style.css'; // 引入 css
|
|
import '@wangeditor/editor/dist/css/style.css'; // 引入 css
|
|
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
|
|
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
|
|
import { downloadFile } from '@/utils/download.js';
|
|
import { downloadFile } from '@/utils/download.js';
|
|
import { listEvent, generateReport, updateEvent, getEvent, getEventTypes } from '@/api/system/event';
|
|
import { listEvent, generateReport, updateEvent, getEvent, getEventTypes } from '@/api/system/event';
|
|
-import { listDevice } from '@/api/system/device';
|
|
|
|
|
|
+import { listDevice, getHlsCode, closeHlsCode } from '@/api/system/device';
|
|
import { dateFormat } from '@/utils/index';
|
|
import { dateFormat } from '@/utils/index';
|
|
import { ElLoading } from 'element-plus';
|
|
import { ElLoading } from 'element-plus';
|
|
import { base64Encoded, base64Decoded } from '@/utils/crypto';
|
|
import { base64Encoded, base64Decoded } from '@/utils/crypto';
|
|
@@ -372,6 +377,8 @@ const statData = reactive({
|
|
report: undefined
|
|
report: undefined
|
|
});
|
|
});
|
|
const showList = ref(true);
|
|
const showList = ref(true);
|
|
|
|
+const showVideo = ref(false);
|
|
|
|
+const curVideoUrl = ref('');
|
|
const shortcuts = [
|
|
const shortcuts = [
|
|
{
|
|
{
|
|
text: '最近一周',
|
|
text: '最近一周',
|
|
@@ -482,6 +489,7 @@ onMounted(() => {
|
|
const point = new BMapGL.Point(118.879999, 32.016216); // 创建点坐标
|
|
const point = new BMapGL.Point(118.879999, 32.016216); // 创建点坐标
|
|
map.centerAndZoom(point, 14);
|
|
map.centerAndZoom(point, 14);
|
|
map.enableScrollWheelZoom(true);
|
|
map.enableScrollWheelZoom(true);
|
|
|
|
+ map.setTrafficOn();
|
|
bdmap.value = map;
|
|
bdmap.value = map;
|
|
map.addEventListener('click', function (e) {
|
|
map.addEventListener('click', function (e) {
|
|
const { lng, lat } = e.latlng;
|
|
const { lng, lat } = e.latlng;
|
|
@@ -689,6 +697,7 @@ const getDeviceList = () => {
|
|
}
|
|
}
|
|
return {
|
|
return {
|
|
...item,
|
|
...item,
|
|
|
|
+ ext1: item.ext1 && JSON.parse(item.ext1),
|
|
hasEvent
|
|
hasEvent
|
|
};
|
|
};
|
|
});
|
|
});
|
|
@@ -718,6 +727,11 @@ const showMarks = () => {
|
|
const icon = new BMapGL.Icon(img, new BMapGL.Size(30, 30));
|
|
const icon = new BMapGL.Icon(img, new BMapGL.Size(30, 30));
|
|
const marker = new BMapGL.Marker(pt, { icon });
|
|
const marker = new BMapGL.Marker(pt, { icon });
|
|
marker.addEventListener('click', (e) => {
|
|
marker.addEventListener('click', (e) => {
|
|
|
|
+ if (item.ext1 && item.ext1.streamUrl) {
|
|
|
|
+ showVideoDialog(item.ext1.streamUrl);
|
|
|
|
+ } else {
|
|
|
|
+ proxy?.$modal.msgError('暂未配置视频流地址');
|
|
|
|
+ }
|
|
if (item.hasEvent == 1) {
|
|
if (item.hasEvent == 1) {
|
|
queryParams.value.params.deviceId = item.id;
|
|
queryParams.value.params.deviceId = item.id;
|
|
queryParams.value.pageNum = 1;
|
|
queryParams.value.pageNum = 1;
|
|
@@ -727,6 +741,45 @@ const showMarks = () => {
|
|
bdmap.value.addOverlay(marker);
|
|
bdmap.value.addOverlay(marker);
|
|
});
|
|
});
|
|
};
|
|
};
|
|
|
|
+const showVideoDialog = (url) => {
|
|
|
|
+ curVideoUrl.value = url;
|
|
|
|
+ showVideo.value = true;
|
|
|
|
+ let realUrl = '';
|
|
|
|
+ getHlsCode(base64Encoded(url)).then(({ code, data }) => {
|
|
|
|
+ if (code == 200) {
|
|
|
|
+ if (import.meta.env.DEV) {
|
|
|
|
+ realUrl = data.replace(/^http:\/\/jtjai\.xt\.wenhq\.top:8083/, '');
|
|
|
|
+ } else {
|
|
|
|
+ realUrl = data;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (HlsPlugin.isSupported()) {
|
|
|
|
+ const player = new Player({
|
|
|
|
+ id: 'mse',
|
|
|
|
+ url: realUrl,
|
|
|
|
+ isLive: false,
|
|
|
|
+ autoplay: true,
|
|
|
|
+ plugins: [HlsPlugin],
|
|
|
|
+ ignores: ['cssfullscreen'],
|
|
|
|
+ hls: {
|
|
|
|
+ retryCount: 3, // 重试 3 次,默认值
|
|
|
|
+ retryDelay: 1000, // 每次重试间隔 1 秒,默认值
|
|
|
|
+ loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
|
|
|
|
+ fetchOptions: {
|
|
|
|
+ // 该参数会透传给 fetch,默认值为 undefined
|
|
|
|
+ mode: 'cors'
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+const closeVideo = () => {
|
|
|
|
+ if (curVideoUrl.value) {
|
|
|
|
+ closeHlsCode(base64Encoded(curVideoUrl.value));
|
|
|
|
+ }
|
|
|
|
+};
|
|
const showDetails = (row) => {
|
|
const showDetails = (row) => {
|
|
getEvent(row.id).then(({ code, data }) => {
|
|
getEvent(row.id).then(({ code, data }) => {
|
|
if (code === 200) {
|
|
if (code === 200) {
|
|
@@ -734,7 +787,7 @@ const showDetails = (row) => {
|
|
data.ext2 = JSON.parse(data.ext2) || {};
|
|
data.ext2 = JSON.parse(data.ext2) || {};
|
|
data.status = data.status || '2';
|
|
data.status = data.status || '2';
|
|
let imgList = [];
|
|
let imgList = [];
|
|
- let videoList = [];
|
|
|
|
|
|
+ let videoList = []; //
|
|
if (data.ext1.length) {
|
|
if (data.ext1.length) {
|
|
videoList = data.ext1
|
|
videoList = data.ext1
|
|
.filter((item) => item.includes('mp4'))
|
|
.filter((item) => item.includes('mp4'))
|
|
@@ -743,7 +796,7 @@ const showDetails = (row) => {
|
|
.filter((item) => !item.includes('mp4'))
|
|
.filter((item) => !item.includes('mp4'))
|
|
.map((item) => `${import.meta.env.VITE_APP_BASE_HOST}/api/oss/local/upload/${item}`);
|
|
.map((item) => `${import.meta.env.VITE_APP_BASE_HOST}/api/oss/local/upload/${item}`);
|
|
}
|
|
}
|
|
- if(data.ext2.EventVideoPath){
|
|
|
|
|
|
+ if (data.ext2.EventVideoPath) {
|
|
videoList = [data.ext2.EventVideoPath];
|
|
videoList = [data.ext2.EventVideoPath];
|
|
}
|
|
}
|
|
|
|
|
|
@@ -752,11 +805,9 @@ const showDetails = (row) => {
|
|
dialog.visible = true;
|
|
dialog.visible = true;
|
|
nextTick(() => {
|
|
nextTick(() => {
|
|
form.value.videoList.forEach((item, index) => {
|
|
form.value.videoList.forEach((item, index) => {
|
|
- new DPlayer({
|
|
|
|
- container: document.getElementById(`dplayer${index}`),
|
|
|
|
- video: {
|
|
|
|
- url: item
|
|
|
|
- }
|
|
|
|
|
|
+ new Player({
|
|
|
|
+ el: document.getElementById(`player${index}`),
|
|
|
|
+ url: item
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
@@ -831,6 +882,7 @@ const saveClick = async () => {
|
|
getList();
|
|
getList();
|
|
getStat();
|
|
getStat();
|
|
getEventTypeOptions();
|
|
getEventTypeOptions();
|
|
|
|
+ getDeviceList();
|
|
proxy?.$modal.msgSuccess('保存成功');
|
|
proxy?.$modal.msgSuccess('保存成功');
|
|
};
|
|
};
|
|
const extractDate = (dateStr) => {
|
|
const extractDate = (dateStr) => {
|
|
@@ -1153,8 +1205,8 @@ const formSubmit = async (field, obj = '') => {
|
|
flex-wrap: wrap;
|
|
flex-wrap: wrap;
|
|
|
|
|
|
div {
|
|
div {
|
|
- height: 200px;
|
|
|
|
- width: 32%;
|
|
|
|
|
|
+ height: 200px !important;
|
|
|
|
+ width: 32% !important;
|
|
border-radius: 4px;
|
|
border-radius: 4px;
|
|
margin-right: 10px;
|
|
margin-right: 10px;
|
|
margin-top: 10px;
|
|
margin-top: 10px;
|
|
@@ -1200,4 +1252,8 @@ const formSubmit = async (field, obj = '') => {
|
|
color: #5d5d5dd9;
|
|
color: #5d5d5dd9;
|
|
font-size: 14px;
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
|
|
+.real-video {
|
|
|
|
+ height: 400px !important;
|
|
|
|
+ width: 100% !important;
|
|
|
|
+}
|
|
</style>
|
|
</style>
|