| | |
| | | </div> |
| | | </div> |
| | | <div class="info-right flex2"> |
| | | <PlayLive></PlayLive> |
| | | <div style="position: relative; width: 100%; height: 330px"> |
| | | <div |
| | | style="width: 100%; height: 330px; border-radius: 9px; background: #f5f5f5; display: flex; justify-content: center; align-items: center; flex-direction: column"> |
| | | <video style="width: 100%; height: 330px; border-radius: 9px; display: none" id="monitoringCard" |
| | | ref="monitoringCard" :controls="false" autoPlay width="620"> |
| | | </video> |
| | | <el-empty description="暂无视频信息" :image-size="80"></el-empty> |
| | | </div> |
| | | <canvas id="myCanvas" style="display:none"></canvas> |
| | | <el-button type="default" style="position: absolute; top: 10px;right: 60px;" |
| | | @click="goBack()">查看回放</el-button> |
| | | <div style="position: absolute; right: 11px; top: 10px"> |
| | | <div style="display: flex;flex-direction: column;align-items: center;justify-content: center; |
| | | background: #ffffff; padding: 3px 10px; border-radius: 6px;margin-bottom: 10px;" @click="fullScreen()"> |
| | | <img style="width: 20px; height: 20px" :src="require('../../assets//homeImg/full.png')" /> |
| | | <div |
| | | style="font-size: 12px;font-weight: 400; line-height: 17px; color: rgba(0, 0, 0, 0.88);"> |
| | | 全屏</div> |
| | | </div> |
| | | <div style="display: flex;flex-direction: column;align-items: center; justify-content: center; |
| | | background: #ffffff;padding: 3px 10px;border-radius: 6px;" @click="shotScreen()"> |
| | | <img style="width: 20px; height: 20px" :src="require('../../assets//homeImg/slot.png')" /> |
| | | <div |
| | | style="font-size: 12px; font-weight: 400; line-height: 17px; color: rgba(0, 0, 0, 0.88);"> |
| | | 截屏</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="tab-content ml--100 mr--30"> |
| | |
| | | <!-- 使用 Tailwind CSS 的内联十六进制颜色类 --> |
| | | <div>{{ info.vehicleNumber }}<span v-if="info.warnList && info.warnList.length > 0">({{ |
| | | info.warnList.length |
| | | }})</span></div><i @click="closeDrawer" class="el-icon-s-unfold color1 pointer"></i> |
| | | }})</span></div><i @click="closeDrawer" class="el-icon-s-unfold color1 pointer"></i> |
| | | </div> |
| | | <hr class="mt--10" /> |
| | | <div class="pl--15 pr--15"> |
| | |
| | | <script> |
| | | import AMapLoader from "@amap/amap-jsapi-loader"; |
| | | import DetailModal from "./components/detailModal.vue"; |
| | | import PlayLive from '@/components/PlayLive/index.vue' |
| | | import DetailOrderModal from "./components/detailOrderModal.vue"; |
| | | import { getCarDetail, getCarOrder, getCarWarning, getCarTrack, getCarVideo,getDetail,getOrderInfo,getOrderTravel,getOrderMonitoring} from './service' |
| | | import { getCarDetail, getCarOrder, getCarWarning, getCarTrack, getCarVideo, getDetail, getOrderInfo, getOrderTravel, getOrderMonitoring, playDetection, closeRealVideo } from './service' |
| | | import moment from "moment"; |
| | | export default { |
| | | name: "detail", |
| | | components: { DetailModal,DetailOrderModal,PlayLive }, |
| | | components: { DetailModal, DetailOrderModal }, |
| | | data() { |
| | | return { |
| | | id: '', |
| | |
| | | activeName: 'first', |
| | | routeList: [], |
| | | videoObj: {}, |
| | | loading:false, |
| | | loading: false, |
| | | drawer: false, |
| | | showWarnDetail: false, |
| | | info: {}, |
| | |
| | | }) |
| | | getCarVideo({ id: this.$route.query.id }).then(res => { |
| | | this.videoObj = res; |
| | | |
| | | this.initVideoPlayer(); |
| | | |
| | | }) |
| | | } |
| | | |
| | | }, |
| | | |
| | | destroyed() { |
| | | this.destroyPlayer(); |
| | | }, |
| | | methods: { |
| | | goBack() { |
| | | this.$router.push('/car-playback?id=' + this.$route.query.id) |
| | | }, |
| | | // 初始化视频播放器 |
| | | initVideoPlayer() { |
| | | // 先销毁之前的播放器 |
| | | if (this.flvPlayer) { |
| | | this.flvPlayer.destroy(); |
| | | this.flvPlayer = null; |
| | | } |
| | | |
| | | // 获取video元素 |
| | | const video = document.getElementById("monitoringCard"); |
| | | if (!video) { |
| | | console.error("Video element not found"); |
| | | return; |
| | | } |
| | | |
| | | // 检查flv.js是否支持 |
| | | if (flvjs.isSupported()) { |
| | | try { |
| | | playDetection(this.carId).then((res) => { |
| | | this.flvPlayer = flvjs.createPlayer({ |
| | | type: "flv", //视频类型 |
| | | isLive: true, //是否为直播 |
| | | cors: true, //是否开启跨域 |
| | | hasAudio: false, //是否开启音频 |
| | | hasVideo: true, //是否开启视频 |
| | | url: `http://${this.videoObj.serverIp}:${this.videoObj.serverPort}/live?port=1935&app=flv&stream=${this.$route.query.id}`, // 后端拿到的视频路径 |
| | | enableWorker: true, //启用 Web Worker 进程来加速视频的解码和处理过程 |
| | | enableStashBuffer: false, // 启用数据缓存机制,提高视频的流畅度和稳定性。 |
| | | stashInitialSize: 1024, // 初始缓存大小。单位:字节。建议针对直播:调整为1024kb |
| | | stashInitialTime: 0.2, // 缓存初始时间。单位:秒。建议针对直播:调整为200毫秒 |
| | | seekType: "range", // 建议将其设置为"range"模式,以便更快地加载视频数据,提高视频的实时性。 |
| | | lazyLoad: false, //关闭懒加载模式,从而提高视频的实时性。建议针对直播:调整为false |
| | | lazyLoadMaxDuration: 0.2, // 懒加载的最大时长。单位:秒。建议针对直播:调整为200毫秒 |
| | | deferLoadAfterSourceOpen: false, // 不预先加载视频数据,在 MSE(Media Source Extensions)打开后立即加载数据,提高视频的实时性。建议针对直播:调整为false |
| | | }); |
| | | let video = document.getElementById("monitoringCard"); |
| | | this.flvPlayer.attachMediaElement(video); // video容器 |
| | | this.flvPlayer.load(); |
| | | this.flvPlayer |
| | | .play() |
| | | .then((res) => { |
| | | // 显示视频元素 |
| | | video.style.display = 'block'; |
| | | // 隐藏空状态 |
| | | const emptyElement = video.parentElement.querySelector('.el-empty'); |
| | | if (emptyElement) { |
| | | emptyElement.style.display = 'none'; |
| | | } |
| | | |
| | | this.videoTimer = setInterval(() => { |
| | | playDetection(this.carId); |
| | | }, 5000); |
| | | }) |
| | | .catch((err) => { |
| | | this.destroyPlayer(); |
| | | }); |
| | | // 错误监听 |
| | | this.flvPlayer.on("error", (err) => { |
| | | this.destroyPlayer(); |
| | | }); |
| | | }); |
| | | } catch (error) { |
| | | console.error("创建播放器失败:", error); |
| | | } |
| | | } else { |
| | | console.error("当前浏览器不支持flv.js"); |
| | | } |
| | | }, |
| | | |
| | | destroyPlayer() { |
| | | // 销毁播放器释放资源 |
| | | if (this.flvPlayer) { |
| | | if (this.videoTimer) clearInterval(this.videoTimer); |
| | | closeRealVideo(this.carId).then((res) => { |
| | | this.flvPlayer.pause(); |
| | | this.flvPlayer.unload(); |
| | | this.flvPlayer.detachMediaElement(); |
| | | this.flvPlayer.destroy(); |
| | | this.flvPlayer = null; |
| | | |
| | | // 恢复空状态的显示 |
| | | const video = document.getElementById("monitoringCard"); |
| | | if (video) { |
| | | video.style.display = 'none'; |
| | | const emptyElement = video.parentElement.querySelector('.el-empty'); |
| | | if (emptyElement) { |
| | | emptyElement.style.display = 'block'; |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | }, |
| | | |
| | | // 处理视频错误 |
| | | handleVideoError(event) { |
| | | console.error("视频加载失败", event); |
| | | if (this.flvPlayer) { |
| | | this.flvPlayer.destroy(); |
| | | this.flvPlayer = null; |
| | | } |
| | | this.infoWindow.setContent( |
| | | '<div style="padding: 20px;text-align: center;color: red;">视频加载失败,请稍后重试</div>' |
| | | ); |
| | | }, |
| | | |
| | | shotScreen() { |
| | | // 获取video和canvas元素 |
| | | const video = document.getElementById("monitoringCard"); |
| | | const canvas = document.getElementById("myCanvas"); |
| | | // 设置canvas的宽度和高度与video相同 |
| | | canvas.width = video.videoWidth; |
| | | canvas.height = video.videoHeight; |
| | | // 获取canvas的2d绘图上下文 |
| | | const context = canvas.getContext("2d"); |
| | | // 将当前video帧绘制到canvas上 |
| | | context.drawImage(video, 0, 0, canvas.width, canvas.height); |
| | | |
| | | setTimeout(() => { |
| | | // 将canvas内容转换为图片 |
| | | let dataURL = canvas.toDataURL("image/png"); |
| | | this.downloadImage(dataURL); |
| | | }, 100); |
| | | }, |
| | | downloadImage(base64) { |
| | | const link = document.createElement("a"); |
| | | link.href = base64; |
| | | link.download = "screenshot.png"; // 你希望下载的文件名 |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | document.body.removeChild(link); |
| | | }, |
| | | fullScreen() { |
| | | const video = document.getElementById("monitoringCard"); |
| | | if (video.requestFullscreen) { |
| | | video.requestFullscreen(); |
| | | } else if (video.mozRequestFullScreen) { |
| | | // Firefox |
| | | video.mozRequestFullScreen(); |
| | | } else if (video.webkitRequestFullscreen) { |
| | | // Chrome, Safari and Opera |
| | | video.webkitRequestFullscreen(); |
| | | } else if (video.msRequestFullscreen) { |
| | | // IE/Edge |
| | | video.msRequestFullscreen(); |
| | | } |
| | | }, |
| | | showDetails(row) { |
| | | this.loading = true |
| | | Promise.all([getOrderInfo(row.id), getOrderTravel({ id: row.id })]).then(res => { |
| | |
| | | closeDrawer() { |
| | | this.drawer = false |
| | | this.showWarnDetail = false |
| | | |
| | | |
| | | }, |
| | | // 查看详情 |
| | | viewDetail(row) { |
| | |
| | | securityJsCode: this.$secretKey, |
| | | }; |
| | | AMapLoader.load({ |
| | | key:this.$mapKey, |
| | | key: this.$mapKey, |
| | | version: "2.0", |
| | | plugins: [ |
| | | "AMap.ToolBar", |
| | |
| | | pageSize: 10, |
| | | total: 0, |
| | | } |
| | | |
| | | |
| | | this.getList(this.detail.vehicleNumber) |
| | | //销毁地图 |
| | | if (this.map) { |
| | |
| | | }, |
| | | reset() { |
| | | this.searchForm = { |
| | | pageCurr: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | date: undefined, |
| | | pageCurr: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | date: undefined, |
| | | } |
| | | }, |
| | | getList(vehicleNumber) { |
| | |
| | | width: 100%; |
| | | height: 600px; |
| | | } |
| | | |
| | | #mapContainers { |
| | | width: 100%; |
| | | height: 500px; |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .color1 { |
| | | color: #0E6EFD; |
| | | } |