123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- <template>
- <div class="container-ln">
- <customHeader>潞宁新增</customHeader>
- <div class="video-ln" v-if="addrList.length > 0">
- <div v-for="(item, index) in addrList" :key="index" class="video-module">
- <!-- <div class="player-name">{{ item.name + index }}</div> -->
- <div style="padding-top:3px">
- <template v-if="item.addr.startsWith('rtsp://')">
- <video :id="`video${index}`" muted autoplay></video>
- <div class="click-box" @dblclick="goFullScreen(`video${index}`)"></div>
- </template>
- <template v-else>
- <div :id="'player' + index"></div>
- </template>
- </div>
- </div>
- </div>
- <div class="content-ln">
- <a-tabs class="tabs-box" type="card" v-model:activeKey="activeKey" @change="tabChange" id="tabsBox">
- <a-tab-pane key="1" tab="安全监控">
- <template v-if="deviceType == 'fan' && activeKey == '1'">
- <GroupMonitorTable :dataSource="dataSource" :columnsType="`${deviceType}_monitor`" />
- </template>
- <template v-else-if="activeKey == '1' && deviceType">
- <template v-if="hasPermission('btn:noGb')">
- <MonitorTable ref="monitorTable" :columnsType="`${deviceType}_monitor`"
- :dataSource="dataSource" design-scope="device_monitor" :isShowPagination="false"
- :isShowActionColumn="false" :is-show-select="false" title="设备监测" :scroll="{ y: 360 }" />
- </template>
- <template v-else>
- <MonitorTable ref="monitorTable" :columnsType="`${deviceType}_monitor`"
- :dataSource="dataSource" design-scope="device_monitor" :isShowPagination="false"
- :isShowActionColumn="false" :is-show-select="false" title="设备监测"
- :form-config="undefined" :scroll="{ y: 650 }">
- <template #filterCell="{ column, record }">
- <template v-if="deviceType.startsWith('gate')">
- <template
- v-if="record.frontGateOpenCtrl == 1 || record.frontGateOpenCtrl === true">
- <a-tag
- v-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 0 && record.frontGateClose == 0"
- color="red">正在打开</a-tag>
- <a-tag v-else-if="column.dataIndex === 'frontGateOpen'"
- color="processing">打开</a-tag>
- </template>
- <template
- v-else-if="record.frontGateOpenCtrl == 0 || record.frontGateOpenCtrl === false">
- <a-tag
- v-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 0 && record.frontGateClose == 0"
- color="red">正在关闭</a-tag>
- <a-tag
- v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 0 && record.frontGateClose == 1"
- color="default">关闭</a-tag>
- <a-tag
- v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 1 && record.frontGateClose == 0"
- color="default">打开</a-tag>
- </template>
- <template
- v-if="record.rearGateOpenCtrl == 1 || record.rearGateOpenCtrl === true">
- <a-tag
- v-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 0 && record.rearGateClose == 0"
- color="red">正在打开</a-tag>
- <a-tag v-else-if="column.dataIndex === 'rearGateOpen'"
- color="processing">打开</a-tag>
- </template>
- <template
- v-else-if="record.rearGateOpenCtrl == 0 || record.rearGateOpenCtrl === false">
- <a-tag
- v-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 0 && record.rearGateClose == 0"
- color="red">正在关闭</a-tag>
- <a-tag
- v-else-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 0 && record.rearGateClose == 1"
- color="default">关闭</a-tag>
- <a-tag
- v-else-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 1 && record.rearGateClose == 0"
- color="default">打开</a-tag>
- </template>
- </template>
- <template v-if="deviceType.startsWith('windrect')">
- <a-tag v-if="column.dataIndex === 'sign'"
- :color="record.sign == '0' ? '#95CF65' : record.sign == 1 ? '#4590EA' : '#9876AA'">
- {{ record.sign == '0' ? '高位' : record.sign == 1 ? '中位' : '低位' }}</a-tag>
- <template
- v-if="record && column && column.dataIndex === 'isRun' && record.isRun">
- <a-tag v-if="record.isRun == -2 || record.isRun == -1"
- :color="record.isRun == -2 ? '#95CF65' : '#ED5700'">{{
- record.isRun == -2 ? '空闲' : '等待'
- }}</a-tag>
- <a-tag v-else-if="record.isRun == 100" color="#4693FF">完成</a-tag>
- <Progress v-else :percent="Number(record.isRun)" size="small"
- status="active" />
- </template>
- </template>
- <template v-if="deviceType.startsWith('safetymonitor')">
- <div v-if="!record.devicename && column.dataIndex === 'devicename'">-</div>
- <div v-if="!record.V && column.dataIndex === 'V'">-</div>
- <div v-if="!record.PointUnit && column.dataIndex === 'PointUnit'">-</div>
- <div v-if="!record.highRange && column.dataIndex === 'highRange'">-</div>
- <div v-if="!record.lowRange && column.dataIndex === 'lowRange'">-</div>
- <div v-if="!record.dataTypeName && column.dataIndex === 'dataTypeName'">-</div>
- </template>
- <a-tag v-if="column.dataIndex === 'warnFlag'"
- :color="record.warnFlag == '0' ? 'green' : record.warnFlag == 1 ? '#FF5812' : 'gray'">
- {{ record.warnFlag == '0' ? '正常' : record.warnFlag == 1 ? '报警' : record.warnFlag
- == 2 ? '断开' : '未监测'
- }}</a-tag>
- <a-tag v-if="column.dataIndex === 'netStatus'"
- :color="record.netStatus == '0' ? '#f00' : 'green'">{{
- record.netStatus == '0' ? '断开' : '连接'
- }}</a-tag>
- </template>
- </MonitorTable>
- </template>
- </template>
- </a-tab-pane>
- <a-tab-pane key="2" tab="瓦斯抽放">
- <MonitorTable ref="monitorTable" :columnsType="`${deviceType}_monitor`" :dataSource="dataSource"
- design-scope="device_monitor" :isShowPagination="false" :isShowActionColumn="false"
- :is-show-select="false" title="设备监测" :form-config="undefined" :scroll="{ y: 360 }">
- <template #filterCell="{ column, record }">
- <template v-if="deviceType.startsWith('gate')">
- <template v-if="record.frontGateOpenCtrl == 1 || record.frontGateOpenCtrl === true">
- <a-tag
- v-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 0 && record.frontGateClose == 0"
- color="red">正在打开</a-tag>
- <a-tag v-else-if="column.dataIndex === 'frontGateOpen'"
- color="processing">打开</a-tag>
- </template>
- <template
- v-else-if="record.frontGateOpenCtrl == 0 || record.frontGateOpenCtrl === false">
- <a-tag
- v-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 0 && record.frontGateClose == 0"
- color="red">正在关闭</a-tag>
- <a-tag
- v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 0 && record.frontGateClose == 1"
- color="default">关闭</a-tag>
- <a-tag
- v-else-if="column.dataIndex === 'frontGateOpen' && record.frontGateOpen == 1 && record.frontGateClose == 0"
- color="default">打开</a-tag>
- </template>
- <template v-if="record.rearGateOpenCtrl == 1 || record.rearGateOpenCtrl === true">
- <a-tag
- v-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 0 && record.rearGateClose == 0"
- color="red">正在打开</a-tag>
- <a-tag v-else-if="column.dataIndex === 'rearGateOpen'" color="processing">打开</a-tag>
- </template>
- <template v-else-if="record.rearGateOpenCtrl == 0 || record.rearGateOpenCtrl === false">
- <a-tag
- v-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 0 && record.rearGateClose == 0"
- color="red">正在关闭</a-tag>
- <a-tag
- v-else-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 0 && record.rearGateClose == 1"
- color="default">关闭</a-tag>
- <a-tag
- v-else-if="column.dataIndex === 'rearGateOpen' && record.rearGateOpen == 1 && record.rearGateClose == 0"
- color="default">打开</a-tag>
- </template>
- </template>
- <template v-if="deviceType.startsWith('windrect')">
- <a-tag v-if="column.dataIndex === 'sign'"
- :color="record.sign == '0' ? '#95CF65' : record.sign == 1 ? '#4590EA' : '#9876AA'">
- {{ record.sign == '0' ? '高位' : record.sign == 1 ? '中位' : '低位' }}</a-tag>
- <template v-if="record && column && column.dataIndex === 'isRun' && record.isRun">
- <a-tag v-if="record.isRun == -2 || record.isRun == -1"
- :color="record.isRun == -2 ? '#95CF65' : '#ED5700'">{{
- record.isRun == -2 ? '空闲' : '等待'
- }}</a-tag>
- <a-tag v-else-if="record.isRun == 100" color="#4693FF">完成</a-tag>
- <Progress v-else :percent="Number(record.isRun)" size="small" status="active" />
- </template>
- </template>
- <template v-if="deviceType.startsWith('safetymonitor')">
- <div v-if="!record.devicename && column.dataIndex === 'devicename'">-</div>
- <div v-if="!record.V && column.dataIndex === 'V'">-</div>
- <div v-if="!record.PointUnit && column.dataIndex === 'PointUnit'">-</div>
- <div v-if="!record.highRange && column.dataIndex === 'highRange'">-</div>
- <div v-if="!record.lowRange && column.dataIndex === 'lowRange'">-</div>
- <div v-if="!record.dataTypeName && column.dataIndex === 'dataTypeName'">-</div>
- </template>
- <a-tag v-if="column.dataIndex === 'warnFlag'"
- :color="record.warnFlag == '0' ? 'green' : record.warnFlag == 1 ? '#FF5812' : 'gray'">
- {{ record.warnFlag == '0' ? '正常' : record.warnFlag == 1 ? '报警' : record.warnFlag
- == 2 ? '断开' : '未监测'
- }}</a-tag>
- <a-tag v-if="column.dataIndex === 'netStatus'"
- :color="record.netStatus == '0' ? '#f00' : 'green'">{{
- record.netStatus == '0' ? '断开' : '连接'
- }}</a-tag>
- </template>
- </MonitorTable>
- </a-tab-pane>
- </a-tabs>
- </div>
- </div>
- </template>
- <script setup lang="ts">
- import { ref, reactive, onMounted, onUnmounted, shallowRef, nextTick } from 'vue'
- import customHeader from '/@/components/vent/customHeader.vue';
- import GroupMonitorTable from '../comment/GroupMonitorTable.vue';
- import MonitorTable from '../comment/MonitorTable.vue';
- import { cameraAddr, } from '../camera/camera.api'
- import { list } from '../safetyMonitor/safety.api';
- import Player, { I18N } from 'xgplayer';
- import ZH from 'xgplayer/es/lang/zh-cn'
- import HlsPlugin from 'xgplayer-hls';
- import FlvPlugin from 'xgplayer-flv';
- import 'xgplayer/dist/index.min.css';
- import { usePermission } from '/@/hooks/web/usePermission';
- const { hasPermission } = usePermission();
- const activeKey = ref('1'); // tab key
- const deviceType = ref('safetymonitor'); // 监测设备类型
- const dataSource = shallowRef([]); // 实时监测数据
- const monitorTable = ref();
- const playerList = ref([])
- const webRtcServerList = <any[]>[]
- let addrList = ref<{ name: string, addr: string, }[]>([])
- I18N.use(ZH)
- // https获取监测数据
- let timer: null | NodeJS.Timeout = null;
- function getMonitor(flag?) {
- if (deviceType.value) {
- if (timer) timer = null;
- timer = setTimeout(
- async () => {
- await getDataSource();
- if (timer) {
- getMonitor();
- }
- },
- flag ? 0 : 1000
- );
- }
- }
- //tabs选项切换
- async function tabChange(activeKeyVal) {
- activeKey.value = activeKeyVal;
- clearTimeout(timer);
- if (activeKey.value != '1') {
- deviceType.value = 'wasichoufang'
- } else {
- deviceType.value = 'safetymonitor'
- }
- getMonitor(true);
- }
- //获取安全监控-实时监测数据
- async function getDataSource() {
- const res = await list({ devicetype: deviceType.value, filterParams: {} });
- if (res.msgTxt.length > 0) {
- dataSource.value = [];
- let dataArr = res.msgTxt[0].datalist || [];
- dataArr.filter((data: any) => {
- const readData = data.readData;
- return Object.assign(data, readData);
- });
- if (deviceType.value == 'safetymonitor') {
- dataSource.value = dataArr;
- dataSource.value = dataArr.filter(v => v.strinstallpos == '31102采面钻孔下风侧一氧化碳' || v.strinstallpos == '31102采面钻孔下风侧甲烷')
- } else {
- dataSource.value = dataArr;
- }
- } else {
- dataSource.value = [];
- }
- }
- function getVideo() {
- const ip = VUE_APP_URL.webRtcUrl;
- for (let i = 0; i < addrList.value.length; i++) {
- const item = addrList.value[i]
- if (item.addr.startsWith('rtsp://')) {
- const dom = document.getElementById('video' + i) as HTMLVideoElement
- dom.muted = true;
- dom.volume = 0
- const webRtcServer = new window['WebRtcStreamer'](dom, location.protocol + ip)
- webRtcServerList.push(webRtcServer)
- webRtcServer.connect(item.addr)
- } else {
- setNoRtspVideo('player' + i, item.addr)
- }
- }
- }
- async function getVideoAddrs() {
- clearCamera();
- playerList.value = []
- const cameraList = <{ name: string, addr: string }[]>[]
- let videoList = [
- { name: '摄像头1', addr: "http://0.0.0.0:8080/live/16.flv", devicekind: 'wasichoufang' },
- { name: '摄像头2', addr: "http://0.0.0.0:8080/live/17.flv", devicekind: 'wasichoufang' }
- ]
- for (let i = 0; i < videoList.length; i++) {
- const item = videoList[i];
- if (item['devicekind'] === 'toHKRtsp') {
- // 从海康平台接口获取视频流
- try {
- const data = await cameraAddr({ cameraCode: item['addr'] });
- if (data) {
- cameraList.push({ name: item['name'], addr: data['url'] });
- }
- } catch (error) {
- }
- } else {
- if (item['addr'].includes('0.0.0.0')) {
- item['addr'] = item['addr'].replace('0.0.0.0', window.location.hostname)
- }
- cameraList.push({ name: item['name'], addr: item['addr'] });
- }
- }
- addrList.value = cameraList
- getVideo()
- }
- function goFullScreen(domId) {
- const videoDom = document.getElementById(domId) as HTMLVideoElement
- if (videoDom.requestFullscreen) {
- videoDom.requestFullscreen()
- videoDom.play()
- } else if (videoDom.mozRequestFullscreen) {
- videoDom.mozRequestFullscreen()
- videoDom.play()
- } else if (videoDom.webkitRequestFullscreen) {
- videoDom.webkitRequestFullscreen()
- videoDom.play()
- } else if (videoDom.msRequestFullscreen) {
- videoDom.msRequestFullscreen()
- videoDom.play()
- }
- }
- function clearCamera() {
- const num = webRtcServerList.length
- for (let i = 0; i < num; i++) {
- webRtcServerList[i].disconnect()
- webRtcServerList[i] = null
- }
- for (let i = 0; i < playerList.value.length; i++) {
- const player = playerList.value[i]
- if (player.destroy) player.destroy()
- }
- playerList.value = []
- }
- function setNoRtspVideo(id, videoAddr) {
- nextTick(() => {
- const fileExtension = videoAddr.split('.').pop();
- if (fileExtension === 'flv') {
- const player = new Player({
- lang: 'zh',
- id: id,
- url: videoAddr,
- width: 589,
- height: 330,
- poster: '/src/assets/images/vent/noSinge.png',
- plugins: [FlvPlugin],
- fluid: true,
- autoplay: true,
- isLive: true,
- playsinline: true,
- screenShot: true,
- whitelist: [''],
- ignores: ['time'],
- closeVideoClick: true,
- customConfig: {
- isClickPlayBack: false
- },
- flv: {
- retryCount: 3, // 重试 3 次,默认值
- retryDelay: 1000, // 每次重试间隔 1 秒,默认值
- loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
- fetchOptions: {
- // 该参数会透传给 fetch,默认值为 undefined
- mode: 'cors'
- },
- targetLatency: 10, // 直播目标延迟,默认 10 秒
- maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
- disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
- maxJumpDistance: 10,
- }
- });
- playerList.value.push(player)
- }
- if (fileExtension === 'm3u8') {
- let player
- if (document.createElement('video').canPlayType('application/vnd.apple.mpegurl')) {
- // 原生支持 hls 播放
- player = new Player({
- lang: 'zh',
- id: id,
- url: videoAddr,
- width: 589,
- height: 330,
- isLive: true,
- autoplay: true,
- autoplayMuted: true,
- cors: true,
- poster: '/src/assets/images/vent/noSinge.png',
- hls: {
- retryCount: 3, // 重试 3 次,默认值
- retryDelay: 1000, // 每次重试间隔 1 秒,默认值
- loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
- fetchOptions: {
- // 该参数会透传给 fetch,默认值为 undefined
- mode: 'cors'
- },
- targetLatency: 10, // 直播目标延迟,默认 10 秒
- maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
- disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
- maxJumpDistance: 10,
- }
- })
- } else if (HlsPlugin.isSupported()) { // 第一步
- player = new Player({
- lang: 'zh',
- id: id,
- url: videoAddr,
- width: 589,
- height: 330,
- isLive: true,
- autoplay: true,
- autoplayMuted: true,
- plugins: [HlsPlugin], // 第二步
- poster: '/src/assets/images/vent/noSinge.png',
- hls: {
- retryCount: 3, // 重试 3 次,默认值
- retryDelay: 1000, // 每次重试间隔 1 秒,默认值
- loadTimeout: 10000, // 请求超时时间为 10 秒,默认值
- fetchOptions: {
- // 该参数会透传给 fetch,默认值为 undefined
- mode: 'cors'
- },
- targetLatency: 10, // 直播目标延迟,默认 10 秒
- maxLatency: 20, // 直播允许的最大延迟,默认 20 秒
- disconnectTime: 10, // 直播断流时间,默认 0 秒,(独立使用时等于 maxLatency)
- maxJumpDistance: 10,
- }
- })
- }
- playerList.value.push(player)
- }
- })
- }
- onMounted(() => {
- getVideoAddrs()
- getMonitor(true);
- })
- onUnmounted(() => {
- clearCamera()
- if (timer) {
- clearTimeout(timer);
- }
- timer = undefined;
- });
- </script>
- <style lang="less" scoped>
- @import '/@/design/vent/color.less';
- @import '/@/design/vent/modal.less';
- .container-ln {
- position: relative;
- width: 100%;
- height: 100%;
- padding: 0px 10px;
- box-sizing: border-box;
- .video-ln {
- display: flex;
- justify-content: space-around;
- align-items: center;
- width: 100%;
- height: 50%;
- padding-top: 80px;
- .video-module {
- position: relative;
- width: 626px;
- height: 370px;
- padding: 17px 18px;
- background: url('/@/assets/images/vent/camera_bg.png');
- background-size: 100% 100%;
- margin: 10px;
- // .player-name {
- // font-size: 14px;
- // position: absolute;
- // top: 15px;
- // right: 15px;
- // color: #fff;
- // background-color: hsla(0, 0%, 50%, .5);
- // border-radius: 2px;
- // padding: 1px 5px;
- // max-width: 120px;
- // overflow: hidden;
- // white-space: nowrap;
- // text-overflow: ellipsis;
- // z-index: 999;
- // }
- .click-box {
- position: absolute;
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
- }
- }
- }
- .content-ln {
- width: 100%;
- height: 50%;
- .tabs-box {
- width: calc(100% - 12px) !important;
- height: 100% !important;
- bottom: 3px !important;
- }
- }
- }
- @ventSpace: zxm;
- :deep(.@{ventSpace}-tabs-tabpane-active) {
- height: 100%;
- border: 1px solid #44d3ff70;
- border-radius: 2px;
- -webkit-backdrop-filter: blur(8px);
- box-shadow: 0 0 20px #44b4ff33 inset;
- background-color: #ffffff11;
- overflow-y: auto;
- }
- :deep(.@{ventSpace}-tabs-card) {
- .@{ventSpace}-tabs-tab {
- background: linear-gradient(#2cd1ff55, #1eb0ff55);
- border-color: #74e9fe;
- border-radius: 0%;
- &:hover {
- color: #64d5ff;
- }
- }
- .@{ventSpace}-tabs-content {
- height: 100% !important;
- }
- .@{ventSpace}-tabs-tab.@{ventSpace}-tabs-tab-active .@{ventSpace}-tabs-tab-btn {
- color: aqua;
- }
- .@{ventSpace}-tabs-nav::before {
- border-color: #74e9fe;
- }
- .@{ventSpace}-table-cell-row-hover {
- background: #264d8833 !important;
- }
- .@{ventSpace}-table-row-selected {
- background: #00c0a311 !important;
- td {
- background-color: #00000000 !important;
- }
- }
- .@{ventSpace}-table-thead {
- // background: linear-gradient(#004a8655 0%, #004a86aa 10%) !important;
- background: #3d9dd45d !important;
- &>tr>th,
- .@{ventSpace}-table-column-title {
- // color: #70f9fc !important;
- border-color: #84f2ff !important;
- border-left: none !important;
- border-right: none !important;
- padding: 7px;
- }
- }
- .@{ventSpace}-table-tbody {
- tr>td {
- padding: 12px;
- }
- }
- .@{ventSpace}-table-tbody>tr:hover.@{ventSpace}-table-row>td {
- background-color: #26648855 !important;
- }
- .jeecg-basic-table-row__striped {
- // background: #97efff11 !important;
- td {
- background-color: #97efff11 !important;
- }
- }
- }
- </style>
|