123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542 |
- <template>
- <div class="camera-container">
- <div class="left-area">
- <cameraTree :selected="selected" :list="listArr" :draggable="true" @detail-node="onDetail" @on-click="onClick">
- <template #icon="{ item }">
- <template v-if="item.isFolder">
- <SvgIcon v-if="item.expanded" size="18" name="file-open" />
- <SvgIcon v-else size="18" name="file-close" />
- </template>
- <treeIcon class="iconfont" :title="item.title" v-else />
- </template>
- <template #operation="{ type }">
- <!-- <i class="iconfont icon-eyeoutlined"></i> -->
- <span style="color:#ccc;font-size:12px">详情</span>
- </template>
- </cameraTree>
- </div>
- <div class="right-area" v-if="addrList.length > 0">
- <div class="vent-flex-row-wrap" :class="addrList.length == 1 ? 'camera-box1' : 'camera-box'">
- <div v-for="(item, index) in addrList" :key="index" class="player-box">
- <div class="player-name">{{ item.name }}</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="pagination">
- <Pagination v-model:current="current" v-model:page-size="pageSize" :total="total" @change="onChange" />
- </div>
- </div>
- <div class="camera-box" v-else>
- <Empty />
- </div>
- </div>
- </template>
- <script lang="ts" setup>
- import { onMounted, onUnmounted, ref, reactive } from 'vue';
- import { useRouter } from 'vue-router';
- import { Pagination, Empty } from 'ant-design-vue';
- import { list, cameraAddr, getCameraDevKind, getDevice, getVentanalyCamera } from './camera.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 cameraTree from './common/cameraTree.vue';
- import { SvgIcon } from '/@/components/Icon';
- import treeIcon from './common/Icon/treeIcon.vue';
- //当前选中树节点
- let selected = reactive<any>({
- id: null,
- pid: null,
- title: '',
- isFolder: false,
- });
- //tree菜单列表
- let listArr = reactive<any[]>([]);
- let searchParam = reactive({
- devKind: '',
- strType: '',
- })
- I18N.use(ZH)
- let router = useRouter(); //路由
- const pageSize = ref(4)
- const current = ref(1)
- const total = ref(0)
- const playerList = ref([])
- const webRtcServerList = <any[]>[]
- let addrList = ref<{ name: string, addr: string }[]>([])
- async function getCameraDevKindList() {
- let res = await getCameraDevKind()
- if (res.length != 0) {
- listArr.length = 0
- listArr.push({
- pid: 'root',
- isFolder: true,
- expanded: true,
- title: '全部',
- id: 0,
- children: []
- })
- res.forEach(el => {
- el.pid = 0
- el.isFolder = true,
- el.expanded = false,
- el.title = el.itemText
- el.id = el.subDictId
- el.children = []
- listArr[0].children.push(el)
- })
- selected.id = listArr[0].id;
- selected.pid = listArr[0].pid;
- selected.title = listArr[0].title;
- selected.isFolder = listArr[0].isFolder;
- }
- }
- //点击目录
- async function onClick(node) {
- if (selected.title === node.title && selected.id === node.id) return
- current.value = 1
- selected.id = node.id;
- selected.pid = node.pid;
- selected.title = node.title;
- selected.isFolder = node.isFolder;
- if (node.pid != 'root') {
- if (node.isFolder) {
- let types, devicetype
- if (node.itemValue.indexOf('&') != -1) {
- types = node.itemValue.substring(node.itemValue.indexOf('&') + 1)
- devicetype = node.itemValue.substring(0, node.itemValue.indexOf('&'))
- } else {
- types = ''
- devicetype = ''
- }
- let res = await getDevice({ ids: types, devicetype: devicetype })
- if (res.msgTxt.length != 0) {
- res.msgTxt[0].datalist.forEach(el => {
- el.pid = node.id
- el.isFolder = false
- el.title = el.strinstallpos
- el.id = el.deviceID
- })
- listArr[0].children.forEach(v => {
- if (v.id == node.id) {
- v.children = res.msgTxt[0].datalist
- }
- })
- }
- searchParam.devKind = node.itemValue
- searchParam.strType = ''
- await getVideoAddrs()
- getVideo()
- } else {
- await getVideoAddrsSon(node.deviceID)
- getVideo()
- }
- } else {
- searchParam.devKind = ''
- searchParam.strType = ''
- await getVideoAddrs()
- getVideo()
- }
- };
- //点击详情跳转
- function onDetail(node) {
- let str = listArr[0].children.filter(v => v.id == node.pid)[0].itemValue
- let type = str.indexOf('&') != -1 ? str.substring(0, str.indexOf('&')) : ''
- console.log(type, 'type--------')
- switch (type) {
- case 'pulping'://注浆
- router.push('/grout-home')
- break;
- case 'window'://自动风窗
- router.push('/monitorChannel/monitor-window?id=' + node.deviceID)
- break;
- case 'gate'://自动风门
- router.push('/monitorChannel/monitor-gate?id=' + node.deviceID + '&deviceType=' + node.deviceType)
- break;
- case 'fanlocal'://局部风机
- router.push('/monitorChannel/monitor-fanlocal?id=' + node.deviceID + '&deviceType=fanlocal')
- break;
- case 'fanmain'://主风机
- router.push('/monitorChannel/monitor-fanmain?id=' + node.deviceID)
- break;
- case 'forcFan'://压风机
- router.push('/forcFan/home')
- break;
- case 'pump'://瓦斯抽采泵
- router.push('/monitorChannel/gasPump-home')
- break;
- case 'nitrogen'://制氮
- router.push('/nitrogen-home')
- break;
- }
- }
- async function getVideoAddrs() {
- clearCamera();
- playerList.value = []
- let paramKind = searchParam.devKind.substring(0, searchParam.devKind.indexOf('&'))
- let res = await list({ devKind: paramKind, strType: searchParam.strType, pageSize: pageSize.value, pageNo: current.value })
- total.value = res['total'] || 0
- if (res.records.length != 0) {
- const cameraList = <{ name: string, addr: string }[]>[]
- const cameras = res.records
- for (let i = 0; i < cameras.length; i++) {
- const item = cameras[i];
- if (item['devicekind'] === 'toHKRtsp') {
- // 从海康平台接口获取视频流
- try {
- const data = await cameraAddr({ cameraCode: item['addr'] });
- if (data) {
- cameraList.push({ name: item['name'], addr: data['url'] });
- }
- // cameraList.push({
- // name: item['name'],
- // // addr: 'http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8'
- // addr: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.mp4/.m3u8',
- // });
- } 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
- }
- }
- async function getVideoAddrsSon(Id) {
- clearCamera();
- playerList.value = []
- let res = await getVentanalyCamera({ deviceid: Id })
- if (res.records.length != 0) {
- const cameraList = <{ name: string, addr: string }[]>[]
- const cameras = res.records
- for (let i = 0; i < cameras.length; i++) {
- const item = cameras[i];
- if (item['devicekind'] === 'toHKRtsp') {
- // 从海康平台接口获取视频流
- try {
- const data = await cameraAddr({ cameraCode: item['addr'] });
- if (data && data['url']) {
- cameraList.push({ name: item['name'], addr: data['url'] });
- }
- // cameraList.push({
- // name: item['name'],
- // // addr: 'http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8'
- // addr: 'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.mp4/.m3u8',
- // });
- } 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
- }
- }
- function onChange(page) {
- current.value = page;
- getVideoAddrs().then(() => {
- getVideo()
- })
- }
- 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)
- }
- }
- }
- function setNoRtspVideo(id, videoAddr) {
- 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)
- }
- }
- 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++) {
- if(webRtcServerList[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 = []
- }
- onMounted(async () => {
- await getVideoAddrs()
- // getTreeList()
- getCameraDevKindList()
- getVideo()
- })
- onUnmounted(() => {
- clearCamera()
- })
- </script>
- <style lang="less">
- .camera-container {
- position: relative;
- width: calc(100% - 30px);
- height: calc(100% - 84px);
- display: flex;
- margin: 15px;
- justify-content: space-between;
- align-items: center;
- .left-area {
- width: 15%;
- height: 100%;
- padding: 20px;
- border: 1px solid #99e8ff66;
- background: #27546e1a;
- box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
- -moz-box-shadow: 0px 0px 20px 7px rgba(145, 233, 254, 0.7) inset;
- -webkit-box-shadow: 0px 0px 50px 1px rgb(149 235 255 / 5%) inset;
- box-sizing: border-box;
- // lxh
- .iconfont {
- color: #fff;
- font-size: 12px;
- margin-left: 5px;
- }
- }
- .right-area {
- width: 85%;
- height: 100%;
- padding: 0px 0px 0px 15px;
- box-sizing: border-box;
- .camera-box {
- width: 100%;
- height: calc(100% - 60px);
- display: flex;
- justify-content: space-around;
- align-items: flex-start;
- flex-wrap: wrap;
- overflow-y: auto;
- }
- .camera-box1 {
- width: 100%;
- height: calc(100% - 60px);
- display: flex;
- justify-content: flex-start;
- align-items: flex-start;
- flex-wrap: wrap;
- overflow-y: auto;
- }
- .player-box {
- width: 626px;
- height: 370px;
- padding: 17px 18px;
- background: url('/@/assets/images/vent/camera_bg.png');
- background-size: 100% 100%;
- position: relative;
- 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;
- }
- }
- .pagination {
- width: 100%;
- height: 60px;
- display: flex;
- justify-content: center;
- align-items: center;
- }
- }
- }
- :deep(video) {
- width: 100% !important;
- height: 100% !important;
- object-fit: cover !important;
- }
- </style>
|