realTime.vue 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163
  1. <!-- -->
  2. <template>
  3. <div class="realTime_box">
  4. <!-- 搜索 -->
  5. <el-row>
  6. <el-col>
  7. <!-- 组织搜索 -->
  8. <div class="search_box">
  9. <el-input
  10. size="mini"
  11. placeholder="请输入用户姓名"
  12. v-model.trim="nameVal"
  13. @change="searchData"
  14. clearable
  15. >
  16. </el-input>
  17. <el-input
  18. size="mini"
  19. placeholder="请输入手机号"
  20. v-model.trim="phoneVal"
  21. @change="searchData"
  22. clearable
  23. >
  24. </el-input>
  25. <div class="btn_box">
  26. <el-button type="info" size="mini" @click="searchData"
  27. >搜索</el-button
  28. >
  29. </div>
  30. </div>
  31. </el-col>
  32. </el-row>
  33. <el-card>
  34. <div class="card_box" style="margin-top: 15px">
  35. <!-- 组织 -->
  36. <div
  37. class="userManger_left"
  38. :style="'height:' + 26 * 25 + 'px;' + 'overflow-y: auto'"
  39. >
  40. <el-tree
  41. :data="data"
  42. :props="defaultProps"
  43. v-loading="loading2"
  44. @node-click="handleNodeClick"
  45. ></el-tree>
  46. </div>
  47. <!-- 表格 -->
  48. <div class="userManger_right">
  49. <el-table
  50. v-loading="loading"
  51. :data="tableData"
  52. stripe
  53. :height="48 * 13"
  54. style="width: 100%; overflow-y: auto"
  55. >
  56. <el-table-column prop="ind" label="序号" width="180">
  57. <template slot-scope="scope">
  58. <span>{{ (page - 1) * 20 + scope.row.ind }}</span>
  59. </template>
  60. </el-table-column>
  61. <el-table-column prop="real_name" label="组织成员" width="330">
  62. </el-table-column>
  63. <el-table-column prop="mobile" label="手机号" width="250">
  64. <template slot-scope="scope">
  65. <span>{{ scope.row.mobile || "无" }}</span>
  66. </template>
  67. </el-table-column>
  68. <el-table-column
  69. :show-overflow-tooltip="true"
  70. prop="address"
  71. label="职位"
  72. width="320"
  73. >
  74. <template slot-scope="scope">
  75. <span v-for="(item, index) in scope.row.org_list" :key="index"
  76. >{{ item.org_name }}
  77. <span v-if="index !== scope.row.org_list.length - 1">、</span>
  78. </span>
  79. </template>
  80. </el-table-column>
  81. <el-table-column label="操作" width="200" fixed="right">
  82. <template slot-scope="scope">
  83. <span
  84. style="
  85. height: 30px;
  86. display: inline-block;
  87. margin: 0 0 0 0;
  88. position: absolute;
  89. left: 0;
  90. top: 11px;
  91. "
  92. >
  93. <el-badge
  94. v-if="scope.row.unread"
  95. :value="scope.row.unread"
  96. is-dot
  97. :max="99"
  98. class="item"
  99. >
  100. <img
  101. @click="msgAxios(scope.row)"
  102. src="../../assets/images/realTime/xiaoxi.png"
  103. alt=""
  104. />
  105. </el-badge>
  106. <img
  107. v-else
  108. @click="msgAxios(scope.row)"
  109. src="../../assets/images/realTime/xiaoxi.png"
  110. alt=""
  111. />
  112. </span>
  113. <span style="position: absolute; top: 12px; left: 60px">
  114. <img
  115. @click="videoAxios(scope.row)"
  116. src="../../assets/images/realTime/shipin.png"
  117. alt=""
  118. />
  119. </span>
  120. </template>
  121. </el-table-column>
  122. </el-table>
  123. <!-- 分页 -->
  124. <el-pagination
  125. v-if="tableData.length !== 0"
  126. @current-change="changeList"
  127. background
  128. layout="prev, pager, next, jumper"
  129. :current-page="page"
  130. :total="pageSum"
  131. :page-size="20"
  132. >
  133. </el-pagination>
  134. </div>
  135. </div>
  136. </el-card>
  137. <!-- 视频通话 -->
  138. <el-dialog
  139. :close-on-click-modal="false"
  140. :close-on-press-escape="false"
  141. :before-close="handleClose"
  142. :modal="false"
  143. v-dialogDrag
  144. :title="videoTle"
  145. :visible.sync="videoVisible"
  146. width="600px"
  147. >
  148. <div class="video_box">
  149. <div class="host_video" v-if="videoShow">
  150. <img
  151. src="../../assets/images/realTime/8.png"
  152. alt=""
  153. class="video_img"
  154. />
  155. <!-- <img
  156. v-if="audioShow"
  157. src="../../assets/images/realTime/8.png"
  158. alt=""
  159. class=""
  160. style="margin: 120px atuo;"
  161. />
  162. <img
  163. v-else
  164. src="../../assets/images/realTime/13.png"
  165. alt=""
  166. class=""
  167. /> -->
  168. </div>
  169. <div class="host_video" v-else>
  170. <!-- <video
  171. style="width: 100%; height: 100%"
  172. ref="localVideo"
  173. autoplay
  174. playsinline
  175. :muted="true"
  176. ></video> -->
  177. <video
  178. style="width: 100%; height: 100%"
  179. :ref="idName1"
  180. autoplay
  181. playsinline
  182. :muted="true"
  183. ></video>
  184. </div>
  185. <!-- 副视频画面 -->
  186. <div v-if="audioShow" @click="switchWindow" class="deputy_video" v-drag>
  187. <!-- <video
  188. style="width: 100%; height: 100%"
  189. ref="remoteVideo"
  190. autoplay
  191. playsinline
  192. :muted="true"
  193. ></video> -->
  194. <video
  195. style="width: 100%; height: 100%"
  196. :ref="idName"
  197. autoplay
  198. playsinline
  199. :muted="true"
  200. ></video>
  201. </div>
  202. <!-- 操作 -->
  203. <div class="operate">
  204. <!-- <div style="margin: 0 10px 0 0">
  205. <img
  206. v-if="microphone"
  207. src="../../assets/images/realTime/10.png"
  208. alt=""
  209. />
  210. <img v-else src="../../assets/images/realTime/9.png" alt="" />
  211. </div> -->
  212. <div v-if="audioShow" style="margin: 0 10px 0 0">
  213. <img
  214. v-if="camera"
  215. @click="notPlugFlow"
  216. src="../../assets/images/realTime/12.png"
  217. alt=""
  218. />
  219. <img
  220. @click="openVideo"
  221. v-else
  222. src="../../assets/images/realTime/11.png"
  223. alt=""
  224. />
  225. </div>
  226. <el-button
  227. type="danger"
  228. style="padding: 2px 10px; margin: -2px 10px 0 0"
  229. @click="notLogin"
  230. size="mini"
  231. >挂断</el-button
  232. >
  233. </div>
  234. </div>
  235. </el-dialog>
  236. <!-- 文本聊天框 -->
  237. <el-dialog
  238. custom-class="msgFrame"
  239. :title="tltData"
  240. v-dialogDrag
  241. :close-on-click-modal="false"
  242. :modal="false"
  243. :close-on-press-escape="false"
  244. :visible.sync="dialogVisible"
  245. width="500px"
  246. >
  247. <!-- 聊天内容 -->
  248. <ul class="ul_list test-5" id="msgBox">
  249. <!-- 查看更多 -->
  250. <!-- <li class="">
  251. <div
  252. v-if="iconShow"
  253. style="color: #0400ff; cursor: pointer; text-align: center"
  254. @click="moreMsg"
  255. >
  256. 查看更多消息
  257. </div>
  258. </li> -->
  259. <li class="list" v-for="(item, index) in msgList.msg_list" :key="index">
  260. <!-- me -->
  261. <div class="list_msgBox1" v-if="item.is_right == true">
  262. <div
  263. style="
  264. background: #60fba5;
  265. font-weight: 550;
  266. color: #000;
  267. padding: 12px 15px 0 15px;
  268. line-height: 25px;
  269. border-radius: 8px;
  270. "
  271. >
  272. {{ item.msg_info }}
  273. </div>
  274. <div
  275. style="
  276. width: 0;
  277. height: 0;
  278. border-top: 6px solid transparent;
  279. border-left: 10px solid #60fba5;
  280. border-bottom: 5px solid transparent;
  281. margin: 23px 0 0 0;
  282. "
  283. ></div>
  284. <div>
  285. <img
  286. src="../../../static/images/realTime/7.png"
  287. alt=""
  288. class=""
  289. />
  290. </div>
  291. </div>
  292. <!-- you -->
  293. <div v-else-if="item.is_right == false" class="list_msgBox2">
  294. <div>
  295. <img
  296. src="../../../static/images/realTime/6.png"
  297. alt=""
  298. class=""
  299. />
  300. </div>
  301. <div
  302. style="
  303. width: 0;
  304. height: 0;
  305. border-top: 5px solid transparent;
  306. border-right: 10px solid #eae9eb;
  307. border-bottom: 6px solid transparent;
  308. margin: 24px 0 0 0;
  309. "
  310. ></div>
  311. <div
  312. style="
  313. background: #eae9eb;
  314. padding: 12px 15px 0 15px;
  315. line-height: 25px;
  316. border-radius: 8px;
  317. font-weight: 550;
  318. color: #000;
  319. "
  320. >
  321. {{ item.msg_info }}
  322. </div>
  323. </div>
  324. </li>
  325. </ul>
  326. <!-- 发送框 -->
  327. <el-input
  328. style="border: 0"
  329. resize="none"
  330. type="textarea"
  331. v-model="input"
  332. @keyup.enter.native="submit()"
  333. @keydown.native="pushKeyword($event)"
  334. maxlength="30"
  335. show-word-limit
  336. placeholder="按下enter按键发送消息"
  337. ></el-input>
  338. </el-dialog>
  339. </div>
  340. </template>
  341. <script>
  342. import { color } from "highcharts";
  343. import { ZegoExpressEngine } from "zego-express-engine-webrtc";
  344. export default {
  345. //import引入的组件需要注入到对象中才能使用
  346. components: {},
  347. data() {
  348. //这里存放数据
  349. return {
  350. fullHeight: document.documentElement.clientHeight - 116, //
  351. // 搜索
  352. nameVal: "",
  353. phoneVal: "",
  354. // 表格
  355. tableData: [],
  356. websockMsgList: [], // websocket传递来的数组数据
  357. loading: false, // 加载
  358. org_id: "", //组织id
  359. // 树形图
  360. data: [],
  361. loading2: false,
  362. defaultProps: {
  363. children: "childrens",
  364. label: "org_name",
  365. },
  366. // 分页
  367. pageSum: 0,
  368. page: 1,
  369. // 视频通话弹框
  370. idName: "remoteVideo", // ref 副视频
  371. idName1: "localVideo", // ref 主视频
  372. videoVisible: false,
  373. videoShow: true, //主视频是否展示画面
  374. audioShow: true, // 判断当前是语音通话还是视频通话 true为视频 false为语音
  375. microphone: true, // 是否打开麦克风
  376. camera: true, // 是否打开摄像头
  377. appID: 2672645646, //项目唯一标识 AppID
  378. // server: "1e8344b2a193220e5e96338ba53c3dcc", // 接入服务器地址Server
  379. videoUrl: "wss://webliveroom2672645646-api.imzego.com/ws", // 请求
  380. zg: null,
  381. // UserID: "sample" + Math.floor(Math.random() * 10000000000000).toString(),
  382. UserID: "user00002",
  383. // UserID: "168",
  384. // StreamID: "web-4796754531236",
  385. StreamID: "web-" + Math.floor(Math.random() * 10000000000000).toString(),
  386. localStream: null,
  387. Token: "",
  388. RoomID: "",
  389. videoTle: "正在和云飞-卢万里视频通话",
  390. // 文本聊天框
  391. tltData: "",
  392. dialogVisible: false,
  393. input: "", //发送框
  394. iconShow: true,
  395. // 文本消息功能
  396. url:
  397. // "ws://192.168.1.17:12345/api/api_gateway?method=control_center.real_time.im_message",
  398. "ws" +
  399. this.$wsUrl +
  400. "/api/api_gateway?method=control_center.real_time.im_message",
  401. websock: null,
  402. getUserObj: {}, // 获取到当前点击的行数据
  403. msgList: [], //当前点击的账号消息列表
  404. userName: "",
  405. };
  406. },
  407. //监听属性 类似于data概念
  408. computed: {},
  409. //监控data中的数据变化
  410. watch: {
  411. fullHeight(val) {
  412. //监控浏览器高度变化
  413. if (!this.timer) {
  414. this.fullHeight = val;
  415. this.timer = true;
  416. let that = this;
  417. setTimeout(function () {
  418. //防止过度调用监测事件,导致卡顿
  419. that.timer = false;
  420. }, 400);
  421. }
  422. },
  423. // 聊天列表
  424. msgList(val) {
  425. // 实现打开聊天框后滚动条定位到最下方
  426. this.$nextTick(() => {
  427. var div = document.getElementById("msgBox");
  428. div.scrollTop = div.scrollHeight;
  429. });
  430. },
  431. // 聊天框
  432. dialogVisible(val) {
  433. if (val == false) {
  434. this.msgList = [];
  435. }
  436. },
  437. // 音视频弹框
  438. videoVisible(val) {
  439. if (val == false) {
  440. this.notPlugFlow(); // 停止推流
  441. this.notTensile(); // 停止拉流
  442. this.notLogin(); //退出房间
  443. }
  444. },
  445. // 给表格文本消息增加提示,数据从webscoket中获取
  446. tableData(val) {
  447. for (var i = 0; i < val.length; i++) {
  448. let sdhkj = val[i];
  449. for (var k = 0; k < this.websockMsgList.length; k++) {
  450. let sdjaks = this.websockMsgList[k];
  451. if (sdhkj.user_id == sdjaks.user_id) {
  452. this.tableData[k].unread = sdjaks.unread;
  453. }
  454. }
  455. }
  456. },
  457. // 测试
  458. },
  459. //方法集合
  460. methods: {
  461. //动态获取浏览器高度
  462. get_boderHeight() {
  463. const that = this;
  464. window.onresize = () => {
  465. return (() => {
  466. window.fullHeight = document.documentElement.clientHeight;
  467. that.fullHeight = window.fullHeight;
  468. })();
  469. };
  470. },
  471. // 树形图
  472. handleNodeClick(data) {
  473. console.log(data);
  474. this.org_id = data.id;
  475. this.userListData();
  476. },
  477. // 搜索
  478. searchData() {
  479. this.userListData();
  480. },
  481. // 分页
  482. changeList(page) {
  483. this.page = page;
  484. this.loading = true;
  485. this.tableData = [];
  486. this.userListData();
  487. },
  488. // 获取左侧组织列表
  489. organizationData() {
  490. this.$axios({
  491. method: "POST",
  492. url: "/api/api_gateway?method=sysmenage.usermanager.org_list",
  493. data: this.qs.stringify({
  494. page: this.page,
  495. page_item: "100000000",
  496. org_name: "",
  497. }),
  498. })
  499. .then((res) => {
  500. if (res.data.data.page_list.length !== 0) {
  501. var obj = {
  502. org_name: "全部",
  503. id: "",
  504. };
  505. var data = res.data.data.page_list;
  506. this.data = [obj, ...data]; // 左侧组织列表
  507. }
  508. this.loading2 = false;
  509. })
  510. .catch((err) => {
  511. this.loading2 = false;
  512. });
  513. },
  514. // 获取右侧用户列表
  515. userListData() {
  516. this.$axios({
  517. method: "POST",
  518. url: "/api/api_gateway?method=sysmenage.usermanager.user_list",
  519. data: this.qs.stringify({
  520. page: this.page,
  521. page_item: "20",
  522. real_name: this.nameVal, //用户名称
  523. mobile: this.phoneVal, // 电话
  524. org_id: this.org_id,
  525. }),
  526. })
  527. .then((res) => {
  528. if (res.data.data.total_item !== 0) {
  529. var data = res.data.data.page_list;
  530. var list = [];
  531. data.forEach((item, index) => {
  532. item.ind = index + 1;
  533. list.push(item);
  534. });
  535. this.tableData = list;
  536. this.pageSum = res.data.data.total_item;
  537. this.loading = false;
  538. }
  539. })
  540. .catch((err) => {
  541. this.loading = false;
  542. });
  543. },
  544. // 文本消息
  545. msgAxios(data) {
  546. this.getUserObj = data;
  547. this.userName = localStorage.getItem("usernme");
  548. console.log(this.userName);
  549. this.tltData = "与" + data.real_name + "的对话";
  550. var obj = {};
  551. obj = {
  552. action: "list",
  553. recv_user_id: data.user_id,
  554. data: {
  555. msg_status: false,
  556. msg_info: "",
  557. },
  558. };
  559. this.websock.send(JSON.stringify(obj)); // 获取聊天记录
  560. var readObj = {};
  561. readObj = {
  562. action: "read",
  563. send_user_id: data.user_id,
  564. data: {
  565. msg_status: false,
  566. msg_info: "",
  567. },
  568. };
  569. this.websock.send(JSON.stringify(readObj)); // 消息已读
  570. this.dialogVisible = true;
  571. },
  572. // 视频消息
  573. videoAxios(data) {
  574. this.getUserObj = data;
  575. this.userName = localStorage.getItem("username");
  576. this.videoTle = "正在和" + data.real_name + "视频通话";
  577. this.UserID = localStorage.getItem("userID");
  578. // 先获取当前用户的房间号和登录所需的Token
  579. var obj = {};
  580. obj = {
  581. action: "send_video",
  582. recv_user_id: data.user_id,
  583. data: {
  584. msg_status: false,
  585. msg_info: "",
  586. },
  587. };
  588. this.websock.send(JSON.stringify(obj));
  589. // // 登录房间
  590. // this.zg.loginRoom(
  591. // this.RoomID,
  592. // this.Token,
  593. // { userID: this.UserID, userName: this.UserID },
  594. // { userUpdate: true }
  595. // ).then((result) => {
  596. // if (result == true) {
  597. // this.plugFlow(); //推流
  598. // this.videoVisible = true
  599. // }
  600. // });
  601. },
  602. // 打开视频画面
  603. openVideo() {
  604. this.plugFlow();
  605. },
  606. // 音视频推流
  607. async plugFlow() {
  608. this.videoShow = false;
  609. this.camera = true;
  610. const localStream = await this.zg.createStream();
  611. // stream 为 MediaStream 对象,开发者可通过赋值给 video 或 audio 的 srcObject 属性进行渲染
  612. this.$refs["localVideo"].srcObject = localStream;
  613. this.localStream = localStream;
  614. // localStream 为创建流获取的 MediaStream 对象
  615. this.zg.startPublishingStream(this.StreamID, localStream);
  616. },
  617. // 音视频停止推流
  618. notPlugFlow() {
  619. this.videoShow = true;
  620. this.camera = false;
  621. this.zg.stopPublishingStream(this.StreamID);
  622. this.zg.destroyStream(this.localStream);
  623. },
  624. // 拉流
  625. async tensile(streamID) {
  626. const remoteStream = await this.zg.startPlayingStream(streamID);
  627. // remoteVideo 为本地 <video> 或 <audio> 对象
  628. this.$refs["remoteVideo"].srcObject = remoteStream;
  629. },
  630. notTensile(streamID) {
  631. this.zg.stopPlayingStream(streamID);
  632. },
  633. // 切换视频窗口
  634. switchWindow() {
  635. // 副视频画面 remoteVideo
  636. // 主视频画面 localVideo
  637. this.idName = this.idName == "remoteVideo" ? "localVideo" : "remoteVideo";
  638. this.idName =
  639. this.idName1 == "remoteVideo" ? "localVideo" : "remoteVideo";
  640. },
  641. // 退出房间
  642. notLogin() {
  643. this.zg.logoutRoom(this.RoomID);
  644. this.videoVisible = false;
  645. },
  646. // 当音视频通话关闭时的回调
  647. handleClose(done) {
  648. this.$confirm("目前正在音视频通话中,确认关闭?")
  649. .then((_) => {
  650. done();
  651. })
  652. .catch((_) => {});
  653. },
  654. // 发送消息
  655. async submit() {
  656. if (this.input.split(" ").join("").length == 0) {
  657. if (document.getElementsByClassName("el-message").length == 0) {
  658. this.$message({
  659. message: "不能发送空白消息!",
  660. type: "warning",
  661. duration: 1500,
  662. });
  663. }
  664. this.input = "";
  665. } else {
  666. try {
  667. var obj = {};
  668. obj = {
  669. action: "send",
  670. recv_user_id: this.getUserObj.user_id,
  671. data: {
  672. msg_status: false,
  673. msg_info: this.input,
  674. },
  675. };
  676. var v = JSON.stringify(obj);
  677. this.websock.send(v);
  678. this.input = "";
  679. } catch (error) {
  680. console.log(">>> sendMsg, error: ", error);
  681. }
  682. }
  683. },
  684. // 取消回车换行行为
  685. pushKeyword(event) {
  686. if (event.keyCode === 13) {
  687. event.preventDefault(); // 阻止浏览器默认换行操作
  688. this.keyword = "";
  689. return false;
  690. }
  691. },
  692. // 更多消息
  693. moreMsg() {},
  694. // 文本消息功能初始化
  695. async msgInit() {
  696. var that = this;
  697. if (typeof WebSocket === "undefined") {
  698. alert("您的浏览器不支持socket!");
  699. } else {
  700. this.websock = new window.WebSocket(
  701. this.url + "&token=" + localStorage.getItem("session")
  702. );
  703. this.websock.onopen = (event) => {
  704. console.log("WebSocket:已连接");
  705. console.log(event);
  706. // 发送消息 - 获取对话列表
  707. var obj1 = {};
  708. obj1 = {
  709. action: "list",
  710. recv_user_id: "",
  711. data: {
  712. msg_status: false,
  713. msg_info: "",
  714. },
  715. };
  716. this.websock.send(JSON.stringify(obj1));
  717. };
  718. this.websock.onmessage = (event) => {
  719. // console.log("WebSocket:消息");
  720. var data = JSON.parse(event.data);
  721. if (data.action == "none") {
  722. // 获取聊天记录
  723. var datArr = data.data; // 总数据
  724. this.websockMsgList = datArr; // 把数据定义在data中
  725. if (datArr.length !== 0) {
  726. for (var i = 0; i < datArr.length; i++) {
  727. if (this.getUserObj.user_id == datArr[i].user_id) {
  728. data.data[i].msg_list = data.data[i].msg_list.reverse();
  729. that.msgList = data.data[i];
  730. console.log(data.data[i]);
  731. }
  732. }
  733. }
  734. // if (data.data[0] !== undefined) {
  735. // data.data[0].msg_list = data.data[0].msg_list.reverse();
  736. // this.msgList = data.data[0];
  737. // }
  738. } else if (data.action == "list") {
  739. // 返回list为发送消息成功后需要再次调用聊天列表
  740. var obj = {};
  741. obj = {
  742. action: "list",
  743. recv_user_id: this.getUserObj.user_id,
  744. data: {
  745. msg_status: false,
  746. msg_info: "",
  747. },
  748. };
  749. this.websock.send(JSON.stringify(obj));
  750. } else if (data.action == "recv_video") {
  751. // 获取当前点击用户的房间号以及登录房间所需的Token
  752. // console.log(JSON.parse(event.data));
  753. console.log(data);
  754. var data = JSON.parse(event.data);
  755. this.RoomID = data.data.room_id; // 房间号
  756. this.Token = data.data.room_token; // Token
  757. // 登录房间
  758. this.zg
  759. .loginRoom(
  760. this.RoomID,
  761. this.Token,
  762. { userID: this.UserID, userName: this.UserID },
  763. { userUpdate: true }
  764. )
  765. .then((result) => {
  766. if (result == true) {
  767. console.log("0000000000000000000000000000000000000登录成功");
  768. this.plugFlow(); //推流
  769. this.videoVisible = true;
  770. }
  771. });
  772. }
  773. };
  774. this.websock.onerror = (event) => {
  775. console.log("WebSocket:发生错误 ");
  776. console.log(event);
  777. };
  778. this.websock.onclose = (event) => {
  779. console.log("WebSocket:已关闭");
  780. console.log(event);
  781. };
  782. }
  783. },
  784. // 音视频消息功能初始化
  785. videoInit() {
  786. this.zg = new ZegoExpressEngine(this.appID, this.videoUrl);
  787. this.soundOn(); // 监听房间
  788. this.detection(); // 检测是否兼容当前浏览器
  789. },
  790. // 监听房间
  791. soundOn() {
  792. var timer;
  793. // 房间状态更新回调
  794. this.zg.on(
  795. "roomStateUpdate",
  796. (roomID, state, errorCode, extendedData) => {
  797. if (state == "CONNECTED") {
  798. // 与房间连接成功,只有当房间状态是连接成功时,才能进行推流、拉流等操作。
  799. // 接下来的“预览并推流”的代码写在这里
  800. console.log("房间连接成功");
  801. //定时器
  802. timer = setTimeout(() => {
  803. if (document.getElementsByClassName("el-message").length == 0) {
  804. this.$message({
  805. message: "没人接听,请稍后重试",
  806. type: "warning",
  807. duration: 2500,
  808. });
  809. }
  810. this.videoVisible = false;
  811. }, 5000);
  812. // }, 60000);
  813. }
  814. if (state == "DISCONNECTED") {
  815. // 与房间断开了连接
  816. console.log("与房间断开连接");
  817. }
  818. if (state == "CONNECTING") {
  819. // 与房间尝试连接中
  820. console.log("与房间尝试连接中");
  821. }
  822. }
  823. );
  824. this.zg.on("roomUserUpdate", (roomID, updateType, userList) => {
  825. // 其他用户进出房间的通知
  826. console.log(updateType);
  827. console.log(userList);
  828. clearTimeout(timer);
  829. console.log("有其他用户进出房间");
  830. });
  831. this.zg.on(
  832. "roomStreamUpdate",
  833. async (roomID, updateType, streamList, extendedData) => {
  834. console.log(roomID);
  835. console.log(updateType);
  836. console.log(streamList);
  837. console.log(extendedData);
  838. // 房间内其他用户音视频流变化的通知
  839. console.log("有其他用户开启或关闭音频");
  840. if (updateType == "ADD") {
  841. // 流新增,开始拉流
  842. this.tensile(streamList[0].streamID);
  843. } else if (updateType == "DELETE") {
  844. // 流删除,停止拉流
  845. this.notTensile(streamList[0].streamID);
  846. }
  847. }
  848. );
  849. this.zg.on("publisherStateUpdate", (result) => {
  850. // 推流状态更新回调
  851. console.log("推流状态更新");
  852. });
  853. this.zg.on("publishQualityUpdate", (streamID, stats) => {
  854. // 推流质量回调
  855. console.log("推流质量更新");
  856. });
  857. // this.zg.on("IMRecvBroadcastMessage", (roomID, chatData) => {
  858. // console.log("IMRecvBroadcastMessage", roomID, chatData);
  859. // console.log(">>>>>>>>>>>>>>>>>>>>>接收到文本消息了");
  860. // var list = {
  861. // id: "2", //模拟数据中使用id来区分当前是发送人还是接收人 1为接收人 2为发送人
  862. // msg: chatData[0].message, // 文本消息
  863. // img: "../../../static/img/6.png", // 头像url
  864. // };
  865. // this.msgList = [...this.msgList, list];
  866. // // let message = {
  867. // // ID: 'zego' + chatData[0].fromUser.userID + chatData[0].sendTime,
  868. // // name: chatData[0].fromUser.userName,
  869. // // time: format(chatData[0].sendTime),
  870. // // content: chatData[0].message + '(广播发送)'
  871. // // }
  872. // });
  873. },
  874. // 检测是否兼容当前浏览器
  875. async detection() {
  876. const result = await this.zg.checkSystemRequirements();
  877. // 返回的 result 为兼容性检测结果。 webRTC 为 true 时表示支持 webRTC,其他属性含义可以参考接口 API 文档
  878. console.log(result);
  879. if (result.webRTC == true) {
  880. console.log("兼容");
  881. } else {
  882. console.log("不兼容");
  883. }
  884. },
  885. },
  886. //生命周期 - 创建完成(可以访问当前this实例)
  887. created() {
  888. this.loading = true;
  889. this.loading2 = true;
  890. this.organizationData(); //获取左侧组织列表
  891. this.userListData(); // 获取右侧用户列表
  892. },
  893. //生命周期 - 挂载完成(可以访问DOM元素)
  894. mounted() {
  895. // this.loading = true;
  896. // this.loading2 = true;
  897. // this.organizationData(); //获取左侧组织列表
  898. // this.userListData(); // 获取右侧用户列表
  899. console.log("---------------------------");
  900. console.log(this.websock);
  901. console.log("---------------------------");
  902. if (this.websock == null) {
  903. this.msgInit(); // 文本消息功能初始化
  904. }
  905. this.videoInit(); // 视频消息功能初始化
  906. },
  907. beforeCreate() {}, //生命周期 - 创建之前
  908. beforeMount() {}, //生命周期 - 挂载之前
  909. beforeUpdate() {}, //生命周期 - 更新之前
  910. updated() {}, //生命周期 - 更新之后
  911. beforeDestroy() {}, //生命周期 - 销毁之前
  912. destroyed() {}, //生命周期 - 销毁完成
  913. activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发
  914. };
  915. </script>
  916. <style lang="less" scoped>
  917. .realTime_box {
  918. // 搜索
  919. .search_box {
  920. display: flex;
  921. /deep/.el-input {
  922. width: 20%;
  923. margin: 0 15px 0 0;
  924. }
  925. .btn_box {
  926. display: flex;
  927. justify-content: start;
  928. }
  929. }
  930. .card_box {
  931. display: flex;
  932. width: 100%;
  933. // 树形图
  934. .userManger_left {
  935. width: 19%;
  936. margin: 0 15px 0 0;
  937. padding: 5px;
  938. border: 1px solid #eeeeee;
  939. border-radius: 5px;
  940. overflow: hidden;
  941. overflow-y: auto;
  942. }
  943. // 搜索和表格
  944. .userManger_right {
  945. width: 80%;
  946. // border: 1px solid red;
  947. a {
  948. text-decoration: none;
  949. }
  950. .reset {
  951. color: #1890ff;
  952. }
  953. .delete {
  954. color: #f56c6c;
  955. }
  956. .line {
  957. display: inline-block;
  958. width: 1px;
  959. background: rgba(0, 0, 0, 0.09);
  960. margin: 0 11px;
  961. height: 14px;
  962. }
  963. }
  964. }
  965. /deep/.el-select {
  966. width: 80%;
  967. }
  968. /deep/.el-cascader {
  969. width: 80%;
  970. }
  971. // 视频通话
  972. .video_box {
  973. border: 1px solid #000;
  974. // margin: 15px 0 0 0;
  975. background: #2c2c2c;
  976. height: 325px;
  977. position: relative;
  978. // 主视频画面
  979. .host_video {
  980. // width: 100%;
  981. height: 326px;
  982. // height: 300px;
  983. // display: table-cell;
  984. vertical-align: middle;
  985. text-align: center;
  986. .video_img {
  987. margin: 120px auto;
  988. }
  989. }
  990. // 副视频画面
  991. .deputy_video {
  992. width: 90px;
  993. height: 90px;
  994. position: absolute;
  995. top: 0;
  996. right: 0;
  997. background: #fff;
  998. }
  999. // 操作
  1000. .operate {
  1001. height: 25px;
  1002. width: 100%;
  1003. background: #000;
  1004. padding: 5px 0 5px 0;
  1005. margin: -13px 0 0 0px;
  1006. position: absolute;
  1007. bottom: 0;
  1008. left: 0;
  1009. display: flex;
  1010. justify-content: flex-end;
  1011. }
  1012. }
  1013. /* 聊天内容 */
  1014. .ul_list {
  1015. height: 350px;
  1016. // margin-left: -41px;
  1017. overflow: hidden;
  1018. overflow-y: auto;
  1019. }
  1020. .list_msgBox1 {
  1021. display: flex;
  1022. margin-top: 22px;
  1023. justify-content: flex-end;
  1024. }
  1025. .list_msgBox2 {
  1026. display: flex;
  1027. margin-top: 22px;
  1028. }
  1029. .test-5::-webkit-scrollbar {
  1030. /*滚动条整体样式*/
  1031. width: 10px; /*高宽分别对应横竖滚动条的尺寸*/
  1032. height: 1px;
  1033. }
  1034. .test-5::-webkit-scrollbar-thumb {
  1035. /*滚动条里面小方块*/
  1036. border-radius: 10px;
  1037. background-color: skyblue;
  1038. background-image: -webkit-linear-gradient(
  1039. 45deg,
  1040. rgba(255, 255, 255, 0.2) 25%,
  1041. transparent 25%,
  1042. transparent 50%,
  1043. rgba(255, 255, 255, 0.2) 50%,
  1044. rgba(255, 255, 255, 0.2) 75%,
  1045. transparent 75%,
  1046. transparent
  1047. );
  1048. }
  1049. .test-5::-webkit-scrollbar-track {
  1050. /*滚动条里面轨道*/
  1051. box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
  1052. background: #ededed;
  1053. border-radius: 10px;
  1054. }
  1055. .list {
  1056. /* border: 1px solid red; */
  1057. width: 100%;
  1058. height: 100px;
  1059. }
  1060. /* ElementUI 样式 */
  1061. // 聊天框
  1062. /deep/.el-dialog {
  1063. overflow: hidden;
  1064. }
  1065. /deep/.el-dialog__header {
  1066. background: #f2f2f2;
  1067. border-bottom: 1px solid #cacaca;
  1068. }
  1069. /deep/.el-dialog__title {
  1070. font-weight: 550;
  1071. font-size: 15px;
  1072. line-height: 0;
  1073. float: left;
  1074. }
  1075. /deep/.el-dialog__headerbtn {
  1076. top: 10px;
  1077. right: 10px;
  1078. }
  1079. /deep/.el-dialog__headerbtn .el-dialog__close {
  1080. font-size: 20px;
  1081. line-height: 15px;
  1082. }
  1083. /deep/.el-dialog__body {
  1084. padding: 0;
  1085. // margin-top: -15px;
  1086. }
  1087. /deep/.el-textarea__inner {
  1088. border: 0;
  1089. border-top: 1px solid #dcdfe6;
  1090. border-radius: 0;
  1091. height: 95px;
  1092. }
  1093. /deep/.el-card {
  1094. overflow: hidden;
  1095. overflow-y: auto;
  1096. }
  1097. }
  1098. /deep/.el-button--info {
  1099. background-color: #409eff;
  1100. border-color: #409eff;
  1101. }
  1102. </style>