realTime.vue 30 KB

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