Merge branch 'master' of http://120.76.84.145:10101/gitblit/r/H5/shehong-vehicle-supervision
| | |
| | | <template> |
| | | <div style="height: 100%; position: relative;"> |
| | | <video id="video" style="height: 100%;width: 100%;" muted controls></video> |
| | | <div style="width: 100%; height: 100%; border-radius: 9px; background: #f5f5f5; display: flex; justify-content: center; align-items: center; flex-direction: column"> |
| | | <video id="video" style="width: 100%; height: 100%; border-radius: 9px; display: none" muted controls></video> |
| | | <el-empty description="暂无视频信息" :image-size="80"></el-empty> |
| | | </div> |
| | | <div v-if="showError" class="error-box" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.8); display: flex; justify-content: center; align-items: center; z-index: 9999;"> |
| | | <div class="error-content" style="text-align: center; color: #fff;"> |
| | | <i class="el-icon-warning" style="font-size: 48px; color: #E6A23C; margin-bottom: 16px;"></i> |
| | |
| | | return { |
| | | flvPlayer: null, |
| | | timer: null, |
| | | showError: false, |
| | | showError: false |
| | | } |
| | | }, |
| | | watch: { |
| | | urlLink: { |
| | | handler(newUrl) { |
| | | if (newUrl) { |
| | | this.destroyPlayer(); |
| | | this.playDetection(); |
| | | } |
| | | }, |
| | | immediate: true |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.playDetection() |
| | | if (this.urlLink) { |
| | | this.playDetection(); |
| | | } |
| | | }, |
| | | beforeDestroy() { |
| | | this.destroyPlayer(); |
| | |
| | | methods: { |
| | | playDetection() { |
| | | this.showError = false; |
| | | if (flvjs.isSupported()) { |
| | | playDetection(this.carId).then(res => { |
| | | if (!flvjs.isSupported()) { |
| | | this.showError = true; |
| | | this.$emit('video-error'); |
| | | return; |
| | | } |
| | | |
| | | playDetection(this.carId).then(res => { |
| | | if (this.flvPlayer) { |
| | | this.destroyPlayer(); |
| | | } |
| | | |
| | | try { |
| | | this.flvPlayer = flvjs.createPlayer({ |
| | | type: 'flv',//视频类型 |
| | | isLive: true,//是否为直播 |
| | | cors: true,//是否开启跨域 |
| | | hasAudio: false,//是否开启音频 |
| | | hasVideo: true,//是否开启视频 |
| | | // url: `http://${this.serverIp}:${this.serverPort}/live?port=1935&app=flv&stream=${this.carId}`, // 后端拿到的视频路径 |
| | | url: this.urlLink, // 后端拿到的视频路径 |
| | | type: 'flv', //视频类型 |
| | | isLive: true, //是否为直播 |
| | | cors: true, //是否开启跨域 |
| | | hasAudio: false, //是否开启音频 |
| | | hasVideo: true, //是否开启视频 |
| | | url: this.urlLink, // 后端拿到的视频路径 |
| | | enableWorker: true, //启用 Web Worker 进程来加速视频的解码和处理过程 |
| | | enableStashBuffer: false, // 启用数据缓存机制,提高视频的流畅度和稳定性。 |
| | | stashInitialSize: 1024, // 初始缓存大小。单位:字节。建议针对直播:调整为1024kb |
| | |
| | | lazyLoadMaxDuration: 0.2, // 懒加载的最大时长。单位:秒。建议针对直播:调整为200毫秒 |
| | | deferLoadAfterSourceOpen: false // 不预先加载视频数据,在 MSE(Media Source Extensions)打开后立即加载数据,提高视频的实时性。建议针对直播:调整为false |
| | | }); |
| | | |
| | | let video = document.getElementById('video'); |
| | | this.flvPlayer.attachMediaElement(video); // video容器 |
| | | if (!video) { |
| | | throw new Error('Video element not found'); |
| | | } |
| | | |
| | | this.flvPlayer.attachMediaElement(video); |
| | | this.flvPlayer.load(); |
| | | this.flvPlayer.play().then(res => { |
| | | |
| | | this.flvPlayer.play().then(() => { |
| | | // 显示视频元素 |
| | | video.style.display = 'block'; |
| | | // 隐藏空状态 |
| | | const emptyElement = video.parentElement.querySelector('.el-empty'); |
| | | if (emptyElement) { |
| | | emptyElement.style.display = 'none'; |
| | | } |
| | | |
| | | this.timer = setInterval(() => { |
| | | playDetection(this.carId) |
| | | }, 5000) |
| | | playDetection(this.carId); |
| | | }, 5000); |
| | | }).catch(err => { |
| | | console.error('视频播放失败:', err); |
| | | this.showError = true; |
| | | this.destroyPlayer(); |
| | | }) |
| | | // 错误监听 |
| | | this.flvPlayer.on('error', (err) => { |
| | | this.showError = true; |
| | | this.destroyPlayer(); |
| | | this.$emit('video-error'); |
| | | }); |
| | | }) |
| | | } |
| | | |
| | | this.flvPlayer.on('error', (err) => { |
| | | console.error('视频播放器错误:', err); |
| | | this.showError = true; |
| | | this.destroyPlayer(); |
| | | this.$emit('video-error'); |
| | | }); |
| | | |
| | | } catch (err) { |
| | | console.error('创建播放器失败:', err); |
| | | this.showError = true; |
| | | this.destroyPlayer(); |
| | | this.$emit('video-error'); |
| | | } |
| | | }).catch(err => { |
| | | console.error('获取视频流失败:', err); |
| | | this.showError = true; |
| | | this.destroyPlayer(); |
| | | this.$emit('video-error'); |
| | | }); |
| | | }, |
| | | destroyPlayer() { |
| | | // 销毁播放器释放资源 |
| | | if (this.flvPlayer) { |
| | | if (this.timer) clearInterval(this.timer) |
| | | closeRealVideo(this.carId).then(res => { |
| | | if (this.timer) { |
| | | clearInterval(this.timer); |
| | | this.timer = null; |
| | | } |
| | | try { |
| | | this.flvPlayer.pause(); |
| | | this.flvPlayer.unload(); |
| | | this.flvPlayer.detachMediaElement(); |
| | | this.flvPlayer.destroy(); |
| | | } catch (err) { |
| | | console.error('销毁播放器失败:', err); |
| | | } finally { |
| | | this.flvPlayer = null; |
| | | }) |
| | | } |
| | | closeRealVideo(this.carId).catch(err => { |
| | | console.error('关闭视频流失败:', err); |
| | | }); |
| | | |
| | | // 恢复空状态的显示 |
| | | const video = document.getElementById('video'); |
| | | if (video) { |
| | | video.style.display = 'none'; |
| | | const emptyElement = video.parentElement.querySelector('.el-empty'); |
| | | if (emptyElement) { |
| | | emptyElement.style.display = 'block'; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | window.location.replace(`/`);
|
| | | return Promise.reject(res.data.data)
|
| | | }
|
| | | if (res.data.msg == '发起实时音视频失败' || res.data.msg == '获取视频失败') {
|
| | | Message({
|
| | | message: '发起实时音视频失败,可能是车辆离线导致',
|
| | | type: 'error',
|
| | | duration: 2000
|
| | | })
|
| | | return Promise.reject(res.data.data)
|
| | | }
|
| | | Message({
|
| | | message: res.data.msg || '服务器错误',
|
| | | type: 'error',
|
| | | duration: 2000
|
| | | duration: 4000
|
| | | })
|
| | | return Promise.reject(res.data.data)
|
| | | }
|
| | |
| | | <el-radio-group v-model="tabPosition" style="margin-bottom: 30px;"> |
| | | <el-radio-button label="order">订单信息</el-radio-button> |
| | | <el-radio-button label="track">行程轨迹</el-radio-button> |
| | | <el-radio-button label="monitoring">行程监控</el-radio-button> |
| | | <!-- <el-radio-button label="monitoring">行程监控</el-radio-button> --> |
| | | </el-radio-group> |
| | | <!-- 订单信息 --> |
| | | <div v-show="tabPosition == 'order'"> |
| | |
| | | <div class="mapContainer" id="mapContainers"></div> |
| | | </div> |
| | | <!-- 行程监控 --> |
| | | <div v-if="tabPosition == 'monitoring'"> |
| | | <!-- <div v-if="tabPosition == 'monitoring'"> |
| | | <PlayLive :serverIp="monitoringData.serverIp" :serverPort="monitoringData.serverPort" |
| | | :urlLink="monitoringData.url" :carId="orderData.carId" /> |
| | | </div> |
| | | </div> --> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | |
| | | </div> --> |
| | | </div> |
| | | </div> |
| | | <div class="info-right flex2"> |
| | | <div class="info-right flex2" style="margin-top: 40px;"> |
| | | <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 style="width: 100%;border-radius: 9px; display: none" id="monitoringCard" |
| | | ref="monitoringCard" :controls="false" autoplay> |
| | | </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;" |
| | | <el-button type="default" style="position: absolute; top: 20px;right: 60px;" |
| | | @click="goBack()">查看回放</el-button> |
| | | <div style="position: absolute; right: 11px; top: 10px"> |
| | | <div style="position: absolute; right: 11px; top: 20px"> |
| | | <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')" /> |
| | |
| | | import DetailOrderModal from "./components/detailOrderModal.vue"; |
| | | import { getCarDetail, getCarOrder, getCarWarning, getCarTrack, getCarVideo, getDetail, getOrderInfo, getOrderTravel, getOrderMonitoring, playDetection, closeRealVideo } from './service' |
| | | import moment from "moment"; |
| | | import flvjs from "flv.js"; |
| | | export default { |
| | | name: "detail", |
| | | components: { DetailModal, DetailOrderModal }, |
| | |
| | | showWarnDetail: false, |
| | | info: {}, |
| | | activeInfo: {}, |
| | | map: null |
| | | map: null, |
| | | carId: null |
| | | } |
| | | }, |
| | | mounted() { |
| | | if (this.$route.query.id) { |
| | | this.carId = this.$route.query.id |
| | | getCarDetail({ id: this.$route.query.id }).then(res => { |
| | | this.detail = res; |
| | | this.getList(res.vehicleNumber); |
| | |
| | | |
| | | }, |
| | | |
| | | destroyed() { |
| | | beforeDestroy() { |
| | | this.destroyPlayer(); |
| | | }, |
| | | methods: { |
| | |
| | | // 检查flv.js是否支持 |
| | | if (flvjs.isSupported()) { |
| | | try { |
| | | playDetection(this.carId).then((res) => { |
| | | playDetection(this.$route.query.id).then((res) => { |
| | | this.flvPlayer = flvjs.createPlayer({ |
| | | type: "flv", //视频类型 |
| | | isLive: true, //是否为直播 |
| | |
| | | } |
| | | |
| | | this.videoTimer = setInterval(() => { |
| | | playDetection(this.carId); |
| | | playDetection(this.$route.query.id); |
| | | }, 5000); |
| | | }) |
| | | .catch((err) => { |
| | |
| | | }); |
| | | }); |
| | | } catch (error) { |
| | | console.error("创建播放器失败:", error); |
| | | console.log("创建播放器失败:", error); |
| | | } |
| | | } else { |
| | | console.error("当前浏览器不支持flv.js"); |
| | |
| | | showDetails(row) { |
| | | this.loading = true |
| | | Promise.all([getOrderInfo(row.id), getOrderTravel({ id: row.id })]).then(res => { |
| | | getOrderMonitoring({ id: row.id }).then(resp => { |
| | | this.$refs.detailOrder.initData(res[0], resp, res[1]) |
| | | this.loading = false |
| | | }).catch(err => { |
| | | this.$refs.detailOrder.initData(res[0], {}, res[1]) |
| | | this.loading = false |
| | | }) |
| | | // getOrderMonitoring({ id: row.id }).then(resp => { |
| | | // this.$refs.detailOrder.initData(res[0], resp, res[1]) |
| | | // this.loading = false |
| | | // }).catch(err => { |
| | | this.$refs.detailOrder.initData(res[0], {}, res[1]) |
| | | this.loading = false |
| | | // }) |
| | | }).catch(err => { |
| | | this.loading = false |
| | | }) |
| | |
| | | |
| | | .info-content { |
| | | padding: 30px; |
| | | margin-bottom: 30px; |
| | | } |
| | | |
| | | ::v-deep .el-descriptions-item__container { |
| | |
| | | export const getOrderMonitoring = (params) => { |
| | | return axios.get(`/system/order/getOrderMonitoring`, { params }) |
| | | } |
| | | // 通知后端开始获取视频流 |
| | | export const playDetection = (id) => { |
| | | return axios.get(`/system/car/playDetection/${id}`) |
| | | } |
| | | |
| | | // 通知后端开始关闭视频流 |
| | | export const closeRealVideo = (id) => { |
| | | return axios.get(`/system/car/closeRealVideo/${id}`) |
| | | } |
| | |
| | | import html2canvas from "html2canvas"; |
| | | import AMapLoader from "@amap/amap-jsapi-loader"; |
| | | import flvjs from "flv.js"; |
| | | import moment from 'moment' |
| | | import { |
| | | getCarCount, |
| | | getCarStatusCount, |
| | |
| | | // 获取预警列表数据 |
| | | async getWarnListData() { |
| | | try { |
| | | const res = await getCarWarnList({ pageNum: 1, pageSize: 100000 }); |
| | | const res = await getCarWarnList({ pageNum: 1, pageSize: 100000,startTime:moment().format('YYYY-MM-DD 00:00:00'),endTime:moment().format('YYYY-MM-DD 23:59:59') }); |
| | | this.warnList = res.records; |
| | | } catch (error) { |
| | | this.$message.error("获取预警列表数据失败"); |
| | |
| | | <el-radio-group v-model="tabPosition" style="margin-bottom: 30px;"> |
| | | <el-radio-button label="order">订单信息</el-radio-button> |
| | | <el-radio-button label="track">行程轨迹</el-radio-button> |
| | | <el-radio-button label="monitoring">行程监控</el-radio-button> |
| | | <!-- <el-radio-button label="monitoring">行程监控</el-radio-button> --> |
| | | </el-radio-group> |
| | | <!-- 订单信息 --> |
| | | <div v-show="tabPosition == 'order'"> |
| | |
| | | <div class="mapContainer" id="mapContainer"></div> |
| | | </div> |
| | | <!-- 行程监控 --> |
| | | <div v-if="tabPosition == 'monitoring'"> |
| | | <!-- <div v-if="tabPosition == 'monitoring'"> |
| | | <PlayLive :serverIp="monitoringData.serverIp" :serverPort="monitoringData.serverPort" |
| | | :urlLink="monitoringData.url" :carId="orderData.carId" /> |
| | | </div> |
| | | </div> --> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | |
| | | methods: { |
| | | initData(orderData = {}, monitoringData = {}, travelData = []) { |
| | | this.orderData = orderData |
| | | this.monitoringData = monitoringData |
| | | // this.monitoringData = monitoringData |
| | | this.travelData = travelData |
| | | this.dialogVisible = true |
| | | }, |
| | |
| | | showDetail(row) { |
| | | this.loading = true |
| | | Promise.all([getOrderInfo(row.id), getOrderTravel({ id: row.id })]).then(res => { |
| | | getOrderMonitoring({ id: row.id }).then(resp => { |
| | | this.$refs.detailModal.initData(res[0], resp, res[1]) |
| | | this.loading = false |
| | | }).catch(err => { |
| | | // getOrderMonitoring({ id: row.id }).then(resp => { |
| | | // this.$refs.detailModal.initData(res[0], resp, res[1]) |
| | | // this.loading = false |
| | | // }).catch(err => { |
| | | this.$refs.detailModal.initData(res[0], {}, res[1]) |
| | | this.loading = false |
| | | }) |
| | | // }) |
| | | }).catch(err => { |
| | | this.loading = false |
| | | }) |
| | |
| | | </el-col> |
| | | <el-col :span="20"> |
| | | <div class="grid-content" style=""> |
| | | <PlayLive :serverPort="serverPort" :serverIp="serverIp" :carId="$route.query.id" |
| | | :urlLink="monitoringData.url" /> |
| | | <PlayLive v-if="urlLink" :serverPort="serverPort" :serverIp="serverIp" :carId="$route.query.id" |
| | | :urlLink="urlLink" @video-error="handleVideoError" /> |
| | | <div v-else class="empty-state"> |
| | | <el-empty description="请选择时间范围并点击查询获取视频" :image-size="80"></el-empty> |
| | | </div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | |
| | | } |
| | | }, |
| | | serverIp: '', |
| | | serverPort: '' |
| | | serverPort: '', |
| | | urlLink: '' |
| | | } |
| | | }, |
| | | created() { |
| | |
| | | return; |
| | | } |
| | | |
| | | // 重置视频链接 |
| | | this.urlLink = ''; |
| | | this.serverIp = ''; |
| | | this.serverPort = ''; |
| | | |
| | | getPlaybackVideo({ |
| | | startTime: new Date(this.searchForm.startTime).getTime(), |
| | | endTime: new Date(this.searchForm.endTime).getTime(), |
| | |
| | | if (res && res.serverIp && res.serverPort) { |
| | | // 构建完整的视频流地址 |
| | | this.serverIp = res.serverIp; |
| | | this.serverPort = res.serverPort |
| | | this.serverPort = res.serverPort; |
| | | this.urlLink = res.url; |
| | | } else { |
| | | this.$message.error('未获取到视频地址'); |
| | | } |
| | |
| | | this.$message.error('获取视频失败,请稍后重试'); |
| | | }); |
| | | }, |
| | | handleVideoError() { |
| | | this.urlLink = ''; |
| | | this.$message.error('视频加载失败,请稍后重试'); |
| | | }, |
| | | resetForm() { |
| | | this.searchForm.startTime = ''; |
| | | this.searchForm.endTime = ''; |