realTime.vue 32 KB

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