realTime.vue 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575
  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 style="margin-top: 15px">
  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 v-if="scope.row.unread" is-dot class="item">
  94. <img
  95. @click="msgAxios(scope.row)"
  96. src="../../assets/images/realTime/xiaoxi.png"
  97. alt=""
  98. />
  99. </el-badge>
  100. <img
  101. v-else
  102. @click="msgAxios(scope.row)"
  103. src="../../assets/images/realTime/xiaoxi.png"
  104. alt=""
  105. />
  106. </span>
  107. <span style="position: absolute; top: 12px; left: 60px">
  108. <img
  109. @click="videoAxios(scope.row)"
  110. src="../../assets/images/realTime/shipin.png"
  111. alt=""
  112. />
  113. </span>
  114. </template>
  115. </el-table-column>
  116. </el-table>
  117. <!-- 分页 -->
  118. <el-pagination
  119. v-if="tableData.length !== 0"
  120. @current-change="changeList"
  121. background
  122. layout="prev, pager, next, jumper"
  123. :current-page="page"
  124. :total="pageSum"
  125. :page-size="20"
  126. >
  127. </el-pagination>
  128. </div>
  129. </div>
  130. </el-card>
  131. <!-- 视频通话 -->
  132. <el-dialog
  133. :close-on-click-modal="false"
  134. :close-on-press-escape="false"
  135. :before-close="handleClose"
  136. :modal="false"
  137. v-dialogDrag
  138. :fullscreen="zoomShow"
  139. :title="videoTle"
  140. :visible.sync="videoVisible"
  141. width="600px"
  142. >
  143. <!-- <div class="video_box"> -->
  144. <div :class="zoomShow == false ? 'video_box' : 'video_box-big'">
  145. <!-- 最大化、最小化 -->
  146. <div
  147. style="
  148. position: absolute;
  149. right: 42px;
  150. color: #9fa2a7;
  151. top: -28px;
  152. font-size: 13px;
  153. "
  154. >
  155. <!-- 最小化 -->
  156. <span
  157. style="font-size: 25px; cursor: pointer"
  158. class="iconfont icon-zuixiaohua"
  159. @click="zoomOperate('small')"
  160. ></span>
  161. <!-- 最大化 -->
  162. <span
  163. style="font-size: 25px; cursor: pointer"
  164. class="iconfont icon-zuidahua"
  165. @click="zoomOperate('big')"
  166. ></span>
  167. </div>
  168. <!-- 最大化、最小化 -->
  169. <div class="host_video" v-if="videoShow">
  170. <img
  171. src="../../assets/images/realTime/8.png"
  172. alt=""
  173. class="video_img"
  174. />
  175. <!-- <img
  176. v-if="audioShow"
  177. src="../../assets/images/realTime/8.png"
  178. alt=""
  179. class=""
  180. style="margin: 120px atuo;"
  181. />
  182. <img
  183. v-else
  184. src="../../assets/images/realTime/13.png"
  185. alt=""
  186. class=""
  187. /> -->
  188. </div>
  189. <div class="host_video" v-else>
  190. <!-- <video
  191. style="width: 100%; height: 100%"
  192. ref="localVideo"
  193. autoplay
  194. playsinline
  195. :muted="true"
  196. ></video> -->
  197. <!-- <video
  198. style="width: 100%; height: 100%"
  199. :ref="idName1"
  200. autoplay
  201. playsinline
  202. :muted="true"
  203. ></video> -->
  204. <video
  205. style="width: 100%; height: 100%"
  206. :ref="idName"
  207. autoplay
  208. playsinline
  209. :muted="false"
  210. ></video>
  211. </div>
  212. <!-- 副视频画面 -->
  213. <div v-if="audioShow" @click="switchWindow" class="deputy_video" v-drag>
  214. <!-- <video
  215. style="width: 100%; height: 100%"
  216. ref="remoteVideo"
  217. autoplay
  218. playsinline
  219. :muted="true"
  220. ></video> -->
  221. <!-- <video
  222. style="width: 100%; height: 100%"
  223. :ref="idName"
  224. autoplay
  225. playsinline
  226. :muted="true"
  227. ></video> -->
  228. <video
  229. style="width: 100%; height: 100%"
  230. :ref="idName1"
  231. autoplay
  232. playsinline
  233. :muted="false"
  234. ></video>
  235. </div>
  236. <!-- 操作 -->
  237. <div class="operate">
  238. <!-- <div style="margin: 0 10px 0 0">
  239. <img
  240. v-if="microphone"
  241. src="../../assets/images/realTime/10.png"
  242. alt=""
  243. />
  244. <img v-else src="../../assets/images/realTime/9.png" alt="" />
  245. </div> -->
  246. <!-- <div v-if="audioShow" style="margin: 0 10px 0 0">
  247. <img
  248. v-if="camera"
  249. @click="notPlugFlow"
  250. src="../../assets/images/realTime/12.png"
  251. alt=""
  252. />
  253. <img
  254. @click="openVideo"
  255. v-else
  256. src="../../assets/images/realTime/11.png"
  257. alt=""
  258. />
  259. </div> -->
  260. <el-button
  261. type="danger"
  262. style="padding: 2px 10px; margin: -2px 10px 0 0"
  263. @click="notLogin"
  264. size="mini"
  265. >挂断</el-button
  266. >
  267. </div>
  268. </div>
  269. </el-dialog>
  270. <!-- 文本聊天框 -->
  271. <el-dialog
  272. custom-class="msgFrame"
  273. :title="tltData"
  274. v-dialogDrag
  275. :close-on-click-modal="false"
  276. :modal="false"
  277. :close-on-press-escape="false"
  278. :visible.sync="dialogVisible"
  279. width="500px"
  280. >
  281. <!-- 聊天内容 -->
  282. <ul class="ul_list test-5" id="msgBox">
  283. <!-- 查看更多 -->
  284. <!-- <li class="">
  285. <div
  286. v-if="iconShow"
  287. style="color: #0400ff; cursor: pointer; text-align: center"
  288. @click="moreMsg"
  289. >
  290. 查看更多消息
  291. </div>
  292. </li> -->
  293. <li class="list" v-for="(item, index) in msgList.msg_list" :key="index">
  294. <!-- me -->
  295. <div class="list_msgBox1" v-if="item.is_right == true">
  296. <!-- <div class="el-icon-loading"></div> -->
  297. <div
  298. style="
  299. background: #60fba5;
  300. font-weight: 550;
  301. color: #000;
  302. padding: 12px 15px 0 15px;
  303. line-height: 25px;
  304. border-radius: 8px;
  305. "
  306. >
  307. {{ item.msg_info }}
  308. </div>
  309. <div
  310. style="
  311. width: 0;
  312. height: 0;
  313. border-top: 6px solid transparent;
  314. border-left: 10px solid #60fba5;
  315. border-bottom: 5px solid transparent;
  316. margin: 23px 0 0 0;
  317. "
  318. ></div>
  319. <div
  320. style="
  321. width: 45px;
  322. height: 45px;
  323. border-radius: 10px;
  324. line-height: 45px;
  325. text-align: center;
  326. font-size: 16px;
  327. background-image: linear-gradient(#a7fbc6, #60fba5);
  328. color: #fff;
  329. "
  330. >
  331. </div>
  332. </div>
  333. <!-- you -->
  334. <div v-else-if="item.is_right == false" class="list_msgBox2">
  335. <div
  336. style="
  337. width: 45px;
  338. height: 45px;
  339. border-radius: 10px;
  340. line-height: 45px;
  341. text-align: center;
  342. font-size: 16px;
  343. background-image: linear-gradient(#addbff, #5cacff);
  344. color: #fff;
  345. "
  346. >
  347. {{ getUserObj.real_name.slice(0, 1) }}
  348. </div>
  349. <div
  350. style="
  351. width: 0;
  352. height: 0;
  353. border-top: 5px solid transparent;
  354. border-right: 10px solid #eae9eb;
  355. border-bottom: 6px solid transparent;
  356. margin: 24px 0 0 0;
  357. "
  358. ></div>
  359. <div
  360. style="
  361. background: #eae9eb;
  362. padding: 12px 15px 0 15px;
  363. line-height: 25px;
  364. border-radius: 8px;
  365. font-weight: 550;
  366. color: #000;
  367. "
  368. >
  369. {{ item.msg_info }}
  370. </div>
  371. </div>
  372. </li>
  373. </ul>
  374. <!-- 发送框 -->
  375. <el-input
  376. style="border: 0"
  377. resize="none"
  378. type="textarea"
  379. v-model="input"
  380. @keyup.enter.native="submit()"
  381. @keydown.native="pushKeyword($event)"
  382. maxlength="30"
  383. show-word-limit
  384. placeholder="按下enter按键发送消息"
  385. ></el-input>
  386. </el-dialog>
  387. </div>
  388. </template>
  389. <script>
  390. import { mapState, mapMutations } from 'vuex';
  391. import { color } from 'highcharts';
  392. import { ZegoExpressEngine } from 'zego-express-engine-webrtc';
  393. export default {
  394. //import引入的组件需要注入到对象中才能使用
  395. components: {},
  396. data() {
  397. //这里存放数据
  398. return {
  399. fullHeight: document.documentElement.clientHeight - 116, //
  400. // 搜索
  401. nameVal: '',
  402. phoneVal: '',
  403. // 表格
  404. tableData: [],
  405. websockMsgList: [], // websocket传递来的数组数据
  406. loading: false, // 加载
  407. org_id: '', //组织id
  408. // 树形图
  409. data: [],
  410. loading2: false,
  411. defaultProps: {
  412. children: 'childrens',
  413. label: 'org_name'
  414. },
  415. // 分页
  416. pageSum: 0,
  417. page: 1,
  418. // 视频通话弹框
  419. idName: 'remoteVideo', // ref 副视频
  420. idName1: 'localVideo', // ref 主视频
  421. videoVisible: false,
  422. videoShow: true, //主视频是否展示画面
  423. audioShow: true, // 判断当前是语音通话还是视频通话 true为视频 false为语音
  424. microphone: true, // 是否打开麦克风
  425. camera: true, // 是否打开摄像头
  426. // appID: 2672645646, //项目唯一标识 AppID
  427. appID: 3215939266, //项目唯一标识 AppID
  428. // server: "1e8344b2a193220e5e96338ba53c3dcc", // 接入服务器地址Server
  429. // videoUrl: "wss://webliveroom2672645646-api.imzego.com/ws", // 请求 - 测试
  430. videoUrl: 'wss://webliveroom3215939266-api.imzego.com/ws', // 请求 - 正式
  431. zg: null,
  432. // UserID: "sample" + Math.floor(Math.random() * 10000000000000).toString(),
  433. UserID: 'user00002',
  434. // UserID: "168",
  435. // StreamID: "web-4796754531236",
  436. StreamID: 'web-' + Math.floor(Math.random() * 10000000000000).toString(),
  437. localStream: null,
  438. Token: '',
  439. RoomID: '',
  440. videoTle: '正在和云飞-卢万里视频通话',
  441. timer: null, // 接听
  442. // 文本聊天框
  443. tltData: '',
  444. dialogVisible: false,
  445. input: '', //发送框
  446. iconShow: true,
  447. // 文本消息功能
  448. url:
  449. // "ws://192.168.1.17:12345/api/api_gateway?method=control_center.real_time.im_message",
  450. // "ws" +
  451. // "https" +
  452. this.$wsUrl +
  453. '/api/api_gateway?method=control_center.real_time.im_message',
  454. websock: null,
  455. getUserObj: {}, // 获取到当前点击的行数据
  456. msgList: [], //当前点击的账号消息列表
  457. userName: '',
  458. // 心跳、重连机制
  459. ws: null, //建立的连接
  460. lockReconnect: false, //是否真正建立连接
  461. timeout: 5 * 1000, //30秒一次心跳
  462. timeoutObj: null, //心跳心跳倒计时
  463. serverTimeoutObj: null, //心跳倒计时
  464. timeoutnum: null, //断开 重连倒计时
  465. // 视频通话窗口缩放
  466. zoomShow: false, // 默认最小化
  467. // websocket断开加载框
  468. fullscreenLoading: null,
  469. // 消息发送加载
  470. msgSetTime: null,
  471. socketTimeoutLogs: []
  472. };
  473. },
  474. //监听属性 类似于data概念
  475. computed: {
  476. ...mapState({
  477. isVideoCallRefused: state => state.isVideoCallRefused,
  478. isVideoTalking: state => state.isVideoTalking
  479. })
  480. },
  481. //监控data中的数据变化
  482. watch: {
  483. fullHeight(val) {
  484. //监控浏览器高度变化
  485. if (!this.timer) {
  486. this.fullHeight = val;
  487. this.timer = true;
  488. let that = this;
  489. setTimeout(function() {
  490. //防止过度调用监测事件,导致卡顿
  491. that.timer = false;
  492. }, 400);
  493. }
  494. },
  495. // 聊天列表
  496. msgList(val) {
  497. // 实现打开聊天框后滚动条定位到最下方
  498. this.$nextTick(() => {
  499. var div = document.getElementById('msgBox');
  500. div.scrollTop = div.scrollHeight;
  501. });
  502. },
  503. // 聊天框
  504. dialogVisible(val) {
  505. if (val == false) {
  506. this.msgList = [];
  507. }
  508. },
  509. // 音视频弹框
  510. videoVisible(val) {
  511. if (val == false) {
  512. this.notPlugFlow(); // 停止推流
  513. this.notTensile(); // 停止拉流
  514. this.notLogin(); //退出房间
  515. }
  516. },
  517. // 给表格文本消息增加提示,数据从webscoket中获取
  518. tableData(val) {
  519. var that = this;
  520. for (var i = 0; i < val.length; i++) {
  521. let sdhkj = val[i];
  522. for (var k = 0; k < this.websockMsgList.length; k++) {
  523. let sdjaks = this.websockMsgList[k];
  524. if (
  525. sdhkj.real_name == sdjaks.real_name &&
  526. sdjaks.unread !== 0 &&
  527. sdjaks.unread
  528. ) {
  529. this.tableData[i]['unread'] = sdjaks.unread;
  530. }
  531. }
  532. }
  533. },
  534. deep: true,
  535. // websocket列表变化
  536. websockMsgList(val) {
  537. console.log(
  538. '-----------------有变化了啊---------------------------',
  539. val
  540. );
  541. // var that = this
  542. // for (var i = 0; i < this.tableData.length; i++) {
  543. // let sdhkj = this.tableData[i];
  544. // for (var k = 0; k < val.length; k++) {
  545. // let sdjaks = val[k];
  546. // if (sdhkj.real_name == sdjaks.real_name && sdjaks.unread !== 0 && sdjaks.unread) {
  547. // this.tableData[i]['unread'] = sdjaks.unread
  548. // }
  549. // }
  550. // }
  551. this.userListData(); // 获取右侧用户列表
  552. },
  553. isVideoCallRefused(val) {
  554. console.warn(val, 'isVideoCallRefused video');
  555. if (val) {
  556. this.$message({
  557. message: '对方正忙,请稍后再试!',
  558. type: 'warning',
  559. duration: 1500
  560. });
  561. this.notLogin();
  562. }
  563. }
  564. },
  565. //方法集合
  566. methods: {
  567. ...mapMutations([
  568. 'updateVideoSelfRoomInfo',
  569. 'updateIsRefusedCall',
  570. 'updateVideoInfo',
  571. 'updateTalkingStatus'
  572. ]),
  573. //动态获取浏览器高度
  574. get_boderHeight() {
  575. const that = this;
  576. window.onresize = () => {
  577. return (() => {
  578. window.fullHeight = document.documentElement.clientHeight;
  579. that.fullHeight = window.fullHeight;
  580. })();
  581. };
  582. },
  583. // 树形图
  584. handleNodeClick(data) {
  585. console.log(data);
  586. this.org_id = data.id;
  587. this.userListData();
  588. },
  589. // 搜索
  590. searchData() {
  591. this.page = 1;
  592. this.userListData();
  593. },
  594. // 分页
  595. changeList(page) {
  596. this.page = page;
  597. this.loading = true;
  598. this.tableData = [];
  599. this.userListData();
  600. },
  601. // 获取左侧组织列表
  602. organizationData() {
  603. this.$axios({
  604. method: 'POST',
  605. url: '/api/api_gateway?method=sysmenage.usermanager.org_list',
  606. data: this.qs.stringify({
  607. page: this.page,
  608. page_item: '100000000',
  609. org_name: ''
  610. })
  611. })
  612. .then(res => {
  613. if (res.data.data.page_list.length !== 0) {
  614. var obj = {
  615. org_name: '全部',
  616. id: ''
  617. };
  618. var data = res.data.data.page_list;
  619. this.data = [obj, ...data]; // 左侧组织列表
  620. }
  621. this.loading2 = false;
  622. })
  623. .catch(err => {
  624. this.loading2 = false;
  625. });
  626. },
  627. // 获取右侧用户列表
  628. userListData() {
  629. this.$axios({
  630. method: 'POST',
  631. url: '/api/api_gateway?method=sysmenage.usermanager.user_list',
  632. data: this.qs.stringify({
  633. page: this.page,
  634. page_item: '20',
  635. real_name: this.nameVal, //用户名称
  636. mobile: this.phoneVal, // 电话
  637. org_id: this.org_id,
  638. is_mails: 1
  639. })
  640. })
  641. .then(res => {
  642. if (res.data.data.total_item !== 0) {
  643. var data = res.data.data.page_list;
  644. var list = [];
  645. data.forEach((item, index) => {
  646. item.ind = index + 1;
  647. list.push(item);
  648. });
  649. this.tableData = list;
  650. this.pageSum = res.data.data.total_item;
  651. this.loading = false;
  652. }
  653. })
  654. .catch(err => {
  655. this.loading = false;
  656. });
  657. },
  658. // 文本消息
  659. msgAxios(data) {
  660. this.getUserObj = data;
  661. this.redtf = data;
  662. this.userName = localStorage.getItem('usernme');
  663. console.log(this.userName);
  664. this.tltData = '与' + data.real_name + '的对话';
  665. var obj = {};
  666. obj = {
  667. action: 'list',
  668. recv_user_id: data.user_id,
  669. data: {
  670. msg_status: false,
  671. msg_info: ''
  672. }
  673. };
  674. this.websock.send(JSON.stringify(obj)); // 获取聊天记录
  675. // var readObj = {};
  676. // readObj = {
  677. // action: "read",
  678. // send_user_id: data.user_id,
  679. // data: {
  680. // msg_status: false,
  681. // msg_info: "",
  682. // },
  683. // };
  684. // this.websock.send(JSON.stringify(readObj)); // 消息已读
  685. this.dialogVisible = true;
  686. },
  687. // 视频消息
  688. videoAxios(data) {
  689. this.getUserObj = data;
  690. this.userName = localStorage.getItem('username');
  691. this.videoTle = '正在和' + data.real_name + '视频通话';
  692. this.UserID = localStorage.getItem('userID');
  693. // 先获取当前用户的房间号和登录所需的Token
  694. var obj = {};
  695. obj = {
  696. action: 'send_video',
  697. recv_user_id: data.user_id,
  698. data: {
  699. msg_status: false,
  700. msg_info: ''
  701. }
  702. };
  703. // console.log('gdahjdgjasgd', data.user_id)
  704. this.websock.send(JSON.stringify(obj));
  705. // // 登录房间
  706. // this.zg.loginRoom(
  707. // this.RoomID,
  708. // this.Token,
  709. // { userID: this.UserID, userName: this.UserID },
  710. // { userUpdate: true }
  711. // ).then((result) => {
  712. // if (result == true) {
  713. // this.plugFlow(); //推流
  714. // this.videoVisible = true
  715. // }
  716. // });
  717. },
  718. // 打开视频画面
  719. openVideo() {
  720. this.plugFlow();
  721. },
  722. // 音视频推流
  723. async plugFlow() {
  724. this.videoShow = false;
  725. this.camera = true;
  726. const localStream = await this.zg.createStream();
  727. // stream 为 MediaStream 对象,开发者可通过赋值给 video 或 audio 的 srcObject 属性进行渲染
  728. this.$refs['localVideo'].srcObject = localStream;
  729. this.localStream = localStream;
  730. // localStream 为创建流获取的 MediaStream 对象
  731. this.zg.startPublishingStream(this.StreamID, localStream);
  732. },
  733. // 音视频停止推流
  734. notPlugFlow() {
  735. this.videoShow = true;
  736. this.camera = false;
  737. this.zg.stopPublishingStream(this.StreamID);
  738. this.zg.destroyStream(this.localStream);
  739. },
  740. // 拉流
  741. async tensile(streamID) {
  742. var playOption = { videoDecodeType: 'H264', playType: 'video' };
  743. const remoteStream = await this.zg.startPlayingStream(
  744. streamID,
  745. playOption
  746. );
  747. // remoteVideo 为本地 <video> 或 <audio> 对象
  748. this.$refs['remoteVideo'].srcObject = remoteStream;
  749. },
  750. notTensile(streamID) {
  751. this.zg.stopPlayingStream(streamID);
  752. },
  753. // 切换视频窗口
  754. switchWindow() {
  755. // 副视频画面 remoteVideo
  756. // 主视频画面 localVideo
  757. this.idName = this.idName == 'remoteVideo' ? 'localVideo' : 'remoteVideo';
  758. this.idName =
  759. this.idName1 == 'remoteVideo' ? 'localVideo' : 'remoteVideo';
  760. },
  761. // 退出房间
  762. notLogin() {
  763. this.updateTalkingStatus(false);
  764. this.updateIsRefusedCall(false);
  765. clearTimeout(this.timer);
  766. this.zg.logoutRoom(this.RoomID);
  767. this.videoVisible = false;
  768. },
  769. // 当音视频通话关闭时的回调
  770. handleClose(done) {
  771. var that = this;
  772. this.$confirm('目前正在音视频通话中,确认关闭?')
  773. .then(_ => {
  774. done();
  775. that.notLogin(); // 退出房间
  776. })
  777. .catch(_ => {});
  778. },
  779. // 发送消息
  780. async submit() {
  781. if (this.input.split(' ').join('').length == 0) {
  782. if (document.getElementsByClassName('el-message').length == 0) {
  783. this.$message({
  784. message: '不能发送空白消息!',
  785. type: 'warning',
  786. duration: 1500
  787. });
  788. }
  789. this.input = '';
  790. } else {
  791. try {
  792. var obj = {};
  793. obj = {
  794. action: 'send',
  795. recv_user_id: this.getUserObj.user_id,
  796. data: {
  797. msg_status: false,
  798. msg_info: this.input
  799. }
  800. };
  801. var v = JSON.stringify(obj);
  802. this.websock.send(v);
  803. this.input = '';
  804. this.msgSetTime = setTimeout(() => {
  805. // 设定十秒内没有接收到服务器返回的list数据就表明消息没有发送成
  806. if (document.getElementsByClassName('el-message').length == 0) {
  807. this.$message({
  808. message: '消息发送失败,请刷新界面重试!',
  809. type: 'warning',
  810. duration: 1500
  811. });
  812. }
  813. }, 10000);
  814. } catch (error) {
  815. console.log('>>> sendMsg, error: ', error);
  816. }
  817. }
  818. },
  819. // 取消回车换行行为
  820. pushKeyword(event) {
  821. if (event.keyCode === 13) {
  822. event.preventDefault(); // 阻止浏览器默认换行操作
  823. this.keyword = '';
  824. return false;
  825. }
  826. },
  827. // 更多消息
  828. moreMsg() {},
  829. // 文本消息功能初始化
  830. async msgInit() {
  831. var that = this;
  832. if (typeof WebSocket === 'undefined') {
  833. alert('您的浏览器不支持socket!');
  834. } else {
  835. console.log(this.url, 'msg int 00000000000000000 12313441');
  836. this.websock = new window.WebSocket(
  837. this.url + '&token=' + localStorage.getItem('session')
  838. );
  839. this.websock.onopen = event => {
  840. console.log('WebSocket:已连接');
  841. console.log(event);
  842. // 发送消息 - 获取对话列表
  843. var obj1 = {};
  844. obj1 = {
  845. action: 'list',
  846. recv_user_id: '',
  847. data: {
  848. msg_status: false,
  849. msg_info: ''
  850. }
  851. };
  852. this.start(); //开启心跳
  853. if (this.fullscreenLoading !== null) {
  854. this.websock.send(JSON.stringify(obj1));
  855. }
  856. // this.fullscreenLoading.close(); // 加载关闭
  857. };
  858. this.websock.onmessage = event => {
  859. var data = JSON.parse(event.data);
  860. console.log('WebSocket:消息---------------------------', data);
  861. if (data.action == 'none') {
  862. // 获取聊天记录
  863. var datArr = data.data; // 总数据
  864. this.websockMsgList = datArr; // 把数据定义在data中
  865. if (datArr.length !== 0) {
  866. for (var i = 0; i < datArr.length; i++) {
  867. if (this.getUserObj.user_id == datArr[i].user_id) {
  868. data.data[i].msg_list = data.data[i].msg_list.reverse();
  869. that.msgList = data.data[i];
  870. console.log(data.data[i]);
  871. }
  872. }
  873. }
  874. // if (data.data[0] !== undefined) {
  875. // data.data[0].msg_list = data.data[0].msg_list.reverse();
  876. // this.msgList = data.data[0];
  877. // }
  878. } else if (data.action == 'list') {
  879. console.log('消息发送成功了----------------------------------');
  880. clearTimeout(this.msgSetTime); // 清除发送失败定时器
  881. // 返回list为发送消息成功后需要再次调用聊天列表
  882. var obj = {};
  883. obj = {
  884. action: 'list',
  885. // recv_user_id: this.getUserObj.user_id,
  886. recv_user_id: '',
  887. data: {
  888. msg_status: false,
  889. msg_info: ''
  890. }
  891. };
  892. this.websock.send(JSON.stringify(obj));
  893. } else if (data.action == 'recv_video') {
  894. console.warn('recv_video', data.data, this.isVideoCallRefused);
  895. if (this.isVideoCallRefused) {
  896. this.updateIsRefusedCall(false);
  897. return;
  898. }
  899. this.updateIsRefusedCall(false);
  900. if (this.isVideoTalking) {
  901. // 占线中
  902. var obj = {
  903. action: 'occupy', // 动作标识,必填
  904. recv_user_id: data.data.user_id, // 接收人用户id, 非必填
  905. data: {}
  906. };
  907. this.websock.send({
  908. data: JSON.stringify(obj),
  909. async success(res) {
  910. console.warn('占线消息发送成功1111', data.data);
  911. }
  912. });
  913. return;
  914. }
  915. // 获取当前点击用户的房间号以及登录房间所需的Token
  916. // console.log(JSON.parse(event.data));
  917. console.log(data);
  918. var data = JSON.parse(event.data);
  919. this.RoomID = data.data.room_id; // 房间号
  920. this.Token = data.data.room_token; // Token
  921. //TODO 提示弹窗,确认后登录房间
  922. this.$confirm(`${data.data.real_name}邀请你进行视频通话...`)
  923. .then(_ => {
  924. this.videoTle = '正在和' + data.data.real_name + '视频通话';
  925. this.loginRoom();
  926. })
  927. .catch(_ => {
  928. console.log('------------------------ confirm catch', _);
  929. const socketData = {
  930. action: 'reject_video', // 动作标识,必填
  931. recv_user_id: data.data.user_id, // 接收人用户id, 非必填
  932. data: {}
  933. };
  934. console.warn('send reject_video', socketData);
  935. this.websock.send({
  936. data: JSON.stringify(socketData),
  937. async success(res) {
  938. console.log('reject_video 消息发送成功 reject_video');
  939. }
  940. });
  941. });
  942. //
  943. } else if (data.action == 'send_video_rsp') {
  944. console.warn('recv_video---------------- response', data.data);
  945. if (this.isVideoCallRefused) {
  946. this.updateIsRefusedCall(false);
  947. return;
  948. }
  949. this.updateIsRefusedCall(false);
  950. this.updateVideoSelfRoomInfo(data.data);
  951. this.RoomID = data.data.room_id; // 房间号
  952. this.Token = data.data.room_token; // Token
  953. console.warn(
  954. 'into romm ready ------------------',
  955. data.data.room_token
  956. );
  957. console.warn('into romm ------------------');
  958. this.updateTalkingStatus(true);
  959. // 唤起视频弹窗
  960. this.loginRoom();
  961. } else if (data.action == 'reject_video_rsp') {
  962. // 被拒绝后挂断视频通话
  963. this.updateIsRefusedCall(true);
  964. } else if (data.action == 'occupy_rsp') {
  965. console.warn('----------------------- is refused');
  966. // 占线中,挂断发起通话
  967. this.updateIsRefusedCall(true);
  968. } else if (data.action == 'ok') {
  969. // 心跳机制 - 当前状态是websocket连接没有问题
  970. this.start(); // 心跳机制
  971. }
  972. };
  973. this.websock.onerror = event => {
  974. console.log('WebSocket:发生错误 ', Date.now());
  975. this.socketTimeoutLogs.push(Date.now());
  976. console.log(event);
  977. if (that.timeoutnum !== null) {
  978. setTimeout(() => {
  979. that.websock.close(); // 先关闭
  980. clearTimeout(that.timeoutObj); //清除时间
  981. that.reconnect(); // 重连
  982. }, 5000);
  983. }
  984. };
  985. this.websock.onclose = event => {
  986. console.log('WebSocket:已关闭');
  987. console.log(event);
  988. console.log(that.timeoutnum);
  989. if (that.timeoutnum !== null) {
  990. // this.openFullScreen2();
  991. setTimeout(() => {
  992. // that.websock.close(); // 先关闭
  993. clearTimeout(that.timeoutObj); //清除时间
  994. // that.reconnect(); // 重连
  995. }, 5000);
  996. }
  997. };
  998. }
  999. },
  1000. loginRoom() {
  1001. const userID = localStorage.getItem('userID');
  1002. console.warn(`userID:${userID};userName:${this.userName}`);
  1003. // 登录房间
  1004. this.zg
  1005. .loginRoom(
  1006. this.RoomID,
  1007. this.Token,
  1008. { userID: userID, userName: this.userName },
  1009. { userUpdate: true }
  1010. )
  1011. .then(result => {
  1012. console.log(result);
  1013. if (result == true) {
  1014. console.log('0000000000000000000000000000000000000登录成功');
  1015. this.plugFlow(); //推流
  1016. this.videoVisible = true;
  1017. }
  1018. });
  1019. },
  1020. // 音视频消息功能初始化
  1021. videoInit() {
  1022. this.zg = new ZegoExpressEngine(this.appID, this.videoUrl);
  1023. this.soundOn(); // 监听房间
  1024. this.detection(); // 检测是否兼容当前浏览器
  1025. },
  1026. // 监听房间
  1027. soundOn() {
  1028. // 房间状态更新回调
  1029. this.zg.on(
  1030. 'roomStateUpdate',
  1031. (roomID, state, errorCode, extendedData) => {
  1032. if (state == 'CONNECTED') {
  1033. // 与房间连接成功,只有当房间状态是连接成功时,才能进行推流、拉流等操作。
  1034. // 接下来的“预览并推流”的代码写在这里
  1035. console.log('房间连接成功');
  1036. //定时器
  1037. this.timer = setTimeout(() => {
  1038. if (document.getElementsByClassName('el-message').length == 0) {
  1039. this.$message({
  1040. message: '没人接听,请稍后重试',
  1041. type: 'warning',
  1042. duration: 2500
  1043. });
  1044. }
  1045. this.videoVisible = false;
  1046. // }, 5000);
  1047. }, 30000);
  1048. }
  1049. if (state == 'DISCONNECTED') {
  1050. // 与房间断开了连接
  1051. console.log('与房间断开连接');
  1052. }
  1053. if (state == 'CONNECTING') {
  1054. // 与房间尝试连接中
  1055. console.log('与房间尝试连接中');
  1056. }
  1057. }
  1058. );
  1059. this.zg.on('roomUserUpdate', (roomID, updateType, userList) => {
  1060. // 其他用户进出房间的通知
  1061. console.log(updateType);
  1062. console.log(userList);
  1063. console.log('有其他用户进出房间');
  1064. if (updateType == 'DELETE') {
  1065. if (document.getElementsByClassName('el-message').length == 0) {
  1066. this.$message({
  1067. message: '对方已挂断,结束通话!',
  1068. type: 'warning',
  1069. duration: 1500
  1070. });
  1071. }
  1072. setTimeout(() => {
  1073. this.notLogin(); // 退出房间
  1074. }, 2000);
  1075. } else if (updateType == 'ADD') {
  1076. clearTimeout(this.timer); // 关闭无人接听回调
  1077. }
  1078. });
  1079. this.zg.on(
  1080. 'roomStreamUpdate',
  1081. async (roomID, updateType, streamList, extendedData) => {
  1082. console.log(roomID);
  1083. console.log(updateType);
  1084. console.log(streamList);
  1085. console.log(extendedData);
  1086. // 房间内其他用户音视频流变化的通知
  1087. console.log('有其他用户开启或关闭音频');
  1088. if (updateType == 'ADD') {
  1089. // 流新增,开始拉流
  1090. console.log('这是拉流的ID', streamList);
  1091. this.tensile(streamList[0].streamID);
  1092. } else if (updateType == 'DELETE') {
  1093. // 流删除,停止拉流
  1094. this.notTensile(streamList[0].streamID);
  1095. }
  1096. }
  1097. );
  1098. this.zg.on('publisherStateUpdate', result => {
  1099. // 推流状态更新回调
  1100. console.log('推流状态更新');
  1101. console.log(result);
  1102. });
  1103. this.zg.on('publishQualityUpdate', (streamID, stats) => {
  1104. // 推流质量回调
  1105. console.log('推流质量更新');
  1106. console.log(streamID, stats);
  1107. });
  1108. this.zg.on('playerStateUpdate', result => {
  1109. // 拉流状态回调
  1110. console.log('拉流状态的回调');
  1111. console.log(result);
  1112. });
  1113. this.zg.on('playQualityUpdate', (streamID, stats) => {
  1114. // 拉流成功后回调
  1115. console.log('拉流质量回调');
  1116. console.log(streamID);
  1117. console.log(stats);
  1118. });
  1119. },
  1120. // 检测是否兼容当前浏览器
  1121. async detection() {
  1122. const result = await this.zg.checkSystemRequirements();
  1123. // 返回的 result 为兼容性检测结果。 webRTC 为 true 时表示支持 webRTC,其他属性含义可以参考接口 API 文档
  1124. console.log(result);
  1125. if (result.webRTC == true) {
  1126. console.log('兼容');
  1127. } else {
  1128. console.log('不兼容');
  1129. }
  1130. },
  1131. // 心跳机制
  1132. start() {
  1133. //开启心跳
  1134. var self = this;
  1135. self.timeoutObj && clearTimeout(self.timeoutObj);
  1136. self.serverTimeoutObj && clearTimeout(self.serverTimeoutObj);
  1137. self.timeoutObj = setTimeout(function() {
  1138. //这里发送一个心跳,后端收到后,返回一个心跳消息,
  1139. console.log('心跳中。。。', self.websock.readyState);
  1140. if (self.websock.readyState == 1) {
  1141. //如果连接正常
  1142. var obj1 = {};
  1143. obj1 = {
  1144. action: 'keepalive',
  1145. type: 'PC',
  1146. recv_user_id: '',
  1147. data: {
  1148. msg_status: false,
  1149. msg_info: ''
  1150. }
  1151. };
  1152. self.websock.send(JSON.stringify(obj1));
  1153. } else {
  1154. //否则重连
  1155. self.reconnect();
  1156. }
  1157. self.serverTimeoutObj = setTimeout(function() {
  1158. //超时关闭
  1159. console.log('又进入了?');
  1160. self.websock.close();
  1161. self.reconnect(); // 重连
  1162. }, self.timeout);
  1163. }, self.timeout);
  1164. },
  1165. reset() {
  1166. //重置心跳
  1167. var that = this;
  1168. clearTimeout(that.timeoutObj); //清除时间
  1169. clearTimeout(that.serverTimeoutObj); //清除时间
  1170. that.start(); //重启心跳
  1171. },
  1172. // 重连机制
  1173. reconnect() {
  1174. var that = this;
  1175. console.log('进入重连了');
  1176. clearTimeout(that.timeoutObj); //清除时间
  1177. if (that.lockReconnect) {
  1178. return;
  1179. }
  1180. that.lockReconnect = true;
  1181. // 没连接上会一直重连,设置延迟避免请求过多
  1182. that.timeoutnum && clearTimeout(that.timeoutnum);
  1183. that.timeoutnum = setTimeout(function() {
  1184. that.msgInit(); //新连接
  1185. that.lockReconnect = false;
  1186. }, 5000);
  1187. },
  1188. // 视频通话窗口最小化、最大化
  1189. zoomOperate(ref) {
  1190. if (ref == 'small') {
  1191. // 最小化
  1192. this.zoomShow = false;
  1193. } else if (ref == 'big') {
  1194. // 最大化
  1195. this.zoomShow = true;
  1196. }
  1197. },
  1198. // websocket断开加载框
  1199. openFullScreen2() {
  1200. // this.fullscreenLoading = this.$loading({
  1201. // lock: true,
  1202. // text: "通信异常,连接中...",
  1203. // spinner: "el-icon-loading",
  1204. // background: "rgba(0, 0, 0, 0.7)",
  1205. // });
  1206. }
  1207. },
  1208. //生命周期 - 创建完成(可以访问当前this实例)
  1209. created() {
  1210. this.loading = true;
  1211. this.loading2 = true;
  1212. this.organizationData(); //获取左侧组织列表
  1213. this.userListData(); // 获取右侧用户列表
  1214. },
  1215. //生命周期 - 挂载完成(可以访问DOM元素)
  1216. mounted() {
  1217. // this.loading = true;
  1218. // this.loading2 = true;
  1219. // this.organizationData(); //获取左侧组织列表
  1220. // this.userListData(); // 获取右侧用户列表
  1221. // console.log("---------------------------");
  1222. // console.log(this.websock);
  1223. // console.log("---------------------------");
  1224. if (this.websock == null) {
  1225. this.msgInit(); // 文本消息功能初始化
  1226. }
  1227. this.videoInit(); // 视频消息功能初始化
  1228. window.socketTimeoutLogs = this.socketTimeoutLogs;
  1229. },
  1230. beforeCreate() {}, //生命周期 - 创建之前
  1231. beforeMount() {}, //生命周期 - 挂载之前
  1232. beforeUpdate() {}, //生命周期 - 更新之前
  1233. updated() {}, //生命周期 - 更新之后
  1234. beforeDestroy() {
  1235. // var that = this;
  1236. // clearTimeout(that.timeoutnum); // 清除重连
  1237. // clearTimeout(that.timeoutObj); // 清除心跳
  1238. // that.websock.close(); // 关闭websocket
  1239. }, //生命周期 - 销毁之前
  1240. destroyed() {
  1241. console.log('我进入到销毁里喽');
  1242. var that = this;
  1243. clearTimeout(that.timeoutnum); // 清除重连
  1244. clearTimeout(that.timeoutObj); // 清除心跳
  1245. that.websock.close(); // 关闭websocket
  1246. that.websock = null; // 清空
  1247. console.log(that.websock);
  1248. }, //生命周期 - 销毁完成
  1249. activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
  1250. };
  1251. </script>
  1252. <style lang="less" scoped>
  1253. .realTime_box {
  1254. // 搜索
  1255. .search_box {
  1256. display: flex;
  1257. /deep/.el-input {
  1258. width: 20%;
  1259. margin: 0 15px 0 0;
  1260. }
  1261. .btn_box {
  1262. display: flex;
  1263. justify-content: start;
  1264. }
  1265. }
  1266. .card_box {
  1267. display: flex;
  1268. width: 100%;
  1269. // 树形图
  1270. .userManger_left {
  1271. width: 19%;
  1272. margin: 0 15px 0 0;
  1273. padding: 5px;
  1274. border: 1px solid #eeeeee;
  1275. border-radius: 5px;
  1276. overflow: hidden;
  1277. overflow-y: auto;
  1278. }
  1279. // 搜索和表格
  1280. .userManger_right {
  1281. width: 80%;
  1282. // border: 1px solid red;
  1283. a {
  1284. text-decoration: none;
  1285. }
  1286. .reset {
  1287. color: #1890ff;
  1288. }
  1289. .delete {
  1290. color: #f56c6c;
  1291. }
  1292. .line {
  1293. display: inline-block;
  1294. width: 1px;
  1295. background: rgba(0, 0, 0, 0.09);
  1296. margin: 0 11px;
  1297. height: 14px;
  1298. }
  1299. }
  1300. }
  1301. /deep/.el-select {
  1302. width: 80%;
  1303. }
  1304. /deep/.el-cascader {
  1305. width: 80%;
  1306. }
  1307. // 视频通话 - 最小化
  1308. .video_box {
  1309. border: 1px solid #000;
  1310. background: #2c2c2c;
  1311. height: 325px;
  1312. position: relative;
  1313. // 主视频画面
  1314. .host_video {
  1315. height: 326px;
  1316. vertical-align: middle;
  1317. text-align: center;
  1318. position: relative;
  1319. .video_img {
  1320. margin: 120px auto;
  1321. position: absolute;
  1322. top: 0;
  1323. right: 0;
  1324. bottom: 0;
  1325. left: 0;
  1326. margin: auto;
  1327. }
  1328. }
  1329. // 副视频画面
  1330. .deputy_video {
  1331. width: 90px;
  1332. height: 90px;
  1333. position: absolute;
  1334. top: 0;
  1335. right: 0;
  1336. background: #fff;
  1337. }
  1338. // 操作
  1339. .operate {
  1340. height: 25px;
  1341. width: 100%;
  1342. background: #000;
  1343. padding: 5px 0 5px 0;
  1344. margin: -13px 0 0 0px;
  1345. position: absolute;
  1346. bottom: 0;
  1347. left: 0;
  1348. display: flex;
  1349. justify-content: flex-end;
  1350. }
  1351. }
  1352. // 视频通话 - 最大化
  1353. .video_box-big {
  1354. border: 1px solid #000;
  1355. background: #2c2c2c;
  1356. height: 100%;
  1357. position: relative;
  1358. // 主视频画面
  1359. .host_video {
  1360. height: 100%;
  1361. vertical-align: middle;
  1362. text-align: center;
  1363. position: relative;
  1364. .video_img {
  1365. margin: 120px auto;
  1366. position: absolute;
  1367. top: 0;
  1368. right: 0;
  1369. bottom: 0;
  1370. left: 0;
  1371. margin: auto;
  1372. }
  1373. }
  1374. // 副视频画面
  1375. .deputy_video {
  1376. width: 190px;
  1377. height: 190px;
  1378. position: absolute;
  1379. top: 0;
  1380. right: 0;
  1381. background: #fff;
  1382. }
  1383. // 操作
  1384. .operate {
  1385. height: 25px;
  1386. width: 100%;
  1387. background: #000;
  1388. padding: 5px 0 5px 0;
  1389. margin: -13px 0 0 0px;
  1390. position: absolute;
  1391. bottom: 3%;
  1392. left: 0;
  1393. display: flex;
  1394. justify-content: flex-end;
  1395. }
  1396. }
  1397. /* 聊天内容 */
  1398. .ul_list {
  1399. height: 350px;
  1400. // margin-left: -41px;
  1401. overflow: hidden;
  1402. overflow-y: auto;
  1403. }
  1404. .list_msgBox1 {
  1405. display: flex;
  1406. margin-top: 22px;
  1407. justify-content: flex-end;
  1408. }
  1409. .list_msgBox2 {
  1410. display: flex;
  1411. margin-top: 22px;
  1412. }
  1413. .test-5::-webkit-scrollbar {
  1414. /*滚动条整体样式*/
  1415. width: 10px; /*高宽分别对应横竖滚动条的尺寸*/
  1416. height: 1px;
  1417. }
  1418. .test-5::-webkit-scrollbar-thumb {
  1419. /*滚动条里面小方块*/
  1420. border-radius: 10px;
  1421. background-color: skyblue;
  1422. background-image: -webkit-linear-gradient(
  1423. 45deg,
  1424. rgba(255, 255, 255, 0.2) 25%,
  1425. transparent 25%,
  1426. transparent 50%,
  1427. rgba(255, 255, 255, 0.2) 50%,
  1428. rgba(255, 255, 255, 0.2) 75%,
  1429. transparent 75%,
  1430. transparent
  1431. );
  1432. }
  1433. .test-5::-webkit-scrollbar-track {
  1434. /*滚动条里面轨道*/
  1435. box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
  1436. background: #ededed;
  1437. border-radius: 10px;
  1438. }
  1439. .list {
  1440. /* border: 1px solid red; */
  1441. width: 100%;
  1442. height: 100px;
  1443. }
  1444. /* ElementUI 样式 */
  1445. // 聊天框
  1446. /deep/.el-dialog {
  1447. overflow: hidden;
  1448. }
  1449. /deep/.el-dialog__header {
  1450. background: #f2f2f2;
  1451. border-bottom: 1px solid #cacaca;
  1452. }
  1453. /deep/.el-dialog__title {
  1454. font-weight: 550;
  1455. font-size: 15px;
  1456. line-height: 0;
  1457. float: left;
  1458. }
  1459. /deep/.el-dialog__headerbtn {
  1460. top: 10px;
  1461. right: 10px;
  1462. }
  1463. /deep/.el-dialog__headerbtn .el-dialog__close {
  1464. font-size: 20px;
  1465. line-height: 15px;
  1466. }
  1467. /deep/.el-dialog__body {
  1468. padding: 0;
  1469. // margin-top: -15px;
  1470. height: 100%;
  1471. }
  1472. /deep/.el-textarea__inner {
  1473. border: 0;
  1474. border-top: 1px solid #dcdfe6;
  1475. border-radius: 0;
  1476. height: 95px;
  1477. }
  1478. /deep/.el-card {
  1479. overflow: hidden;
  1480. overflow-y: auto;
  1481. }
  1482. }
  1483. /deep/.el-button--info {
  1484. background-color: #409eff;
  1485. border-color: #409eff;
  1486. }
  1487. </style>