@sdk185/sip-phone-sdk26 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/sip-phone-sdk.common.js +46 -45
- package/dist/sip-phone-sdk.css +1 -1
- package/dist/sip-phone-sdk.umd.js +46 -45
- package/dist/sip-phone-sdk.umd.min.js +5 -5
- package/package.json +1 -1
- package/src/App.vue +1 -1
- package/src/components/{MobilePhone copy.vue → MobilePhone-nl.vue} +418 -223
- package/src/components/{MobilePhone copy 2.vue → MobilePhone-ol.vue} +315 -243
- package/src/components/MobilePhone.vue +4 -4
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
<!-- 主模板:视频通话界面 -->
|
|
2
2
|
<template>
|
|
3
3
|
<!-- 主容器:使用flex垂直布局,内容居中对齐 -->
|
|
4
|
-
<div class="d-flex flex-column align-center justify-space-between sip-call-main">
|
|
5
|
-
<!-- 顶部提示信息 -->
|
|
6
|
-
<div style="position: absolute; top: 0; left: 0; background: transparent; color: #FFFFFF; width: 100%; z-index: 9999; height: 0; padding-top: 10px; padding-left: 10px;">
|
|
7
|
-
{{ prompt }}
|
|
8
|
-
</div>
|
|
4
|
+
<div class="phone-nmain d-flex flex-column align-center justify-space-between sip-call-main">
|
|
9
5
|
<!-- 位置信息显示区域 -->
|
|
10
6
|
<div class="location-wrapper" v-if="showLocation">
|
|
11
7
|
<!-- 显示经纬度和海拔信息 -->
|
|
@@ -14,127 +10,79 @@
|
|
|
14
10
|
<p>ALT: {{altitude}}</p>
|
|
15
11
|
</div>
|
|
16
12
|
<!-- 通话信息显示区域 -->
|
|
17
|
-
<div class="d-flex flex-column align-center" v-if="!miniMode && !showDialPad"
|
|
13
|
+
<div class="d-flex flex-column align-center call-btn" v-if="!miniMode && !showDialPad">
|
|
18
14
|
<!-- 应用logo -->
|
|
19
|
-
<img class="elevation-4" alt="logo"
|
|
15
|
+
<img class="elevation-4 call-img" alt="logo" src="https://kefu-gm-jc-dat.boc-samsunglife.cn/static/normal-logo.png"/>
|
|
20
16
|
<!-- 通话状态提示 -->
|
|
21
|
-
<p class="mt-6 mb-0
|
|
17
|
+
<p class="mt-6 mb-0 call-label" >{{callPrompt}}</p>
|
|
22
18
|
</div>
|
|
23
|
-
|
|
24
19
|
<!-- 功能按钮区域 -->
|
|
25
|
-
<div class="pr-10 pl-10
|
|
20
|
+
<div class="pr-10 pl-10 " v-if="!miniMode && !showDialPad">
|
|
26
21
|
<v-row>
|
|
27
22
|
<v-col cols="12" class="text-center">
|
|
28
23
|
<!-- 通话计时器 -->
|
|
29
24
|
<div
|
|
30
|
-
v-show="isCalling && showHangup"
|
|
31
|
-
视频时长{{duration}}
|
|
25
|
+
v-show="isCalling && showHangup" class="call-duration">
|
|
26
|
+
视频时长 {{duration}}
|
|
32
27
|
</div>
|
|
33
28
|
</v-col>
|
|
34
29
|
</v-row>
|
|
35
30
|
<!-- 第一行按钮组 -->
|
|
36
31
|
<v-row class="d-flex justify-space-around mb-10">
|
|
37
|
-
<!--
|
|
32
|
+
<!-- 取消按钮 - 用于微信公众号调用的场景 -->
|
|
38
33
|
<v-btn
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
color="#FFFFFF"
|
|
34
|
+
class="cancel-btn"
|
|
35
|
+
style=" "
|
|
42
36
|
v-show="!isCalling && !needDial"
|
|
43
|
-
@click="
|
|
37
|
+
@click="onHangupBtnClick"
|
|
44
38
|
>
|
|
45
|
-
<
|
|
46
|
-
<span class="
|
|
39
|
+
<img class="cancel-img" src="https://kefu-gm-jc-dat.boc-samsunglife.cn/static/call-down.png" alt="hangup" >
|
|
40
|
+
<span class="cancel-label">取消</span>
|
|
47
41
|
</v-btn>
|
|
48
|
-
|
|
49
|
-
<!-- 麦克风静音/取消静音按钮 -->
|
|
50
|
-
<!-- 当正在通话且显示挂断按钮时显示 -->
|
|
51
|
-
<!-- 点击时触发onMuteBtnClick方法,传入'audio'参数 -->
|
|
52
42
|
<v-btn
|
|
53
|
-
style="opacity: .6"
|
|
54
|
-
color="blue-grey"
|
|
55
|
-
fab
|
|
56
43
|
large
|
|
57
|
-
dark
|
|
58
44
|
v-show="isCalling && showHangup"
|
|
59
45
|
@click="onMuteBtnClick('audio')"
|
|
46
|
+
class="custom-mute-btn btn-box"
|
|
60
47
|
>
|
|
61
|
-
|
|
62
|
-
<img
|
|
63
|
-
<img v-else :src="require('@/assets/audio-close.png')" alt="mic-off" width="24" height="24">
|
|
64
|
-
<!-- 显示当前状态的文字提示 -->
|
|
65
|
-
<!-- <span class="btn-label">{{ isAudioMute ? '取消静音' : '静音' }}</span> -->
|
|
48
|
+
<img class="img-open-close" v-if="!isAudioMute" src="https://kefu-gm-jc-dat.boc-samsunglife.cn/static/adudio-open.png" >
|
|
49
|
+
<img class="img-open-close" v-else src="https://kefu-gm-jc-dat.boc-samsunglife.cn/static/adudio-close.png" >
|
|
66
50
|
</v-btn>
|
|
67
|
-
|
|
68
51
|
<!-- 摄像头开关按钮 -->
|
|
69
52
|
<v-btn
|
|
70
|
-
style="opacity: .6"
|
|
71
|
-
color="blue-grey"
|
|
72
|
-
fab
|
|
73
53
|
large
|
|
74
|
-
dark
|
|
75
54
|
v-show="isCalling && showHangup"
|
|
76
55
|
@click="onMuteBtnClick('video')"
|
|
56
|
+
class="custom-mute-btn"
|
|
77
57
|
>
|
|
78
|
-
<img v-if="!isVideoMute"
|
|
79
|
-
<img v-else
|
|
58
|
+
<img class="img-open-close" v-if="!isVideoMute" src="https://kefu-gm-jc-dat.boc-samsunglife.cn/static/camera-open.png" alt="mic-on">
|
|
59
|
+
<img class="img-open-close" v-else src="https://kefu-gm-jc-dat.boc-samsunglife.cn/static/camera-close.png" alt="mic-off">
|
|
80
60
|
</v-btn>
|
|
81
61
|
|
|
82
|
-
<!-- 显示/隐藏本地视频按钮 -->
|
|
83
|
-
<!-- <v-btn
|
|
84
|
-
style="opacity: .6"
|
|
85
|
-
color="blue-grey"
|
|
86
|
-
fab
|
|
87
|
-
large
|
|
88
|
-
dark
|
|
89
|
-
v-show="isCalling && showHangup"
|
|
90
|
-
@click="onShowLocalVideoBtnClick"
|
|
91
|
-
>
|
|
92
|
-
<v-icon v-if="localVideo.show">mdi-monitor</v-icon>
|
|
93
|
-
<v-icon v-else>mdi-monitor-multiple</v-icon>
|
|
94
|
-
<span class="btn-label">{{ localVideo.show ? '隐藏本地' : '显示本地' }}</span>
|
|
95
|
-
</v-btn> -->
|
|
96
62
|
</v-row>
|
|
97
63
|
|
|
98
64
|
<!-- 第二行按钮组 -->
|
|
99
|
-
<v-row class="d-flex justify-
|
|
100
|
-
<!-- 拨号盘按钮 -->
|
|
101
|
-
<v-btn
|
|
102
|
-
style="opacity: .6"
|
|
103
|
-
large
|
|
104
|
-
fab
|
|
105
|
-
v-show="needDial && !showDialPad"
|
|
106
|
-
@click="onDialPadBtnClick"
|
|
107
|
-
>
|
|
108
|
-
<v-icon>mdi-dialpad</v-icon>
|
|
109
|
-
<span class="btn-label">拨号盘</span>
|
|
110
|
-
</v-btn>
|
|
111
|
-
|
|
65
|
+
<v-row class="d-flex justify-center mt-10" style="position: relative; width: 100%;">
|
|
112
66
|
<!-- 挂断按钮 -->
|
|
113
67
|
<v-btn
|
|
114
|
-
style="opacity: 1; background: transparent; box-shadow: none;"
|
|
115
|
-
fab
|
|
116
|
-
large
|
|
68
|
+
style="opacity: 1; background: transparent !important; box-shadow: none !important; position: relative; min-width: 0 !important; width: auto !important; height: auto !important; padding: 0 !important; margin: 0 !important;"
|
|
117
69
|
v-show="isCalling && showHangup"
|
|
118
70
|
@click="onHangupBtnClick"
|
|
71
|
+
class="transparent-btn"
|
|
119
72
|
>
|
|
120
|
-
<img
|
|
73
|
+
<img class="hangup-img" src="https://kefu-gm-jc-dat.boc-samsunglife.cn/static/call-down.png" alt="hangup">
|
|
74
|
+
<span class="hangup-label">挂断</span>
|
|
121
75
|
</v-btn>
|
|
122
|
-
|
|
123
76
|
<!-- 切换摄像头按钮 -->
|
|
124
77
|
<v-btn
|
|
125
|
-
style="opacity: .6"
|
|
126
|
-
color="blue-grey"
|
|
127
|
-
fab
|
|
128
|
-
large
|
|
129
|
-
dark
|
|
130
78
|
v-show="isCalling && showHangup"
|
|
131
79
|
@click="onSwitchCameraBtnClick"
|
|
80
|
+
class="transparent-btn"
|
|
132
81
|
>
|
|
133
|
-
<v-icon>mdi-camera-flip</v-icon>
|
|
82
|
+
<v-icon size="30">mdi-camera-flip</v-icon>
|
|
134
83
|
</v-btn>
|
|
135
84
|
</v-row>
|
|
136
85
|
</div>
|
|
137
|
-
|
|
138
86
|
<!-- 视频容器区域 -->
|
|
139
87
|
<v-expand-transition>
|
|
140
88
|
<div class="sip-call-wrapper" v-show="showVideoCt" @click="onSipCallClick">
|
|
@@ -168,7 +116,7 @@
|
|
|
168
116
|
ref="remoteVideo"
|
|
169
117
|
class="sip-call-video"
|
|
170
118
|
style="object-fit: contain;"
|
|
171
|
-
|
|
119
|
+
|
|
172
120
|
></video>
|
|
173
121
|
</vue-draggable-resizable>
|
|
174
122
|
<!-- 本地视频窗口 -->
|
|
@@ -198,28 +146,11 @@
|
|
|
198
146
|
x5-video-orientation="h5"
|
|
199
147
|
ref="localVideo"
|
|
200
148
|
class="sip-call-video"
|
|
201
|
-
|
|
149
|
+
|
|
202
150
|
></video>
|
|
203
151
|
</vue-draggable-resizable>
|
|
204
|
-
|
|
205
|
-
<!-- 视频控制区域 -->
|
|
206
|
-
<!-- <template v-if="!miniMode">
|
|
207
|
-
通话计时器
|
|
208
|
-
<span class="video-timer">
|
|
209
|
-
<img :src="require('@/assets/icon_Recording_Fill_Red_Active.svg')" alt="icon" width="24" height="24" class="mr-2">
|
|
210
|
-
{{duration}}
|
|
211
|
-
</span>
|
|
212
|
-
|
|
213
|
-
全屏切换按钮
|
|
214
|
-
<v-btn class="camera-switch-btn" dark text x-small width="24" height="24" min-width="24" @click="onChangeVideoFullScreenBtnClick">
|
|
215
|
-
<v-icon>mdi-box-shadow</v-icon>
|
|
216
|
-
<span class="btn-label">全屏</span>
|
|
217
|
-
</v-btn>
|
|
218
|
-
</template> -->
|
|
219
|
-
|
|
220
152
|
</div>
|
|
221
153
|
</v-expand-transition>
|
|
222
|
-
|
|
223
154
|
<!-- 拨号盘组件 -->
|
|
224
155
|
<v-expand-transition>
|
|
225
156
|
<dial-panel-mini
|
|
@@ -232,12 +163,10 @@
|
|
|
232
163
|
ref="dialPanelMini"
|
|
233
164
|
/>
|
|
234
165
|
</v-expand-transition>
|
|
235
|
-
|
|
236
166
|
<!-- 音频播放器(用于播放提示音) -->
|
|
237
167
|
<v-bottom-sheet v-model="showAudioPlayer" hide-overlay>
|
|
238
168
|
<audio :src="require('@/assets/outgoing-call2.mp3')" loop ref="audioPlayer"></audio>
|
|
239
169
|
</v-bottom-sheet>
|
|
240
|
-
|
|
241
170
|
<!-- 提示信息弹窗 -->
|
|
242
171
|
<v-snackbar v-model="showSnackbar" :timeout="2000">
|
|
243
172
|
{{snackbarText}}
|
|
@@ -251,7 +180,7 @@ import SipMix from '@/libs/SIP_MIX_WEB'
|
|
|
251
180
|
import DialPanelMini from '@/components/DialPanelMini'
|
|
252
181
|
import VueDraggableResizable from 'vue-draggable-resizable'
|
|
253
182
|
import { closeStream } from '@/libs/tool'
|
|
254
|
-
|
|
183
|
+
import jsNative from 'js-native-n22'
|
|
255
184
|
|
|
256
185
|
export default {
|
|
257
186
|
name: 'MobilePhone',
|
|
@@ -325,7 +254,7 @@ export default {
|
|
|
325
254
|
miniMode: false,
|
|
326
255
|
dialNum: '',
|
|
327
256
|
|
|
328
|
-
controls: '',
|
|
257
|
+
//controls: '',
|
|
329
258
|
|
|
330
259
|
isAudioMute: false,
|
|
331
260
|
isVideoMute: false,
|
|
@@ -370,13 +299,13 @@ export default {
|
|
|
370
299
|
},
|
|
371
300
|
|
|
372
301
|
watch: {
|
|
373
|
-
isCalling (is) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
},
|
|
302
|
+
// isCalling (is) {
|
|
303
|
+
// if (is) {
|
|
304
|
+
// this.startDurationCount()
|
|
305
|
+
// } else {
|
|
306
|
+
// this.stopDurationCount()
|
|
307
|
+
// }
|
|
308
|
+
// },
|
|
380
309
|
callId (id) {
|
|
381
310
|
this.lastCallId = id || this.lastCallId
|
|
382
311
|
},
|
|
@@ -395,7 +324,80 @@ export default {
|
|
|
395
324
|
}
|
|
396
325
|
},
|
|
397
326
|
|
|
327
|
+
created() {
|
|
328
|
+
setTimeout(() => {
|
|
329
|
+
// this.onMakeCallBtnClick();
|
|
330
|
+
// 处理微信公众号传递的参数
|
|
331
|
+
// this.handleWechatParams();
|
|
332
|
+
}, 2000);
|
|
333
|
+
},
|
|
334
|
+
|
|
398
335
|
methods: {
|
|
336
|
+
handleAndroidParams(val){
|
|
337
|
+
console.log("handleAndroidParamsvalvalval", val)
|
|
338
|
+
jsNative.bridge.callhandler('webRTC', '', (result) => {
|
|
339
|
+
console.log("%c调用原生返回成功", "color: green", result)
|
|
340
|
+
console.log("c调用原生返回成功valval", val)
|
|
341
|
+
if (!result.error) {
|
|
342
|
+
// success && success(result.content)
|
|
343
|
+
console.log('webRTC调用成功', result.content)
|
|
344
|
+
console.log('webRTC调用成功valval', val)
|
|
345
|
+
this.handleWechatParams(val);
|
|
346
|
+
} else {
|
|
347
|
+
// fail && fail(result.content)
|
|
348
|
+
console.log('webRTC调用失败', result.content)
|
|
349
|
+
console.log('webRTC调用失败valval', val)
|
|
350
|
+
}
|
|
351
|
+
})
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
// 处理微信公众号参数
|
|
356
|
+
handleWechatParams(val) {
|
|
357
|
+
// ===== 测试代码开始 =====
|
|
358
|
+
// 测试用的URL参数字符串 - 模拟微信公众号传递的参数
|
|
359
|
+
// const testParams = 'debug=true&calledNum=0000021000021001&av=video&callData={"calledNum":"0000021000021001","videoRecordId":"ae756872ff3a122817848719605aa5cb"}';
|
|
360
|
+
// // 解析测试参数
|
|
361
|
+
// const urlParams = new URLSearchParams(testParams);
|
|
362
|
+
// // 获取参数值
|
|
363
|
+
// const calledNum = urlParams.get('calledNum');
|
|
364
|
+
// const callDataStr = urlParams.get('callData');
|
|
365
|
+
console.log('e行销传过来的参数',val);
|
|
366
|
+
|
|
367
|
+
// ===== 原始代码(已注释) ===== // 用于微信公众号 此方式是获取url参数;父子组件传值可以使用props
|
|
368
|
+
// const urlParams = new URLSearchParams(window.location.search);
|
|
369
|
+
// const calledNum = urlParams.get('calledNum');
|
|
370
|
+
// const callDataStr = urlParams.get('callData');
|
|
371
|
+
// const urlParams = val;
|
|
372
|
+
const calledNum = val.calledNum;
|
|
373
|
+
const callDataStr = val.callData;
|
|
374
|
+
|
|
375
|
+
// console.log('测试模式:使用模拟参数');
|
|
376
|
+
|
|
377
|
+
if (calledNum) {
|
|
378
|
+
// 存储被叫号码
|
|
379
|
+
this.setDialNum(calledNum);
|
|
380
|
+
|
|
381
|
+
// 如果有callData,解析并存储
|
|
382
|
+
let callData = null;
|
|
383
|
+
try {
|
|
384
|
+
callData = callDataStr ? JSON.parse(decodeURIComponent(callDataStr)) : null;
|
|
385
|
+
// 这里可以将callData存储到Vuex或组件的data中
|
|
386
|
+
if (callData) {
|
|
387
|
+
// 例如:this.$store.commit('setCallData', callData);
|
|
388
|
+
console.log('Call data received:', callData);
|
|
389
|
+
}
|
|
390
|
+
} catch (e) {
|
|
391
|
+
console.error('Failed to parse callData:', e);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// 自动发起呼叫
|
|
395
|
+
this.$nextTick(() => {
|
|
396
|
+
this.onMakeCallBtnClick();
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
|
|
399
401
|
_resetCmpWHLRTB ({rl =0, rt = 0, ll = 0, lt = 0} = {}) {
|
|
400
402
|
this.$refs.remoteVideoWrapper.left = rl
|
|
401
403
|
this.$refs.remoteVideoWrapper.top = rt
|
|
@@ -541,12 +543,17 @@ export default {
|
|
|
541
543
|
}
|
|
542
544
|
|
|
543
545
|
const params = { target: this.dialNum }
|
|
546
|
+
console.log('paramsparams Data', params)
|
|
547
|
+
console.log('this.callData Data', this.callData)
|
|
548
|
+
console.log('window.config.videoParams', window.config.videoParams)
|
|
544
549
|
if (this.callType === 'videoOnly') {
|
|
550
|
+
console.log('videoOnly')
|
|
545
551
|
params.video = Object.assign({
|
|
546
552
|
facingMode: 'user'
|
|
547
553
|
}, window.config.videoParams)
|
|
548
554
|
params.audio = false
|
|
549
555
|
} else if (this.callType === 'video') {
|
|
556
|
+
console.log('video')
|
|
550
557
|
params.video = Object.assign({
|
|
551
558
|
facingMode: 'user'
|
|
552
559
|
}, window.config.videoParams)
|
|
@@ -558,6 +565,7 @@ export default {
|
|
|
558
565
|
|
|
559
566
|
if (this.callData) {
|
|
560
567
|
params.callData = this.callData
|
|
568
|
+
console.log('Call Da1ta', this.callData)
|
|
561
569
|
}
|
|
562
570
|
|
|
563
571
|
// console.info('Call Params', params)
|
|
@@ -579,9 +587,16 @@ export default {
|
|
|
579
587
|
}
|
|
580
588
|
|
|
581
589
|
},
|
|
582
|
-
|
|
583
590
|
onHangupBtnClick () {
|
|
584
|
-
this.sipMix.hangup()
|
|
591
|
+
this.sipMix.hangup();
|
|
592
|
+
// this.$emit('hangup-event', { imType: 0 });// 通知父组件
|
|
593
|
+
// 触发 hangup 事件,并传递参数给父组件
|
|
594
|
+
this.$emit('hangup', {
|
|
595
|
+
// callId: this.callId,
|
|
596
|
+
// duration: this.duration,
|
|
597
|
+
// timestamp: new Date().getTime(),
|
|
598
|
+
imType: 0,
|
|
599
|
+
});
|
|
585
600
|
},
|
|
586
601
|
|
|
587
602
|
onMuteBtnClick (type) {
|
|
@@ -648,26 +663,67 @@ export default {
|
|
|
648
663
|
}, 1000)
|
|
649
664
|
},
|
|
650
665
|
|
|
666
|
+
// async onSwitchCameraBtnClick() {
|
|
667
|
+
// const list = await navigator.mediaDevices.enumerateDevices()
|
|
668
|
+
// console.warn(list)
|
|
669
|
+
// let videoOption = {
|
|
670
|
+
// facingMode: 'user',
|
|
671
|
+
// }
|
|
672
|
+
// if (this.cameraType === 'front') {
|
|
673
|
+
// videoOption = {
|
|
674
|
+
// facingMode: { exact: 'environment' }
|
|
675
|
+
// }
|
|
676
|
+
// }
|
|
677
|
+
// Object.assign(videoOption, window.config.videoParams)
|
|
678
|
+
|
|
679
|
+
// const stream = await navigator.mediaDevices.getUserMedia({
|
|
680
|
+
// video: videoOption,
|
|
681
|
+
// audio: false
|
|
682
|
+
// })
|
|
683
|
+
// if (stream) {
|
|
684
|
+
// this.cameraType = this.cameraType === 'front' ? 'back' : 'front'
|
|
685
|
+
// this.$refs.localVideo.srcObject = stream
|
|
686
|
+
// this.sipMix.changeLocalVideoTrack({stream})
|
|
687
|
+
// }
|
|
688
|
+
// },
|
|
651
689
|
async onSwitchCameraBtnClick() {
|
|
690
|
+
// 1. 获取当前设备列表(包含音频/视频输入设备)
|
|
652
691
|
const list = await navigator.mediaDevices.enumerateDevices()
|
|
653
|
-
console.warn(list)
|
|
654
|
-
|
|
655
|
-
|
|
692
|
+
console.warn(list) // 调试用:打印设备列表,可看到摄像头设备信息
|
|
693
|
+
|
|
694
|
+
// 2. 初始化摄像头配置(默认前置摄像头)
|
|
695
|
+
let videoOption = {
|
|
696
|
+
facingMode: 'user', // user = 前置摄像头(面向用户)
|
|
656
697
|
}
|
|
698
|
+
|
|
699
|
+
// 3. 切换摄像头配置:如果当前是前置,切换为后置
|
|
657
700
|
if (this.cameraType === 'front') {
|
|
658
701
|
videoOption = {
|
|
659
|
-
facingMode: { exact: 'environment' }
|
|
702
|
+
facingMode: { exact: 'environment' } // environment = 后置摄像头(面向环境)
|
|
660
703
|
}
|
|
661
704
|
}
|
|
705
|
+
|
|
706
|
+
// 4. 合并全局视频参数(比如分辨率、帧率等配置)
|
|
662
707
|
Object.assign(videoOption, window.config.videoParams)
|
|
663
708
|
|
|
709
|
+
// 5. 重新请求摄像头媒体流(只请求视频,不请求音频)
|
|
664
710
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
665
711
|
video: videoOption,
|
|
666
712
|
audio: false
|
|
667
713
|
})
|
|
714
|
+
|
|
715
|
+
// 6. 流获取成功后执行核心操作
|
|
668
716
|
if (stream) {
|
|
717
|
+
// ① 切换摄像头状态标记(front ↔ back)
|
|
669
718
|
this.cameraType = this.cameraType === 'front' ? 'back' : 'front'
|
|
719
|
+
|
|
720
|
+
// ② 更新本地video标签的流(这是视频画面显示的核心)
|
|
670
721
|
this.$refs.localVideo.srcObject = stream
|
|
722
|
+
|
|
723
|
+
// 本次新增修改点:强制显示本地视频
|
|
724
|
+
this.localVideo.show = true
|
|
725
|
+
|
|
726
|
+
// ③ 同步新的视频流到通话链路(让对方也看到切换后的画面)
|
|
671
727
|
this.sipMix.changeLocalVideoTrack({stream})
|
|
672
728
|
}
|
|
673
729
|
},
|
|
@@ -737,19 +793,40 @@ export default {
|
|
|
737
793
|
console.info('New session, session id: ', sessionId)
|
|
738
794
|
})
|
|
739
795
|
|
|
796
|
+
// 处理通话失败事件
|
|
740
797
|
this.sipMix.on('failed', (e) => {
|
|
741
|
-
console.warn('
|
|
742
|
-
this.
|
|
743
|
-
this.
|
|
798
|
+
console.warn('通话失败', e)
|
|
799
|
+
this.stopDurationCount() // 确保停止计时器 0105
|
|
800
|
+
this.dealCallEndOrFail() // 清理通话相关状态
|
|
801
|
+
this.callPrompt = this.$t('cancelled') // 显示通话已取消提示
|
|
802
|
+
|
|
803
|
+
// 触发挂断事件,通知父组件
|
|
804
|
+
this.$emit('hangup', {
|
|
805
|
+
imType: 0, // 即时通讯类型
|
|
806
|
+
reason: 'call_failed', // 失败原因
|
|
807
|
+
error: e // 错误信息
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
// 2秒后恢复默认提示
|
|
744
811
|
setTimeout(() => {
|
|
745
812
|
this.callPrompt = this.$t('pleaseBePatient')
|
|
746
813
|
}, 2000)
|
|
747
814
|
})
|
|
748
815
|
|
|
816
|
+
// 处理通话结束事件(对方挂断或通话正常结束)
|
|
749
817
|
this.sipMix.on('ended', () => {
|
|
750
|
-
console.warn('
|
|
751
|
-
this.
|
|
752
|
-
this.
|
|
818
|
+
console.warn('通话结束')
|
|
819
|
+
this.stopDurationCount() // 确保停止计时器 0105
|
|
820
|
+
this.dealCallEndOrFail() // 清理通话相关状态
|
|
821
|
+
this.callPrompt = this.$t('hangedUp') // 显示通话已挂断提示
|
|
822
|
+
|
|
823
|
+
// 触发挂断事件,通知父组件
|
|
824
|
+
this.$emit('hangup', {
|
|
825
|
+
imType: 0, // 即时通讯类型
|
|
826
|
+
reason: 'call_ended' // 通话结束原因
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
// 2秒后恢复默认提示
|
|
753
830
|
setTimeout(() => {
|
|
754
831
|
this.callPrompt = this.$t('pleaseBePatient')
|
|
755
832
|
}, 2000)
|
|
@@ -757,14 +834,18 @@ export default {
|
|
|
757
834
|
|
|
758
835
|
this.sipMix.on('localStream', async (stream, info) => {
|
|
759
836
|
console.info('localStream', stream, info)
|
|
837
|
+
this.$refs.localVideo.srcObject = stream
|
|
838
|
+
if(this.localVideo.show) {
|
|
839
|
+
return
|
|
840
|
+
}
|
|
760
841
|
this.localVideo.w = this.clientWidth
|
|
761
842
|
this.localVideo.h = this.clientHeight
|
|
762
843
|
this.localVideo.draggable = false
|
|
763
844
|
this.showVideoCt = true
|
|
764
845
|
this.localVideo.show = true
|
|
765
846
|
|
|
766
|
-
await this.$nextTick()
|
|
767
|
-
this.$refs.localVideo.srcObject = stream
|
|
847
|
+
// await this.$nextTick()
|
|
848
|
+
// this.$refs.localVideo.srcObject = stream
|
|
768
849
|
})
|
|
769
850
|
|
|
770
851
|
this.sipMix.on('remoteStream', (stream, info) => {
|
|
@@ -827,6 +908,7 @@ export default {
|
|
|
827
908
|
})
|
|
828
909
|
|
|
829
910
|
this.sipMix.on('confirmed', () => {
|
|
911
|
+
this.startDurationCount(); // 通话完全建立后开始计时 0105
|
|
830
912
|
this.startSendPosition()
|
|
831
913
|
})
|
|
832
914
|
|
|
@@ -912,6 +994,11 @@ export default {
|
|
|
912
994
|
})
|
|
913
995
|
|
|
914
996
|
setTimeout(() => {
|
|
997
|
+
// this.miniMode = false;// 调试用
|
|
998
|
+
// this.showDialPad = false;// 调试用
|
|
999
|
+
// this.isCalling = true;// 调试用
|
|
1000
|
+
// this.needDial = false;// 调试用
|
|
1001
|
+
// this.showHangup = true;// 调试用
|
|
915
1002
|
const {clientHeight, clientWidth} = window.document.body
|
|
916
1003
|
this.clientHeight = clientHeight
|
|
917
1004
|
this.clientWidth = clientWidth
|
|
@@ -929,118 +1016,226 @@ export default {
|
|
|
929
1016
|
}
|
|
930
1017
|
}
|
|
931
1018
|
</script>
|
|
932
|
-
|
|
1019
|
+
<!-- <style>
|
|
1020
|
+
@import './vuetify.css';
|
|
1021
|
+
</style> -->
|
|
933
1022
|
<style lang="scss">
|
|
934
|
-
.
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1023
|
+
.phone-nmain{
|
|
1024
|
+
background-color: #737778;
|
|
1025
|
+
padding: 25% 10px 20% !important;
|
|
1026
|
+
width: 100vw;
|
|
1027
|
+
height: 100vh;
|
|
1028
|
+
overflow: hidden;
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
.sip-call-wrapper {
|
|
1032
|
+
position: absolute;
|
|
1033
|
+
left: 0;
|
|
1034
|
+
top: 0;
|
|
1035
|
+
width: 100vw;
|
|
1036
|
+
height: 100vh;
|
|
1037
|
+
z-index: 100;
|
|
1038
|
+
}
|
|
941
1039
|
|
|
942
|
-
.sip-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
}
|
|
1040
|
+
.sip-btn-group {
|
|
1041
|
+
width: 100%;
|
|
1042
|
+
position: absolute;
|
|
1043
|
+
//bottom: 250px;
|
|
1044
|
+
bottom: 25%;
|
|
1045
|
+
z-index: 4000;
|
|
1046
|
+
}
|
|
950
1047
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1048
|
+
@media screen and (min-width: 1024px) {
|
|
1049
|
+
.sip-btn-group {
|
|
1050
|
+
position: absolute;
|
|
1051
|
+
bottom: 40px;
|
|
1052
|
+
width: 320px;
|
|
1053
|
+
z-index: 4000;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
955
1056
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
}
|
|
963
|
-
}
|
|
1057
|
+
.sip-call-video {
|
|
1058
|
+
width: 100%;
|
|
1059
|
+
height: 100%;
|
|
1060
|
+
object-fit: cover;
|
|
1061
|
+
background-color: #000000;
|
|
1062
|
+
}
|
|
964
1063
|
|
|
965
|
-
.
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1064
|
+
.video-panel {
|
|
1065
|
+
position: absolute;
|
|
1066
|
+
display: flex;
|
|
1067
|
+
flex-direction: column;
|
|
1068
|
+
justify-content: space-around;
|
|
1069
|
+
align-items: center;
|
|
1070
|
+
}
|
|
971
1071
|
|
|
972
|
-
.video-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1072
|
+
.video-timer {
|
|
1073
|
+
background-color: rgba(255,255,255,0.80);
|
|
1074
|
+
position: absolute;
|
|
1075
|
+
top: 40px;
|
|
1076
|
+
left: 16px;
|
|
1077
|
+
font-size: 14px;
|
|
1078
|
+
font-weight: bold;
|
|
1079
|
+
padding: 2px 8px 2px 6px;
|
|
1080
|
+
border-radius: 12px;
|
|
1081
|
+
z-index: 2000;
|
|
1082
|
+
display: flex;
|
|
1083
|
+
justify-content: space-around;
|
|
1084
|
+
align-items: center;
|
|
1085
|
+
}
|
|
980
1086
|
|
|
981
|
-
.video-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1087
|
+
.video-switch-btn {
|
|
1088
|
+
position: absolute !important;
|
|
1089
|
+
top: 40px;
|
|
1090
|
+
right: 80px;
|
|
1091
|
+
z-index: 2000;
|
|
1092
|
+
img {
|
|
1093
|
+
background-color: #000000;
|
|
1094
|
+
border-radius: 6px;
|
|
1095
|
+
border: 1px solid #000000;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
.video-mini-mode {
|
|
1100
|
+
position: absolute !important;
|
|
1101
|
+
top: 40px;
|
|
1102
|
+
right: 48px;
|
|
1103
|
+
z-index: 2000;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
.camera-switch-btn {
|
|
1107
|
+
position: absolute !important;
|
|
1108
|
+
top: 40px;
|
|
1109
|
+
right: 16px;
|
|
1110
|
+
z-index: 2000;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
.video-normal-mode {
|
|
1114
|
+
position: absolute !important;
|
|
1115
|
+
top: 16px;
|
|
1116
|
+
right: 16px;
|
|
1117
|
+
z-index: 2000;
|
|
1118
|
+
}
|
|
995
1119
|
|
|
996
|
-
.
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1120
|
+
.location-wrapper {
|
|
1121
|
+
position: absolute;
|
|
1122
|
+
top: 0;
|
|
1123
|
+
right: 0;
|
|
1124
|
+
width: 150px;
|
|
1125
|
+
background-color: #00000055;
|
|
1126
|
+
color: #FFFFFF;
|
|
1127
|
+
padding: 6px;
|
|
1128
|
+
p {
|
|
1129
|
+
font-size: 12px;
|
|
1130
|
+
margin: 0;
|
|
1131
|
+
padding: 0;
|
|
1132
|
+
width: 100%;
|
|
1133
|
+
white-space: nowrap;
|
|
1134
|
+
text-overflow: ellipsis;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1005
1137
|
}
|
|
1006
|
-
|
|
1138
|
+
</style>
|
|
1139
|
+
<style scoped lang="scss">
|
|
1140
|
+
.phone-nmain{
|
|
1141
|
+
.custom-mute-btn {
|
|
1142
|
+
background: transparent !important;
|
|
1143
|
+
box-shadow: none !important;
|
|
1144
|
+
z-index: 9999 !important;
|
|
1145
|
+
min-width: 0 !important;
|
|
1146
|
+
width: auto !important;
|
|
1147
|
+
height: auto !important;
|
|
1148
|
+
padding: 0 !important;
|
|
1149
|
+
margin: 0 !important;
|
|
1150
|
+
}
|
|
1007
1151
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
}
|
|
1152
|
+
/* 移除按钮悬停和激活状态的背景色 */
|
|
1153
|
+
.custom-mute-btn::before {
|
|
1154
|
+
background-color: transparent !important;
|
|
1155
|
+
opacity: 0 !important;
|
|
1156
|
+
}
|
|
1014
1157
|
|
|
1015
|
-
.
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1158
|
+
.btn-box{
|
|
1159
|
+
padding: 0 90px !important; // 0113
|
|
1160
|
+
margin-left: -100px !important; // 0113
|
|
1161
|
+
}
|
|
1162
|
+
/* 移除按钮的点击效果 */
|
|
1163
|
+
.custom-mute-btn:hover::before,
|
|
1164
|
+
.custom-mute-btn:focus::before,
|
|
1165
|
+
.custom-mute-btn:active::before {
|
|
1166
|
+
opacity: 0 !important;
|
|
1167
|
+
}
|
|
1021
1168
|
|
|
1022
|
-
.
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1169
|
+
.transparent-btn {
|
|
1170
|
+
background: transparent !important;
|
|
1171
|
+
box-shadow: none !important;
|
|
1172
|
+
min-width: 0 !important;
|
|
1173
|
+
width: auto !important;
|
|
1174
|
+
height: auto !important;
|
|
1175
|
+
padding: 0 !important;
|
|
1176
|
+
margin: 0 !important;
|
|
1177
|
+
/* opacity: .6 !important; */
|
|
1178
|
+
position: absolute !important;
|
|
1179
|
+
left: calc(50% + 80px) !important;
|
|
1180
|
+
color: #FFFFFF !important;
|
|
1181
|
+
}
|
|
1182
|
+
.transparent-btn::before {
|
|
1183
|
+
background-color: transparent !important;
|
|
1184
|
+
opacity: 0 !important;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
.call-btn{
|
|
1188
|
+
/* margin-top:160px; */
|
|
1189
|
+
position: relative;
|
|
1190
|
+
top: 0%;
|
|
1191
|
+
}
|
|
1192
|
+
.call-img{
|
|
1193
|
+
width:100px;
|
|
1194
|
+
height:100px;
|
|
1195
|
+
border-radius: 50%;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
.call-label{
|
|
1200
|
+
font-size: 16px;
|
|
1201
|
+
color: #FFFFFF
|
|
1202
|
+
}
|
|
1028
1203
|
|
|
1029
|
-
.
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1204
|
+
.cancel-btn{
|
|
1205
|
+
opacity: 1;
|
|
1206
|
+
background: transparent !important;
|
|
1207
|
+
box-shadow: none !important;
|
|
1208
|
+
position: relative !important;
|
|
1209
|
+
margin-top: -50px !important;
|
|
1210
|
+
}
|
|
1211
|
+
.cancel-img {
|
|
1212
|
+
width: 80px;
|
|
1213
|
+
height: 80px;
|
|
1214
|
+
}
|
|
1215
|
+
.cancel-label{
|
|
1216
|
+
top: 78px;
|
|
1217
|
+
position: absolute; color: #FFFFFF;
|
|
1218
|
+
font-size: 16px;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
|
|
1222
|
+
.call-duration{
|
|
1223
|
+
font-size: 14px;margin-top: -12.5px; color: #FFFFFF;
|
|
1224
|
+
}
|
|
1225
|
+
.hangup-img{
|
|
1226
|
+
width: 80px;
|
|
1227
|
+
height: 80px;
|
|
1228
|
+
}
|
|
1229
|
+
.hangup-label{
|
|
1230
|
+
position: relative;
|
|
1231
|
+
color: #FFFFFF;
|
|
1232
|
+
font-size: 14px;
|
|
1233
|
+
margin-left: 8px;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
.img-open-close{
|
|
1237
|
+
width: 55px;
|
|
1238
|
+
height: 80px;
|
|
1239
|
+
}
|
|
1044
1240
|
}
|
|
1045
|
-
|
|
1046
|
-
</style>
|
|
1241
|
+
</style>
|