videoVoice.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. <template>
  2. <view class="videoContont" :style="{height:screenHeight+'px'}">
  3. <!-- 发起人 -->
  4. <block v-if="messageType==20">
  5. <!-- 我的视频(拨打视频未接通) -->
  6. <view v-show="isOpenCamera" class="local-video" :class="isJie?'mySmartVideo':''"
  7. :style="{height:screenHeight+'px'}" id="local-video">
  8. </view>
  9. <!-- 对方的视频(拨打视频接通后) -->
  10. <view class="local-video" :style="{height:screenHeight+'px'}" v-show="isJie == true" id="myLocalVideo">
  11. </view>
  12. </block>
  13. <!-- 顶部信息 -->
  14. <view class="topInfo" v-if="isRol == 1 && userType == 2">
  15. <image class="topInfo-avatar" :src="resumesIdInfo.avatar" mode="aspectFill"></image>
  16. <view class="topInfo-name">
  17. {{resumesIdInfo.resumesName}}
  18. </view>
  19. <view class="topInfo-time">
  20. <!-- 邀请你语音通话... -->
  21. <smhTimer v-if="isJie" ref="timer" @timing="timing" :auto="isJie" />
  22. </view>
  23. </view>
  24. <view class="topInfo" v-if="isRol == 1 && userType == 1">
  25. <image class="topInfo-avatar" :src="company.companyLogo" mode="aspectFill"></image>
  26. <view class="topInfo-name">
  27. {{company.companyName}}
  28. </view>
  29. <view class="topInfo-time">
  30. <!-- 邀请你语音通话... -->
  31. <smhTimer v-if="isJie" ref="timer" @timing="timing" :auto="isJie" />
  32. </view>
  33. </view>
  34. <view class="topInfo" v-if="isRol == 2 && userType == 1">
  35. <image class="topInfo-avatar" :src="company.companyLogo" mode="aspectFill"></image>
  36. <view class="topInfo-name">
  37. {{company.companyName}}
  38. </view>
  39. <view class="topInfo-time" v-if="isRol == 2">
  40. <block v-if="isJie == false">邀请你通话...</block>
  41. <smhTimer v-if="isJie" ref="timer" @timing="timing" :auto="isJie" />
  42. </view>
  43. </view>
  44. <view class="topInfo" v-if="isRol == 2 && userType == 2">
  45. <image class="topInfo-avatar" :src="resumesIdInfo.avatar" mode="aspectFill"></image>
  46. <view class="topInfo-name">
  47. {{resumesIdInfo.resumesName}}
  48. </view>
  49. <view class="topInfo-time" v-if="isRol == 2">
  50. <block v-if="isJie == false">邀请你通话...</block>
  51. <smhTimer v-if="isJie" ref="timer" @timing="timing" :auto="isJie" />
  52. </view>
  53. </view>
  54. <!-- 未接通 -->
  55. <block v-if="isJie == false">
  56. <!-- 发起人 -->
  57. <view v-if="isRol==1" class="bottomInfo flex align-center justify-center">
  58. <view class="bottomInfo-item" @click="jieOrGuaCall(4)">
  59. <image class="bottomInfo-item-img" src="../../static/images/voice/cancel.png" mode=""></image>
  60. <view class="bottomInfo-item-txt">
  61. 挂断
  62. </view>
  63. </view>
  64. </view>
  65. <!-- 底部操作(语音未接通) 接受人 -->
  66. <view v-if="isRol==2" class="bottomInfo flex align-center justify-between">
  67. <view class="bottomInfo-item" @click="jieOrGuaCall(4)">
  68. <image class="bottomInfo-item-img" src="../../static/images/voice/cancel.png" mode=""></image>
  69. <view class="bottomInfo-item-txt">
  70. 挂断
  71. </view>
  72. </view>
  73. <view class="bottomInfo-item" @click="jieOrGuaCall(2)">
  74. <image class="bottomInfo-item-img" src="../../static/images/voice/answer.png" mode=""></image>
  75. <view class="bottomInfo-item-txt">
  76. 接听
  77. </view>
  78. </view>
  79. </view>
  80. </block>
  81. <!-- 接通 -->
  82. <block v-if="isJie">
  83. <!-- 语音通话 -->
  84. <view class="bottomInfo flex align-center justify-between" v-if="messageType == 21">
  85. <view class="bottomInfo-item" @click="setStopLocalAudio()">
  86. <view class="bottomInfo-item-center">
  87. <image class="bottomInfo-item-centerI"
  88. :src="isOpenMicrophone?'../../static/images/voice/openMkf.png':'../../static/images/voice/closeMkf.png'"
  89. mode="widthFix">
  90. </image>
  91. </view>
  92. <view class="bottomInfo-item-txt">
  93. {{isOpenMicrophone?'麦克风已开':'麦克风已关'}}
  94. </view>
  95. </view>
  96. <view class="bottomInfo-item" @click="jieOrGuaCall(4)">
  97. <image class="bottomInfo-item-img" src="../../static/images/voice/cancel.png" mode=""></image>
  98. <view class="bottomInfo-item-txt">
  99. 挂断
  100. </view>
  101. </view>
  102. </view>
  103. <!-- 视频 -->
  104. <view class="bottomInfo flex align-center justify-center flex-wrap" v-if="messageType == 20">
  105. <view class="flex align-center justify-between" style="width: 100%;">
  106. <view class="bottomInfo-item" @click="setStopLocalAudio()">
  107. <view class="bottomInfo-item-center">
  108. <image class="bottomInfo-item-centerI"
  109. :src="isOpenMicrophone?'../../static/images/voice/openMkf.png':'../../static/images/voice/closeMkf.png'"
  110. mode="widthFix">
  111. </image>
  112. </view>
  113. <view class="bottomInfo-item-txt">
  114. {{isOpenMicrophone?'麦克风已开':'麦克风已关'}}
  115. </view>
  116. </view>
  117. <view class="bottomInfo-item">
  118. <!-- 前置摄像头开启 -->
  119. <view class="bottomInfo-item-center" v-if="cameraType==1" @click="selectVideoCamera()">
  120. <image class="bottomInfo-item-centerI" style="width: 70rpx;"
  121. src="../../static/images/voice/fanzhuan_font.png" mode="widthFix">
  122. </image>
  123. </view>
  124. <!-- 后置摄像头开启 -->
  125. <view class="bottomInfo-item-center" @click="selectVideoCamera()"
  126. style="background-color: rgba(27,27,27, 0.4);" v-else>
  127. <image class="bottomInfo-item-centerI" style="width: 70rpx;"
  128. src="../../static/images/voice/fanhzuan-back.png" mode="widthFix">
  129. </image>
  130. </view>
  131. <view class="bottomInfo-item-txt">
  132. 翻转摄像头
  133. </view>
  134. </view>
  135. <view class="bottomInfo-item">
  136. <!-- 摄像头开启 -->
  137. <view class="bottomInfo-item-center" v-if="isOpenCamera" @click="closeCamera()">
  138. <image class="bottomInfo-item-centerI" style="width: 70rpx;"
  139. src="../../static/images/voice/openSxt.png" mode="widthFix">
  140. </image>
  141. </view>
  142. <!-- 摄像头已关 -->
  143. <view class="bottomInfo-item-center" @click="openCamera()"
  144. style="background-color: rgba(27,27,27, 0.4);" v-else>
  145. <image class="bottomInfo-item-centerI" style="width: 70rpx;"
  146. src="../../static/images/voice/closeSxt.png" mode="widthFix">
  147. </image>
  148. </view>
  149. <view class="bottomInfo-item-txt">
  150. {{isOpenCamera?'摄像头已开':'摄像头已关'}}
  151. </view>
  152. </view>
  153. </view>
  154. <!-- <view class="bottomInfo-item" style="height: 0;width: 164rpx;">
  155. </view> -->
  156. <view class="bottomInfo-item" @click="jieOrGuaCall(4)" style="margin-top: 20rpx;">
  157. <image class="bottomInfo-item-img" src="../../static/images/voice/cancel.png" mode=""></image>
  158. <view class="bottomInfo-item-txt">
  159. 挂断
  160. </view>
  161. </view>
  162. <!-- <view class="bottomInfo-item" style="height: 0;width: 164rpx;">
  163. </view> -->
  164. </view>
  165. </block>
  166. </view>
  167. </template>
  168. <script>
  169. import TRTC from 'trtc-sdk-v5';
  170. import {
  171. BasicBeauty
  172. } from 'trtc-sdk-v5/plugins/video-effect/basic-beauty';
  173. import smhTimer from '@/components/smh-timer/smh-timer.vue'
  174. export default {
  175. components: {
  176. smhTimer
  177. },
  178. data() {
  179. return {
  180. sdkAppId: parseInt(uni.getStorageSync('sdkAppId')), //trtcSdkAppId
  181. byUserId: '', //对方的userId
  182. chatContentId: '',
  183. isRol: 1, //1:发起人 2:接受人
  184. messageType: '21', //类型 20:视频通话 21:语音通话
  185. chatConversationId: '',
  186. screenHeight: '', //屏幕高度
  187. videoStatusInter: null, //获取通话状态定时器
  188. videoStatus: 1, //通话状态 1:未接通 2:接通 4:挂断
  189. isJie: false, //是否接通 true:接通 false:未接通
  190. trtc: null, //trtc单例对象
  191. isOpenMicrophone: true, //是否打开麦克风
  192. isOpenCamera: true, //摄像头是否开启
  193. resumesIdInfo: {
  194. resumesName: '',
  195. avatar: '',
  196. }, //用户简历
  197. company: {
  198. companyLogo: '',
  199. companyName: '',
  200. }, //企业信息
  201. userType: 1, //1:用户 2:企业
  202. postPushId: '', //岗位id,
  203. resumesId: '', //简历id
  204. isRemoterAvailable: false, //是否拉取到别人的视频流
  205. cameraType: 1, //默认使用前置摄像头
  206. };
  207. },
  208. watch: {
  209. videoStatus() {
  210. //通话中
  211. if (this.videoStatus == 2) {
  212. console.log('开始拉取视频')
  213. //接通后开启音频
  214. this.startLocalAudio()
  215. this.isJie = true
  216. } else if (this.videoStatus == 4) { //如果等于4,那么就是挂断电话
  217. console.log(this.videoStatus, '走这里了4444444')
  218. this.exitRoom()
  219. }
  220. },
  221. },
  222. onLoad(option) {
  223. //定时获取通话状态
  224. this.getVideoStatus()
  225. let systemInfo = uni.getSystemInfoSync();
  226. console.log(systemInfo, '系统信息')
  227. //获取屏幕高度
  228. //获取手机系统状态栏高度
  229. this.screenHeight = systemInfo.windowHeight - systemInfo.statusBarHeight;
  230. if (option.byUserId) {
  231. this.byUserId = option.byUserId
  232. }
  233. if (option.isRol) {
  234. this.isRol = option.isRol
  235. }
  236. if (option.messageType) {
  237. this.messageType = option.messageType
  238. }
  239. if (option.chatConversationId) {
  240. this.chatConversationId = option.chatConversationId
  241. }
  242. if (option.chatContentId) {
  243. this.chatContentId = option.chatContentId
  244. //获取getUserSig 开始进房操作
  245. this.getUserSig()
  246. }
  247. if (option.postPushId) {
  248. this.postPushId = option.postPushId
  249. }
  250. if (option.resumesId) {
  251. this.resumesId = option.resumesId
  252. }
  253. // if (option.userType) {
  254. // this.userType = option.userType
  255. // if (this.userType == 1) { //发起人是用户
  256. // if (this.isRol == 1) { //发起人
  257. // //查询企业信息
  258. // this.selectPostPushDetails()
  259. // } else { //接受人
  260. // //查询用户简历信息
  261. // this.selectResumesByResumesId()
  262. // }
  263. // } else { //发起人是企业
  264. // if (this.isRol == 1) { //发起人
  265. // //查询用户简历信息
  266. // this.selectResumesByResumesId()
  267. // } else { //接受人
  268. // //查询企业信息
  269. // this.selectPostPushDetails()
  270. // }
  271. // }
  272. // }
  273. this.userType = uni.getStorageSync('userType')
  274. this.selectPostPushDetails()
  275. this.selectResumesByResumesId()
  276. this.onRemoteVideoAvailable()
  277. this.getRemoteUserExit()
  278. this.addRefreshListener()
  279. },
  280. onUnload(e) {
  281. console.log(e, '页面卸载')
  282. clearInterval(this.videoStatusInter)
  283. this.videoStatusInter = null
  284. this.unloadRoom()
  285. },
  286. onBackPress(e) {
  287. if (e.from != 'navigateBack') { //如果不是挂断也走挂断操作
  288. this.jieOrGuaCall(5)
  289. }
  290. },
  291. methods: {
  292. //翻转摄像头
  293. async selectVideoCamera() {
  294. if (this.cameraType == 2) { //切换至前置摄像头
  295. await this.trtc.updateLocalVideo({
  296. option: {
  297. useFrontCamera: true
  298. }
  299. });
  300. this.cameraType = 1
  301. } else { //切换至后置摄像头
  302. await this.trtc.updateLocalVideo({
  303. option: {
  304. useFrontCamera: false
  305. }
  306. });
  307. this.cameraType = 2
  308. }
  309. },
  310. //计时回掉
  311. timing(e) {
  312. },
  313. //打开摄像头
  314. async openCamera() {
  315. // 打开摄像头
  316. await this.trtc.updateLocalVideo({
  317. mute: false
  318. });
  319. this.isOpenCamera = true
  320. },
  321. //关闭摄像头
  322. async closeCamera() {
  323. // 关闭摄像头,在关闭摄像头后,摄像头预览画面会变成黑屏,您可以在此时显示业务侧自身的 UI 遮罩。
  324. await this.trtc.updateLocalVideo({
  325. mute: true
  326. });
  327. this.isOpenCamera = false
  328. },
  329. //获取企业信息
  330. selectPostPushDetails() {
  331. let data = {
  332. postPushId: this.postPushId
  333. }
  334. this.$Request.getT('/app/postPush/selectPostPushDetails', data).then(res => {
  335. if (res.code == 0) {
  336. this.company = res.data.company
  337. } else {
  338. uni.showToast({
  339. title: res.msg,
  340. icon: 'none'
  341. })
  342. }
  343. })
  344. },
  345. //获取用户简历
  346. selectResumesByResumesId() {
  347. let data = {
  348. resumesId: this.resumesId
  349. }
  350. this.$Request.getT('/app/resumes/selectResumesByResumesId', data).then(res => {
  351. if (res.code == 0) {
  352. this.resumesIdInfo = res.data
  353. }
  354. })
  355. },
  356. //监听页面是否刷新了
  357. addRefreshListener() {
  358. // 监听页面显示的事件,可以用来判断是否刷新了页面
  359. window.addEventListener('pageshow', (event) => {
  360. console.log(event, '页面刷新了')
  361. // alert('页面刷新了')
  362. this.jieOrGuaCall(5)
  363. // 页面刷新了直接挂断电话
  364. });
  365. },
  366. //退房并销毁trtc
  367. async unloadRoom() {
  368. await this.trtc.exitRoom();
  369. // 退房成功后,若后续无需使用 trtc 实例,则可以调用 trtc.destroy 方法销毁实例,及时释放相关资源。销毁后的 trtc 实例无法继续使用,需要重新创建新的实例。
  370. this.trtc.destroy();
  371. },
  372. //关闭/开启摄像头
  373. async setStopLocalAudio() {
  374. if (this.isOpenMicrophone) { //如果开启则静音
  375. await this.trtc.updateLocalAudio({
  376. mute: true
  377. })
  378. this.isOpenMicrophone = false
  379. } else {
  380. await this.trtc.updateLocalAudio({
  381. mute: false
  382. })
  383. this.isOpenMicrophone = true
  384. }
  385. },
  386. //退房
  387. async exitRoom() {
  388. let pages = getCurrentPages();
  389. let hasPrevPage = pages.length > 1;
  390. if (hasPrevPage) {
  391. // 有上一页
  392. //直接返回
  393. uni.navigateBack()
  394. uni.setStorageSync('isDial', false)
  395. } else {
  396. // 没有上一页
  397. //返回首页
  398. uni.switchTab({
  399. url: '/pages/index/index'
  400. })
  401. uni.setStorageSync('isDial', false)
  402. }
  403. },
  404. //接电话或拒绝
  405. jieOrGuaCall(type) {
  406. let data = {
  407. videoStatus: type,
  408. chatContentId: this.chatContentId,
  409. }
  410. this.$Request.post('/app/chat/updateChatContent', data).then(res => {
  411. if (res.code == 0) {
  412. if (type == 4 || type == 5) {
  413. this.exitRoom()
  414. } else if (type == 2) { //接通(接受人)
  415. this.isJie = true
  416. if (this.messageType == 20) { //视频通话时开启摄像头
  417. setTimeout(() => {
  418. this.openCameraAndAudio()
  419. }, 500)
  420. }
  421. }
  422. } else {
  423. uni.showToast({
  424. title: res.msg,
  425. icon: 'none'
  426. })
  427. }
  428. })
  429. },
  430. //获取sign
  431. getUserSig() {
  432. this.$Request.getT('/app/chat/selectSign').then(res => {
  433. if (res.code == 0) {
  434. //进房
  435. this.setEnterRoom(res.data)
  436. } else {
  437. uni.showToast({
  438. title: res.msg,
  439. icon: 'none'
  440. })
  441. }
  442. })
  443. },
  444. //h5进房
  445. async setEnterRoom(userSig) {
  446. try {
  447. let data = {
  448. strRoomId: this.chatContentId.toString(),
  449. scene: 'rtc', //rtc实时通话模式 live在线直播模式
  450. sdkAppId: this.sdkAppId,
  451. userId: uni.getStorageSync('userId') + '',
  452. userSig: userSig,
  453. role: 'anchor', //anchor:“主播”角色,可以推流和拉流。 audience:“观众”角色,只能拉流观看,无法推流。
  454. autoReceiveAudio: true, //自动接收音频
  455. }
  456. await this.trtc.enterRoom(data);
  457. if (this.messageType == 20) { //视频通话时开启摄像头
  458. // if (this.isRol == 1) { //是发起人则直接打开摄像头
  459. // this.openCameraAndAudio()
  460. // }
  461. this.openCameraAndAudio()
  462. }
  463. console.log('进房成功');
  464. } catch (error) {
  465. console.error('进房失败 ' + error);
  466. }
  467. },
  468. async openCameraAndAudio() {
  469. //开启摄像头
  470. const view = 'local-video';
  471. await this.trtc.startLocalVideo({
  472. view
  473. });
  474. // 设置基础美颜效果
  475. await this.trtc.startPlugin('BasicBeauty', {
  476. beauty: 0.5, // 美颜
  477. brightness: 0.5, // 明亮
  478. ruddy: 0.5, // 红润
  479. });
  480. //摄像头状态设置为开启
  481. this.isOpenCamera = true
  482. },
  483. //对方退房(挂断)
  484. getRemoteUserExit() {
  485. //监听对方是否退房
  486. this.trtc.on(TRTC.EVENT.REMOTE_USER_EXIT, event => {
  487. // 如果对方退房了则是对方挂断了了,自己也走退房
  488. this.exitRoom()
  489. });
  490. },
  491. //创建trtc单例并监听视频流
  492. onRemoteVideoAvailable() {
  493. //创建trtc实例
  494. this.trtc = TRTC.create({
  495. plugins: [BasicBeauty]
  496. });
  497. this.trtc.on(TRTC.EVENT.REMOTE_VIDEO_AVAILABLE, ({
  498. userId,
  499. streamType
  500. }) => {
  501. this.isRemoterAvailable = true
  502. // 为了播放视频画面,您需在 DOM 中放置一个 HTMLElement,可以是一个 div 标签,假设其 id 为 `${userId}_${streamType}`
  503. const view = 'myLocalVideo';
  504. this.trtc.startRemoteVideo({
  505. userId,
  506. streamType,
  507. view
  508. });
  509. console.log('已拉取到别人的视频流')
  510. //已拉取到别人的视频流
  511. });
  512. },
  513. //开启麦克风
  514. async startLocalAudio() {
  515. //开启麦克风
  516. await this.trtc.startLocalAudio();
  517. this.isOpenMicrophone = true
  518. },
  519. //定时器获取接电话的状态
  520. getVideoStatus() {
  521. this.videoStatus = uni.getStorageSync('videoStatus')
  522. this.videoStatusInter = setInterval(() => {
  523. this.videoStatus = uni.getStorageSync('videoStatus')
  524. }, 1000)
  525. },
  526. }
  527. }
  528. </script>
  529. <style lang="scss">
  530. .videoContont {
  531. background-image: radial-gradient(circle at center, #00DD9A 10%, #0D0D0D 100%);
  532. position: relative;
  533. }
  534. .mySmartVideo {
  535. width: 200rpx !important;
  536. height: 300rpx !important;
  537. border-radius: 24rpx !important;
  538. position: fixed !important;
  539. top: 100rpx !important;
  540. right: 30rpx !important;
  541. z-index: 9999 !important;
  542. }
  543. .local-video {
  544. width: 100%;
  545. }
  546. .topInfo {
  547. width: 100%;
  548. height: auto;
  549. position: absolute;
  550. top: 230rpx;
  551. display: flex;
  552. flex-direction: column;
  553. align-items: center;
  554. justify-content: center;
  555. .topInfo-avatar {
  556. width: 170rpx;
  557. height: 170rpx;
  558. border-radius: 24rpx;
  559. }
  560. .topInfo-name {
  561. color: #FFFFFF;
  562. font-size: 44rpx;
  563. font-weight: 500;
  564. margin-top: 20rpx;
  565. }
  566. .topInfo-time {
  567. color: #FFFFFF;
  568. font-size: 28rpx;
  569. font-weight: 500;
  570. }
  571. }
  572. .bottomInfo {
  573. width: 100%;
  574. height: auto;
  575. position: absolute;
  576. bottom: 180rpx;
  577. left: 0;
  578. padding: 0 50rpx;
  579. .bottomInfo-item {
  580. display: flex;
  581. flex-direction: column;
  582. align-items: center;
  583. justify-content: center;
  584. }
  585. .bottomInfo-item-center {
  586. width: 164rpx;
  587. height: 164rpx;
  588. border-radius: 50%;
  589. background-color: #FFFFFF;
  590. display: flex;
  591. align-items: center;
  592. justify-content: center;
  593. .bottomInfo-item-centerI {
  594. width: 64rpx;
  595. height: 64rpx;
  596. }
  597. }
  598. .bottomInfo-item-img {
  599. width: 164rpx;
  600. height: 164rpx;
  601. border-radius: 50%;
  602. }
  603. .bottomInfo-item-txt {
  604. color: #FFFFFF;
  605. font-size: 28rpx;
  606. font-weight: 500;
  607. margin-top: 12rpx;
  608. }
  609. }
  610. </style>