| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- <template>
- <view>
- <web-view v-if="webviewUrl" @message="handleMessage" :src="webviewUrl"></web-view>
- </view>
- </template>
- <script setup lang="ts">
- import {ref, watch} from 'vue'
- import {useUserStore} from '@/store/userStore'
- import {onLoad,onReady} from "@dcloudio/uni-app";
- import {useWsStore} from "@/store/WsStore";
- import {storeToRefs} from "pinia";
- import {usePeerStore} from "@/store/peerStore";
- import SendVideoCode from "@/plugins/video/SendVideoCode";
- import UserApi from "@/api/UserApi";
- import ChatType from "@/utils/ChatType";
- import type VideoSendInfo from "@/plugins/video/mode/VideoSendInfo";
- import type VideoCallMessage from "@/plugins/video/mode/VideoCallMessage";
- import SendCode from "@/utils/SendCode";
- import type VideoClose from "@/plugins/video/mode/VideoClose";
- import MessageVideoViewPlugin from "@/plugins/video/MessageVideoViewPlugin";
- import {
- UTSVoip,
- UTSLocalNotification,
- UTSCXProvider
- } from "@/uni_modules/wrs-uts-voip"
- const peerStore = usePeerStore()
- const friendId = ref("")
- const showVideo = ref(false)
- const isConnect = ref(false)
- const startTime = ref(0)
- const friend = ref()
- const peerId = ref()
- const checkPermission = function (title: string) {
- // #ifndef H5
- if (uni.getSystemInfoSync().platform !== 'android') {
- return new Promise((resolve) => {
- resolve(true)
- })
- }else {
- return new Promise((resolve) => {
- plus.android.requestPermissions(
- ["android.permission.RECORD_AUDIO", "android.permission.CAMERA"],
- function (resultObj) {
- if (resultObj.granted.length < 2) {
- uni.showToast({
- icon: "none",
- title,
- });
- resolve(false)
- const timer1 = setTimeout(() => { //没有开对应的权限,打开app的系统权限管理页
- let Intent = plus.android.importClass("android.content.Intent");
- let Settings = plus.android.importClass("android.provider.Settings");
- let Uri = plus.android.importClass("android.net.Uri");
- let mainActivity = plus.android.runtimeMainActivity();
- let intent = new Intent();
- intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
- let uri = Uri.fromParts("package", mainActivity.getPackageName(), null);
- intent.setData(uri);
- mainActivity.startActivity(intent);
- clearTimeout(timer1)
- }, 1000)
- } else {
- resolve(true)
- }
- }
- );
- })
- }
- // #endif
- // #ifdef H5
- return new Promise((resolve) => {
- resolve(true)
- })
- // #endif
- }
- onLoad((opt) => {
- const user = useUserStore().getUser()
- friendId.value = opt?.friendId
- peerId.value = opt?.peerId
- if(typeof opt?.showVideo === 'string'){
- showVideo.value = opt?.showVideo === 'true'
- }
- peerStore.updateBusyStatus(true)
- UserApi.getUser(friendId.value).then((res) => {
- friend.value = res.data
- if (user && friend.value) {
- checkPermission("请开启相机和麦克风权限")
- .then((res) => {
- peerStore.setCallId(friend.value.id)
- peerStore.updateCloseStatus(false)
- const avatar = encodeURI(friend.value.avatar)
- if(showVideo.value){
- //转义url
- const url = `/hybrid/html/${peerId.value ? 'answer' : 'calling'}.html?id=${user.id}&name=${user.name}&friendId=${friend.value.id}&friendName=${friend.value.name}&friendAvatar=${avatar}&showVideo=${showVideo.value}&peerId=${peerId.value ?? ''}`
- webviewUrl.value = encodeURI(url)
- }
- else{
- //转义url
- const url = `/hybrid/html/${peerId.value ? 'answerAudio' : 'callingAudio'}.html?id=${user.id}&name=${user.name}&friendId=${friend.value.id}&friendName=${friend.value.name}&friendAvatar=${avatar}&showVideo=${showVideo.value}&peerId=${peerId.value ?? ''}`
- webviewUrl.value = encodeURI(url)
- }
- })
- }
- })
- })
- onReady(()=>{
- setTimeout(function() {
- // 这里写要延时执行的代码
- if(uni.getSystemInfoSync().platform == "ios"){
- let provider = new UTSCXProvider()
- provider.endCall({
- uuid: uni.getStorageSync("voip_UUID")
- }, (resp: any)=>{
- console.log(JSON.stringify(resp))
- })
- }
- }, 500);
-
- });
- const wsStore = useWsStore()
- const webviewUrl = ref()
- const {isClose} = storeToRefs(peerStore)
- watch(isClose, (newVal) => {
- if (newVal) {
- uni.navigateBack()
- }
- })
- /**
- * 发送关闭
- * @param calling
- */
- const sendCloseMsg = (calling: boolean) => {
- const userId = useUserStore().getUser()?.id
- if (userId) {
- const closeMessage: VideoSendInfo<VideoClose> = {
- code: SendVideoCode.CLOSE,
- message: {
- chatId: friend.value.id,
- fromId: userId,
- timestamp: new Date().getTime(),
- type: ChatType.FRIEND
- }
- }
- wsStore.send(JSON.stringify(closeMessage))
- console.log('发送关闭消息', closeMessage)
- //发起方才有发送视频结果消息的权限
- if (calling) {
- //界面展示视频消息情况,例如:对方是否接受了视频通话,通话时长
- const callMessage: VideoSendInfo<VideoCallMessage> = {
- code: SendCode.MESSAGE,
- message: {
- messageType: new MessageVideoViewPlugin().messageType,
- chatId: friend.value.id,
- fromId: userId,
- timestamp: new Date().getTime(),
- type: ChatType.FRIEND,
- result: isConnect.value,
- video: showVideo.value,
- duration: isConnect.value ? new Date().getTime() - startTime.value : 0
- }
- }
- //console.log(callMessage)
- wsStore.send(JSON.stringify(callMessage))
- }
- }
- }
- /**
- * 处理消息 接受来自webview的消息
- * @param data
- */
- const handleMessage = (data: any) => {
- const message = data.detail.data[0];
- console.log('124444',message);
-
- // 创建音频播放器实例
- //var player = plus.audio.createPlayer(stream);
-
- switch (message.type) {
- case 'ws'://ws消息
- useWsStore().send(JSON.stringify(message.data))
- break;
- case 'changeSpeaker'://ws消息
- break;
- case 'connect'://ws消息
- isConnect.value = true
- startTime.value = new Date().getTime()
- break;
- case 'accept'://接受视频请求
- isConnect.value = true
- startTime.value = new Date().getTime()
- //发送请求,告诉自己的peerId给自己的另一个客户端
- const userId = useUserStore().user?.id
- usePeerStore().setPeerId(message.data.peerId)
- //给自己发一份忙碌状态,多个客户端同一个人不能同时接受,需要客户端接受后,其他客户端就关闭
- useWsStore().send(
- JSON.stringify({
- code: SendVideoCode.BUSY,
- message: {
- chatId: userId,
- fromId: userId,
- peerId: message.data.peerId
- }
- })
- )
- break;
- case 'close'://关闭
- sendCloseMsg(message.data.calling)
- isConnect.value = false
- peerStore.setCallId(undefined)
- peerStore.updateBusyStatus(false)
- uni.navigateBack()
- break;
- case 'stream'://
- console.log('stream',JSON.stringify(message.data))
- break;
- }
- }
- </script>
- <style scoped lang="scss">
- .chat-container {
- display: block;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- height: 100vh;
- background-color: #000; /* 微信通常使用深色背景 */
- position: relative;
- }
- .main-video {
- width: 100%;
- height: 100%;
- object-fit: cover; /* 确保视频填充整个屏幕 */
- position: absolute;
- top: 0;
- left: 0;
- }
- .local-video {
- width: 50%;
- height: 50%;
- position: absolute;
- top: 50%;
- left: 50%;
- }
- .controls {
- position: absolute;
- bottom: 50px; /* 将控制按钮放在屏幕底部 */
- display: flex;
- justify-content: space-around;
- width: 100%;
- }
- button {
- border: none;
- border-radius: 50%;
- background-color: red;
- color: white;
- font-size: 32upx;
- cursor: pointer;
- transition: background-color 0.3s;
- z-index: 1000000000000000000000000;
- width: 20vw;
- height: 20vw;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- button:hover {
- background-color: rgba(0, 0, 0, 0.9); /* 鼠标悬停时颜色加深 */
- }
- button:active {
- background-color: rgba(0, 0, 0, 1); /* 点击时颜色更加深 */
- }
- /* 特定按钮的图标,例如使用Font Awesome或类似图标库 */
- .start-call-icon {
- /* 添加开始通话图标样式 */
- }
- .end-call-icon {
- /* 添加结束通话图标样式 */
- }
- </style>
|