| | |
| | | <template> |
| | | <div style="height: 100%;"> |
| | | <div style="height: 100%; position: relative;"> |
| | | <video id="video" style="height: 100%;width: 100%;" muted controls></video> |
| | | <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> |
| | | <p style="margin: 8px 0; font-size: 16px;">视频播放失败</p> |
| | | <p style="margin: 8px 0; font-size: 16px;">请稍后重试</p> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | props: { |
| | | serverIp: { |
| | | type: String, |
| | | required: '' |
| | | required: null |
| | | }, |
| | | serverPort: { |
| | | type: Number, |
| | |
| | | type: Number, |
| | | required: null |
| | | }, |
| | | urlLink: { |
| | | type: String, |
| | | required: null, |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | flvPlayer: null, |
| | | timer: null, |
| | | showError: false, |
| | | } |
| | | }, |
| | | mounted() { |
| | |
| | | }, |
| | | methods: { |
| | | playDetection() { |
| | | this.showError = false; |
| | | if (flvjs.isSupported()) { |
| | | playDetection(this.carId).then(res => { |
| | | this.flvPlayer = flvjs.createPlayer({ |
| | |
| | | cors: true,//是否开启跨域 |
| | | hasAudio: false,//是否开启音频 |
| | | hasVideo: true,//是否开启视频 |
| | | url: `http://${this.serverIp}:${this.serverPort}/live?port=1935&app=flv&stream=${this.carId}`, // 后端拿到的视频路径 |
| | | // url: `http://${this.serverIp}:${this.serverPort}/live?port=1935&app=flv&stream=${this.carId}`, // 后端拿到的视频路径 |
| | | url: this.urlLink, // 后端拿到的视频路径 |
| | | enableWorker: true, //启用 Web Worker 进程来加速视频的解码和处理过程 |
| | | enableStashBuffer: false, // 启用数据缓存机制,提高视频的流畅度和稳定性。 |
| | | stashInitialSize: 1024, // 初始缓存大小。单位:字节。建议针对直播:调整为1024kb |
| | | stashInitialTime: 0.2, // 缓存初始时间。单位:秒。建议针对直播:调整为200毫秒 |
| | | seekType: 'range', // 建议将其设置为“range”模式,以便更快地加载视频数据,提高视频的实时性。 |
| | | seekType: 'range', // 建议将其设置为"range"模式,以便更快地加载视频数据,提高视频的实时性。 |
| | | lazyLoad: false, //关闭懒加载模式,从而提高视频的实时性。建议针对直播:调整为false |
| | | lazyLoadMaxDuration: 0.2, // 懒加载的最大时长。单位:秒。建议针对直播:调整为200毫秒 |
| | | deferLoadAfterSourceOpen: false // 不预先加载视频数据,在 MSE(Media Source Extensions)打开后立即加载数据,提高视频的实时性。建议针对直播:调整为false |
| | |
| | | playDetection(this.carId) |
| | | }, 5000) |
| | | }).catch(err => { |
| | | this.showError = true; |
| | | this.destroyPlayer(); |
| | | }) |
| | | // 错误监听 |
| | | this.flvPlayer.on('error', (err) => { |
| | | this.showError = true; |
| | | this.destroyPlayer(); |
| | | }); |
| | | }) |
| | |
| | | } |
| | | </script> |
| | | |
| | | <style></style> |
| | | <style scoped> |
| | | /* 移除之前的样式,使用内联样式确保样式生效 */ |
| | | </style> |
| | |
| | | <template>
|
| | | <div class="sticky top0 layout">
|
| | | <div class="header relative">
|
| | | <div class="header relative" style="align-items: flex-end;">
|
| | | <div @click="$router.push('/home')" class="title">
|
| | | <img src="@/assets/logo.png" alt="">
|
| | | 射洪“两客一危”监管平台
|
| | | <span>
|
| | | 射洪两客一危监管平台
|
| | | </span>
|
| | | </div>
|
| | | <div></div>
|
| | | <div class="flex a-center pr--40">
|
| | |
| | | <div v-if="!item.meta || !item.meta.title" class="h100">
|
| | | <template v-for="(item2, index2) in item.children">
|
| | | <div :key="index2" @click="pushPath(item2.path)" v-if="!item2.meta.hide"
|
| | | class="flex a-center j-center h100 w--160 menuItemHover pointer"
|
| | | class="flex a-center j-center h100 br--18 w--160 menuItemHover pointer"
|
| | | :class="item2.path == $route.path && 'bgColor2'">
|
| | | <img v-if="item2.meta.icon" :src="require(`@/assets/routerIcon/${item2.meta.icon}.png`)"
|
| | | class="w--15 h--15 mr--12 shrink0" />
|
| | | class="w--40 h--40 mr--12 shrink0" />
|
| | | <div class="color1">
|
| | | {{ item2.meta.title }}
|
| | | </div>
|
| | |
| | | </template>
|
| | | </div>
|
| | | <div v-else :class="$route.path.includes('systemManage') && 'bgColor2'"
|
| | | class="h100 w--160 menuItemHover dropdown" @mouseenter="routerDropdown(true)"
|
| | | class="h100 w--160 br--18 menuItemHover dropdown" @mouseenter="routerDropdown(true)"
|
| | | @mouseleave="routerDropdown(false)">
|
| | | <div class="flex a-center j-center h100">
|
| | | <img :src="require(`@/assets/routerIcon/${item.meta.icon}.png`)"
|
| | | class="w--15 h--15 mr--12 shrink0" />
|
| | | class="w--40 h--40 mr--12 shrink0" />
|
| | | <div class="color1">
|
| | | {{ item.meta.title }}
|
| | | </div>
|
| | |
| | | }
|
| | |
|
| | | .bgColor1 {
|
| | | background-color: #0E6EFD;
|
| | | background-color: #3367ce;
|
| | | }
|
| | |
|
| | | .bgColor2 {
|
| | | background: #0D55B9;
|
| | | background: #2b5ab6;
|
| | | }
|
| | |
|
| | | .color1 {
|
| | |
| | | }
|
| | |
|
| | | .color2 {
|
| | | color: rgba(0, 0, 0, .6);
|
| | | // color: rgba(0, 0, 0, .6);
|
| | | color: #fff;
|
| | | }
|
| | |
|
| | | .header {
|
| | | height: 80px;
|
| | | background: #fff;
|
| | | background: #3367ce;
|
| | | background-image: url('../assets/title.png');
|
| | | background-size: 100% 100%;
|
| | | display: flex;
|
| | | align-items: center;
|
| | | justify-content: space-between;
|
| | |
| | | display: flex;
|
| | | justify-content: center;
|
| | | font-weight: 600;
|
| | | font-size: 24px;
|
| | | font-size: 30px;
|
| | | align-items: center;
|
| | | position: absolute;
|
| | | top: 50%;
|
| | | left: 50%;
|
| | | top: 38%;
|
| | | left: 48.5%;
|
| | | transform: translate(-50%, -50%);
|
| | | color: rgba(0, 0, 0, .8);
|
| | | // color: rgba(0, 0, 0, .8);
|
| | | color: #fff;
|
| | |
|
| | | img {
|
| | | width: 40px;
|
| | | height: 40px;
|
| | | margin-right: 10px;
|
| | | width: 50px;
|
| | | height: 50px;
|
| | | margin-right: 25px;
|
| | | }
|
| | |
|
| | | span {
|
| | | letter-spacing: 4px;
|
| | | text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.3); // 新增字体阴影
|
| | | }
|
| | | }
|
| | | }
|
| | |
| | | height: 60px;
|
| | | display: flex;
|
| | | align-items: center;
|
| | | justify-content: center;
|
| | | justify-content: space-between;
|
| | | padding: 0 220px;
|
| | | }
|
| | |
|
| | | .menuItemHover:hover {
|
| | | background: #0D55B9;
|
| | | border-radius: 18px;
|
| | | background: #2b5ab6;
|
| | | }
|
| | |
|
| | | .dropdown {
|
| | |
| | | <!-- 行程监控 --> |
| | | <div v-if="tabPosition == 'monitoring'"> |
| | | <PlayLive :serverIp="monitoringData.serverIp" :serverPort="monitoringData.serverPort" |
| | | :carId="orderData.carId" /> |
| | | :urlLink="monitoringData.url" :carId="orderData.carId" /> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | |
| | | cors: true, //是否开启跨域 |
| | | hasAudio: false, //是否开启音频 |
| | | hasVideo: true, //是否开启视频 |
| | | url: `http://${this.videoObj.serverIp}:${this.videoObj.serverPort}/live?port=1935&app=flv&stream=${this.$route.query.id}`, // 后端拿到的视频路径 |
| | | // url: `http://${this.videoObj.serverIp}:${this.videoObj.serverPort}/live?port=1935&app=flv&stream=${this.$route.query.id}`, // 后端拿到的视频路径 |
| | | url: this.videoObj.url, // 后端拿到的视频路径 |
| | | enableWorker: true, //启用 Web Worker 进程来加速视频的解码和处理过程 |
| | | enableStashBuffer: false, // 启用数据缓存机制,提高视频的流畅度和稳定性。 |
| | | stashInitialSize: 1024, // 初始缓存大小。单位:字节。建议针对直播:调整为1024kb |
| | |
| | | <div class="carCount"> |
| | | <div class="title">车辆统计</div> |
| | | <div class="fir"> |
| | | <div |
| | | class="countCard" |
| | | v-for="(item, index) in carCountData.slice(0, 3)" |
| | | :key="item.id" |
| | | @click="toCarManage(item.name)" |
| | | > |
| | | <div class="countCard" v-for="(item, index) in carCountData.slice(0, 3)" :key="item.id" |
| | | @click="toCarManage(item.name)"> |
| | | <img class="iconImg" :src="imgList[index]" /> |
| | | <div> |
| | | <div class="name">{{ item.name || "" }}(辆)</div> |
| | |
| | | </div> |
| | | </div> |
| | | <div class="sec"> |
| | | <div |
| | | class="countCard" |
| | | v-for="(item, index) in carCountData.slice(3, 7)" |
| | | @click="toCarManage(item.name)" |
| | | :key="item.id" |
| | | > |
| | | <div class="countCard" v-for="(item, index) in carCountData.slice(3, 7)" @click="toCarManage(item.name)" |
| | | :key="item.id"> |
| | | <img class="iconImg" :src="imgList[index + 3]" /> |
| | | <div> |
| | | <div class="name">{{ item.name || "" }}(辆)</div> |
| | |
| | | <div class="name">在线</div> |
| | | <div class="num">{{ carStatusData.online || 0 }}</div> |
| | | </div> |
| | | <el-progress |
| | | type="circle" |
| | | :width="20" |
| | | :show-text="false" |
| | | stroke-linecap="butt" |
| | | :percentage="carStatusData.onlinePercent" |
| | | color="rgba(91, 143, 249, 1)" |
| | | define-back-color="rgba(91, 143, 249, 0.25)" |
| | | class="progressCard" |
| | | ></el-progress> |
| | | <el-progress type="circle" :width="20" :show-text="false" stroke-linecap="butt" |
| | | :percentage="carStatusData.onlinePercent" color="rgba(91, 143, 249, 1)" |
| | | define-back-color="rgba(91, 143, 249, 0.25)" class="progressCard"></el-progress> |
| | | </div> |
| | | <div class="statusLine"></div> |
| | | <div class="statusCard"> |
| | |
| | | <div class="name">离线</div> |
| | | <div class="num">{{ carStatusData.offline || 0 }}</div> |
| | | </div> |
| | | <el-progress |
| | | type="circle" |
| | | :width="20" |
| | | :show-text="false" |
| | | stroke-linecap="butt" |
| | | :percentage="carStatusData.offlinePercent" |
| | | color="rgba(93, 112, 146, 1)" |
| | | define-back-color="rgba(93, 112, 146, 0.25)" |
| | | class="progressCard" |
| | | ></el-progress> |
| | | <el-progress type="circle" :width="20" :show-text="false" stroke-linecap="butt" |
| | | :percentage="carStatusData.offlinePercent" color="rgba(93, 112, 146, 1)" |
| | | define-back-color="rgba(93, 112, 146, 0.25)" class="progressCard"></el-progress> |
| | | </div> |
| | | </div> |
| | | <div class="statusSec"> |
| | |
| | | <div class="name">故障</div> |
| | | <div class="num">{{ carStatusData.breakdown || 0 }}</div> |
| | | </div> |
| | | <el-progress |
| | | type="circle" |
| | | :width="20" |
| | | :show-text="false" |
| | | stroke-linecap="butt" |
| | | :percentage="carStatusData.breakdownPercent" |
| | | color="rgba(253, 83, 118, 1)" |
| | | define-back-color="rgba(253, 83, 118, 0.25)" |
| | | class="progressCard" |
| | | ></el-progress> |
| | | <el-progress type="circle" :width="20" :show-text="false" stroke-linecap="butt" |
| | | :percentage="carStatusData.breakdownPercent" color="rgba(253, 83, 118, 1)" |
| | | define-back-color="rgba(253, 83, 118, 0.25)" class="progressCard"></el-progress> |
| | | </div> |
| | | <div class="statusLine"></div> |
| | | <div class="statusCard"> |
| | |
| | | <div class="name">异常</div> |
| | | <div class="num">{{ carStatusData.abnormal || 0 }}</div> |
| | | </div> |
| | | <el-progress |
| | | type="circle" |
| | | :width="20" |
| | | :show-text="false" |
| | | stroke-linecap="butt" |
| | | :percentage="carStatusData.abnormalPercent" |
| | | color="rgba(246, 189, 22, 1)" |
| | | define-back-color="rgba(246, 189, 22, 0.25)" |
| | | class="progressCard" |
| | | ></el-progress> |
| | | <el-progress type="circle" :width="20" :show-text="false" stroke-linecap="butt" |
| | | :percentage="carStatusData.abnormalPercent" color="rgba(246, 189, 22, 1)" |
| | | define-back-color="rgba(246, 189, 22, 0.25)" class="progressCard"></el-progress> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <div class="todayWarn"> |
| | | <div class="title">今日预警</div> |
| | | <div class="warnList" v-if="warnList.length > 0"> |
| | | <div |
| | | class="warnItem" |
| | | v-for="(item, index) in warnList" |
| | | :key="index" |
| | | :class=" |
| | | item.warnLevel |
| | | <div class="warnItem" v-for="(item, index) in warnList" :key="index" :class="item.warnLevel |
| | | ? ['oneWarn', 'twoWarn', 'threeWarn', 'fourWarn'][ |
| | | item.warnLevel - 1 |
| | | ] |
| | | : 'fiveWarn' |
| | | " |
| | | > |
| | | "> |
| | | <div class="grade"> |
| | | {{ |
| | | item.warnLevel |
| | |
| | | <div class="rankChart" id="rankChart"> |
| | | <div class="rankItem" v-for="(item, index) in rankList" :key="index"> |
| | | <div class="left">{{ item.name }}</div> |
| | | <div |
| | | class="rankRight" |
| | | :class="[0, 1, 2].includes(index) ? 'rankColor' : ''" |
| | | > |
| | | <div class="rankRight" :class="[0, 1, 2].includes(index) ? 'rankColor' : ''"> |
| | | <div class="rank" :style="{ width: item.percentage + '%' }"></div> |
| | | </div> |
| | | </div> |
| | |
| | | serverIp: "", //监控ip |
| | | serverPort: "", //监控端口 |
| | | carId: "", //监控车辆 |
| | | urlLink: '',//视频地址 |
| | | }; |
| | | }, |
| | | watch: { |
| | |
| | | // 将RTSP流转换为FLV流 |
| | | this.serverIp = res.serverIp; |
| | | this.serverPort = res.serverPort; |
| | | this.urlLink = res.url; |
| | | } catch (error) { |
| | | console.error("获取视频地址失败", error); |
| | | return {}; |
| | |
| | | cors: true, //是否开启跨域 |
| | | hasAudio: false, //是否开启音频 |
| | | hasVideo: true, //是否开启视频 |
| | | url: `http://${this.serverIp}:${this.serverPort}/live?port=1935&app=flv&stream=${this.carId}`, // 后端拿到的视频路径 |
| | | // url: `http://${this.serverIp}:${this.serverPort}/live?port=1935&app=flv&stream=${this.carId}`, // 后端拿到的视频路径 |
| | | url: this.urlLink, // 后端拿到的视频路径 |
| | | enableWorker: true, //启用 Web Worker 进程来加速视频的解码和处理过程 |
| | | enableStashBuffer: false, // 启用数据缓存机制,提高视频的流畅度和稳定性。 |
| | | stashInitialSize: 1024, // 初始缓存大小。单位:字节。建议针对直播:调整为1024kb |
| | |
| | | </div> |
| | | </div> |
| | | <div style="display: flex;justify-content: space-between;margin-top: 15px;margin-bottom: 12px;"> |
| | | <div style="font-weight: 500;font-size: 18px;color: rgba(0, 0, 0, 0.85);line-height: 25px;">车牌号:${ |
| | | record.vehicleNumber || "" |
| | | <div style="font-weight: 500;font-size: 18px;color: rgba(0, 0, 0, 0.85);line-height: 25px;">车牌号:${record.vehicleNumber || "" |
| | | }</div> |
| | | <div style="font-weight: 500; font-size: 18px;color: rgba(0, 0, 0, 0.85);line-height: 25px;">驾驶员:${ |
| | | record.driverName || "" |
| | | <div style="font-weight: 500; font-size: 18px;color: rgba(0, 0, 0, 0.85);line-height: 25px;">驾驶员:${record.driverName || "" |
| | | }</div> |
| | | </div> |
| | | <div style="display: flex; justify-content: space-between"> |
| | | <div style="font-weight: 500; font-size: 14px; color: rgba(0, 0, 0, 0.65);line-height: 26px;width: 200px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;" title="${ |
| | | record.location |
| | | <div style="font-weight: 500; font-size: 14px; color: rgba(0, 0, 0, 0.65);line-height: 26px;width: 200px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;" title="${record.location |
| | | }">位置:${record.location}</div> |
| | | <div style="font-weight: 500;font-size: 14px; color: rgba(0, 0, 0, 0.65);line-height: 26px;">经纬度:${ |
| | | record.longitude + "," + record.latitude |
| | | <div style="font-weight: 500;font-size: 14px; color: rgba(0, 0, 0, 0.65);line-height: 26px;">经纬度:${record.longitude + "," + record.latitude |
| | | }</div> |
| | | </div> |
| | | <div style="display: flex; justify-content: space-between"> |
| | | <div style="font-weight: 500;font-size: 14px; color: rgba(0, 0, 0, 0.65);line-height: 26px;">当前时速:${ |
| | | record.speed || "" |
| | | <div style="font-weight: 500;font-size: 14px; color: rgba(0, 0, 0, 0.65);line-height: 26px;">当前时速:${record.speed || "" |
| | | }${record.speed && "km/h"}</div> |
| | | <div style="font-weight: 500;font-size: 14px; color: rgba(0, 0, 0, 0.65);line-height: 26px;">驾驶时长:${ |
| | | record.drivingTime |
| | | <div style="font-weight: 500;font-size: 14px; color: rgba(0, 0, 0, 0.65);line-height: 26px;">驾驶时长:${record.drivingTime |
| | | }</div> |
| | | </div> |
| | | <div style="margin-top: 14px;display: flex;justify-content: flex-end;align-items: center;cursor: pointer;" onclick="toCarDetail(${ |
| | | record.id |
| | | <div style="margin-top: 14px;display: flex;justify-content: flex-end;align-items: center;cursor: pointer;" onclick="toCarDetail(${record.id |
| | | })"> |
| | | <div style="font-weight: 400;font-size: 18px; color: rgba(22, 119, 255, 1);line-height: 25px;">车辆详情</div> |
| | | <img style="width:18px;height: 18px;margin-left: 8px;" src="${require("../../assets//homeImg/right.png")}" /> |
| | |
| | | height: 100%; |
| | | } |
| | | } |
| | | |
| | | .mapTop { |
| | | z-index: 99; |
| | | position: absolute; |
| | |
| | | |
| | | .countCard { |
| | | flex: 1; |
| | | background: linear-gradient( |
| | | 180deg, |
| | | background: linear-gradient(180deg, |
| | | rgba(246, 246, 252, 0) 0%, |
| | | #f3f4f8 100% |
| | | ); |
| | | #f3f4f8 100%); |
| | | box-shadow: inset 0px -1px 4px 0px #ffffff; |
| | | border-radius: 10px; |
| | | border: 1px solid #f1f1f1; |
| | |
| | | text-transform: none; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .mt-0{ |
| | | margin-top: 0 !important; |
| | | } |
| | |
| | | color: rgba(82, 196, 26, 1); |
| | | } |
| | | } |
| | | |
| | | .fiveWarn { |
| | | background: rgba(214, 219, 228, 0.3); |
| | | |
| | |
| | | <template> |
| | | <div class="bgImg"> |
| | | <div class="login_box"> |
| | | <div class="fs--40 fw-bold"> |
| | | <h1> |
| | | 射洪“两客一危”监管平台 |
| | | </h1> |
| | | <div class="logo"> |
| | | <img src="@/assets/logo.png" alt=""> |
| | | <div class="fs--25 fw-bold" style="letter-spacing: 4px;">射洪两客一危监管平台</div> |
| | | </div> |
| | | <div class="mt--20 txt-center py--20 px--20"> |
| | | <div class="fs--20 fw-bold">登陆</div> |
| | | <div class="txt-center pb--20 px--20"> |
| | | <div class="mt--20"> |
| | | <el-input class="w100" prefix-icon="el-icon-user" placeholder="账号" v-model="username" /> |
| | | </div> |
| | |
| | | }, |
| | | // 校验验证码 |
| | | matchCaseInsensitive(str, pattern) { |
| | | return str.match(new RegExp(pattern, "i")); |
| | | return str.toLowerCase() === pattern.toLowerCase(); |
| | | }, |
| | | resetCodeStr() { |
| | | this.codeStr = generateVerificationCode(); |
| | |
| | | } |
| | | |
| | | .login_box { |
| | | width: 25%; |
| | | position: absolute; |
| | | top: 50%; |
| | | left: 50%; |
| | |
| | | box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.2); |
| | | border-radius: 10px; |
| | | padding: 20px; |
| | | |
| | | .logo { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | |
| | | img { |
| | | width: 50px; |
| | | height: 50px; |
| | | margin-right: 15px; |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | .code_box { |
| | |
| | | <!-- 行程监控 --> |
| | | <div v-if="tabPosition == 'monitoring'"> |
| | | <PlayLive :serverIp="monitoringData.serverIp" :serverPort="monitoringData.serverPort" |
| | | :carId="orderData.carId" /> |
| | | :urlLink="monitoringData.url" :carId="orderData.carId" /> |
| | | </div> |
| | | </el-dialog> |
| | | </div> |
| | |
| | | </el-col> |
| | | <el-col :span="20"> |
| | | <div class="grid-content" style=""> |
| | | <PlayLive :serverPort="serverPort" :serverIp="serverIp" :carId="$route.query.id"/> |
| | | <PlayLive :serverPort="serverPort" :serverIp="serverIp" :carId="$route.query.id" |
| | | :urlLink="monitoringData.url" /> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |