Files
sgxt_web/src/views/consultation/draggerAble.vue
2025-12-11 18:12:51 +08:00

352 lines
9.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="mt20 flex align-center">
<el-input style="width:200px" class="mr10" v-model="keyWoed" placeholder="输入编号"></el-input>
<el-button @click="handleBtn('无人机视频')">无人机视频</el-button>
<el-button @click="handleBtn('音频')">音频</el-button>
</div>
<DraggableResizableVue id="call_div" v-model:x="element.x" v-model:y="element.y" v-model:h="element.height" v-model:w="element.width" v-model:active="element.isActive" class="container">
<div>
<el-row justify="space-around">
<a-tag v-for="item in speakers" :key="item.number" bordered color="green">{{ item.alias || item.number }}</a-tag>
</el-row>
<div id="voice_wave" class="wave"></div>
<audio ref="audioRef" :volume="80 / 100" autoplay controls hidden />
<video id="video" ref="videoRef" autoplay playsinline :hidden="!showVideo" class="video"/>
<video id="localvideo" ref="localvideoRef" autoplay playsinline :hidden="!showlocalVideo" class="video"/>
<video id="pushVideo" ref="pushVideoRef" autoplay playsinline :hidden="!showPushlVideo" class="video" />
<el-row class="buttonContainer" justify="space-around">
<el-button :type="selfGranted ? 'success' : 'pramary'" shape="round" @click="applayFloor">话权</el-button>
<el-button type="danger" shape="round" @click="hangup">挂断</el-button>
</el-row>
</div>
</DraggableResizableVue>
</template>
<script setup>
import useCallModule from '@/sdk/call';
import useRecorder from '@/hooks/recorder';
import DraggableResizableVue from "draggable-resizable-vue3";
import { nextTick, onMounted, onUnmounted, ref } from "vue";
import SiriWave from "siriwave";
import { init } from "echarts";
let waveContainer;
let audioWave = SiriWave || undefined;
const element = ref({
x: 0,
y: 0,
width: 200,
height: 200,
isActive: false
});
let rootView;
let streamCbId;
let hangupCbId;
let grantCbId;
// 本端视频流 local video stream
let localVideoStream;
// 远端视频流 remote video stream
let remoteVideoStream;
const selfGranted = ref(false);
// 54040254001310305009 视频
// 38467417 音频
const keyWoed = ref('38467417') //关键字
// 是否 半双工用一个Video来显示远端流和本地流。 true 为 共用一个video。 false 为 2个video分别显示。
const useOneVideoShow = true;
const useMedia = useRecorder();
const CALL = useCallModule();
const audioRef = ref();
// 远端流和Video。
const videoRef = ref();
const showVideo = ref(false);
const localvideoRef = ref(); // 本端Video
const showlocalVideo = ref(false); // 显示本端流
const pushVideoRef = ref();
// 显示推送流 show push video
const showPushlVideo = ref(false);
const showPanel = ref(false);
const speakers = ref([]);
const codeId = ref(null)
// 处理按钮
const handleBtn = (type) =>{
if(!keyWoed.value) return;
let params = { page_size:10,page_index:1,org_id:"",key_word:keyWoed.value}
lemon.basedata.fetchDeviceList(params).then(resp => {
if(resp.device_list[0].basedata_id){
let basedata_id = resp.device_list[0].basedata_id
debugger
switch(type){
case '无人机视频':
let obj = { basedata_id , video_frame_size: 3, hook_flag: 0 }
window.lemon.call.makeVideoCall(obj)?.then((resp) => {
console.log('无人机视频', resp);
codeId.value = resp.call_id
});
break;
case '音频':
let obj1 = { basedata_id,"hook_flag":0 }
window.lemon.call.makeVoiceCall(obj1)?.then((resp) => {
console.log('音频', resp);
codeId.value = resp.call_id
});
break;
}
}
}).catch(err => {})
}
// register media stream callbackinclude audio and video stream
const registerStream = () => {
streamCbId = window.lemon.call.addMediaStream((call_id, stream, type) => {
console.log(type,'============监听播放类型');
showPanel.value = true;
rootView.style.display = 'unset';
if (type === 'audio_dst') {
setAudio(stream);
} else if (type === 'video_dst') {
if (useOneVideoShow) {
remoteVideoStream = stream;
showVideo.value = true;
videoRef.value.srcObject = stream;
remoteVideoStream = stream;
} else {
showVideo.value = true;
showlocalVideo.value = false;
videoRef.value.srcObject = stream;
}
} else if (type === 'video_src') {
if (useOneVideoShow) {
// 一般全双工显示远端的画面。 拥有话权的时候,才显示自己的画面,所以流赋值到话权模块处理。
showVideo.value = false;
localVideoStream = stream;
} else {
showlocalVideo.value = true;
showVideo.value = false;
localvideoRef.value.srcObject = stream;
localVideoStream = stream;
}
}
});
};
const setAudio = (stream) => {
if (audioRef.value) {
audioRef.value.srcObject = stream;
} else {
nextTick(() => {
setAudio();
});
}
};
const registerhangupEvent = () => {
hangupCbId = window.lemon.call.addHangupEvt(() => {
speakers.value.length = 0;
selfGranted.value = false;
showVideo.value = false;
showlocalVideo.value = false;
showPanel.value = false;
try {
videoRef.value.pause();
} catch (error) {
console.error('on hangup,video stop error', error);
}
videoRef.value.srcObject = null;
close();
});
};
const registerGrantEvent = () => {
grantCbId = window.lemon.floor.addGrantEvt((event) => {
speakers.value = event.speaker;
selfGranted.value = event.speaker.findIndex((item) => item.number === sessionStorage.getItem('user_id')) >= 0;
if (!localVideoStream && !remoteVideoStream) return;
switch (event.grant_status) {
case '0':
// 话权空闲,本端和对端都不显示画面
if (useOneVideoShow) {
videoRef.value.srcObject = null;
} else {
showVideo.value = false;
showlocalVideo.value = false;
}
break;
case '3':
// 远端有话权设置video 为远端流
if (useOneVideoShow) {
videoRef.value.srcObject = remoteVideoStream;
} else {
showVideo.value = true;
showlocalVideo.value = false;
}
break;
case '4':
// 自己有话权显示本端设置video 为本地流,此时对端是没有画面的
if (useOneVideoShow) {
videoRef.value.srcObject = localVideoStream;
} else {
showVideo.value = false;
showlocalVideo.value = true;
}
break;
}
});
};
const close = () => {
if (rootView) {
rootView.style.display = 'none';
}
};
const applayFloor = () => {
const self = sessionStorage.getItem('user_id');
const index = speakers.value?.findIndex((item) => item.number === self);
if (index >= 0) {
CALL.releaseFloor();
} else {
CALL.applyFloor();
}
};
const hangup = () => {
CALL.hangup(codeId.value);
};
const Init = () => {
let token = window.localStorage.getItem("rhToken");
if (!token) {
let userInfo = {
username: "sgxtcs",
password: "123456",
realm: "puc.com",
webpucUrl: "https://89.40.9.95:16888"
};
lemon.login.login(userInfo).then((esacpe) => {
token = esacpe.token;
window.localStorage.setItem("rhToken", esacpe.token);
});
} else {
ConnectWebsocket(token);
}
};
const ConnectWebsocket = (token) => {
lemon.login.reConnectWebsocket({
username: "sgxtcs",
realm: "puc.com",
webpucUrl: "https://89.40.9.95:16888",
token: token
}).then((resp) => {
console.log(resp,"ConnectWebsocket")
window.lemon.call.addMediaStream((call_id, stream, type) => {
console.log(call_id, stream, type);
});
});
};
const initWave = () => {
if (!waveContainer) {
waveContainer = document.getElementById('voice_wave');
}
audioWave = new SiriWave({
container: waveContainer,
autostart: false,
width: element.value.width,
});
audioWave.start();
};
// 如果要用到录音功能绑定的video 不能切流。 如果半双工要显示本端流和远端流需要用两个Video。
const initVideo = () => {
if (!useOneVideoShow) {
useMedia.init(videoRef.value, audioRef.value);
}
};
onMounted(() => {
nextTick(() => {
Init();
registerStream();
registerhangupEvent();
registerGrantEvent();
videoRef.value.onloadedmetadata = initVideo;
initWave();
});
rootView = document.getElementById('call_div');
window.video = pushVideoRef.value;
window.showVideo = showPushlVideo;
window.videoViewRoot = rootView;
});
onUnmounted(() => {
window.lemon.call.removeMediaStream(streamCbId);
window.lemon.call.removeHangupEvt(hangupCbId);
window.lemon.floor.removeGrantEvt(grantCbId);
});
</script>
<style scoped>
.container {
position: absolute;
display: flex;
flex-direction: column;
justify-content: center;
margin: 2px;
background: #000;
border: 2px dashed #ccc;
border-radius: 16px;
cursor: pointer;
top: 30%;
left: 40%;
}
.video {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: scale-down;
}
.buttonContainer {
position: absolute;
bottom: 0;
width: 96%;
margin: 8px;
}
.speaks {
align-items: center;
}
.wave {
top: 0;
width: 100%;
height: 100px;
}
.close {
position: absolute;
top: 0;
right: 0;
margin: 5px;
}
</style>