| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696 |
- <!-- -->
- <template>
- <div class="realTime_box">
- <!-- 搜索 -->
- <el-row>
- <el-col>
- <!-- 组织搜索 -->
- <div class="search_box">
- <el-input
- size="mini"
- placeholder="请输入用户姓名"
- v-model.trim="nameVal"
- @change="searchData"
- clearable
- >
- </el-input>
- <el-input
- size="mini"
- placeholder="请输入手机号"
- v-model.trim="phoneVal"
- @change="searchData"
- clearable
- >
- </el-input>
- <div class="btn_box">
- <el-button type="info" size="mini" @click="searchData"
- >搜索</el-button
- >
- </div>
- </div>
- </el-col>
- </el-row>
- <el-card style="margin-top: 15px">
- <div class="card_box" style="margin-top: 15px">
- <!-- 组织 -->
- <div
- class="userManger_left"
- :style="'height:' + 26 * 25 + 'px;' + 'overflow-y: auto'"
- >
- <el-tree
- :data="data"
- :props="defaultProps"
- v-loading="loading2"
- @node-click="handleNodeClick"
- ></el-tree>
- </div>
- <!-- 表格 -->
- <div class="userManger_right">
- <el-table
- v-loading="loading"
- :data="tableData"
- stripe
- :height="48 * 13"
- style="width: 100%; overflow-y: auto"
- >
- <el-table-column prop="ind" label="序号" width="180">
- <template slot-scope="scope">
- <span>{{ (page - 1) * 20 + scope.row.ind }}</span>
- </template>
- </el-table-column>
- <el-table-column prop="real_name" label="成员" width="330">
- </el-table-column>
- <el-table-column prop="mobile" label="手机号" width="250">
- <template slot-scope="scope">
- <span>{{ scope.row.mobile || '无' }}</span>
- </template>
- </el-table-column>
- <el-table-column
- :show-overflow-tooltip="true"
- prop="address"
- label="职位"
- width="320"
- >
- <template slot-scope="scope">
- <span v-for="(item, index) in scope.row.org_list" :key="index"
- >{{ item.org_name }}
- <span v-if="index !== scope.row.org_list.length - 1">、</span>
- </span>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="200" fixed="right">
- <template slot-scope="scope">
- <span
- style="
- height: 30px;
- display: inline-block;
- margin: 0 0 0 0;
- position: absolute;
- left: 0;
- top: 11px;
- "
- >
- <el-badge v-if="scope.row.unread" is-dot class="item">
- <img
- @click="msgAxios(scope.row)"
- src="../../assets/images/realTime/xiaoxi.png"
- alt=""
- />
- </el-badge>
- <img
- v-else
- @click="msgAxios(scope.row)"
- src="../../assets/images/realTime/xiaoxi.png"
- alt=""
- />
- </span>
- <span style="position: absolute; top: 12px; left: 60px">
- <img
- @click="videoAxios(scope.row)"
- src="../../assets/images/realTime/shipin.png"
- alt=""
- />
- </span>
- </template>
- </el-table-column>
- </el-table>
- <!-- 分页 -->
- <el-pagination
- v-if="tableData.length !== 0"
- @current-change="changeList"
- background
- layout="prev, pager, next, jumper"
- :current-page="page"
- :total="pageSum"
- :page-size="20"
- >
- </el-pagination>
- </div>
- </div>
- </el-card>
- <!-- 视频通话 -->
- <el-dialog
- :close-on-click-modal="false"
- :close-on-press-escape="false"
- :before-close="handleClose"
- :modal="false"
- v-dialogDrag
- :fullscreen="zoomShow"
- :title="videoTle"
- :visible.sync="videoVisible"
- width="600px"
- >
- <!-- <div class="video_box"> -->
- <div :class="zoomShow == false ? 'video_box' : 'video_box-big'">
- <!-- 最大化、最小化 -->
- <div
- style="
- position: absolute;
- right: 42px;
- color: #9fa2a7;
- top: -28px;
- font-size: 13px;
- "
- >
- <!-- 最小化 -->
- <span
- style="font-size: 25px; cursor: pointer"
- class="iconfont icon-zuixiaohua"
- @click="zoomOperate('small')"
- ></span>
- <!-- 最大化 -->
- <span
- style="font-size: 25px; cursor: pointer"
- class="iconfont icon-zuidahua"
- @click="zoomOperate('big')"
- ></span>
- </div>
- <!-- 最大化、最小化 -->
- <div class="host_video" v-if="videoShow">
- <img
- src="../../assets/images/realTime/8.png"
- alt=""
- class="video_img"
- />
- <!-- <img
- v-if="audioShow"
- src="../../assets/images/realTime/8.png"
- alt=""
- class=""
- style="margin: 120px atuo;"
- />
- <img
- v-else
- src="../../assets/images/realTime/13.png"
- alt=""
- class=""
- /> -->
- </div>
- <div class="host_video" v-else>
- <!-- <video
- style="width: 100%; height: 100%"
- ref="localVideo"
- autoplay
- playsinline
- :muted="true"
- ></video> -->
- <!-- <video
- style="width: 100%; height: 100%"
- :ref="idName1"
- autoplay
- playsinline
- :muted="true"
- ></video> -->
- <video
- style="width: 100%; height: 100%"
- :ref="idName"
- autoplay
- playsinline
- :muted="false"
- ></video>
- </div>
- <!-- 副视频画面 -->
- <div v-if="audioShow" @click="switchWindow" class="deputy_video" v-drag>
- <!-- <video
- style="width: 100%; height: 100%"
- ref="remoteVideo"
- autoplay
- playsinline
- :muted="true"
- ></video> -->
- <!-- <video
- style="width: 100%; height: 100%"
- :ref="idName"
- autoplay
- playsinline
- :muted="true"
- ></video> -->
- <video
- style="width: 100%; height: 100%"
- :ref="idName1"
- autoplay
- playsinline
- :muted="false"
- ></video>
- </div>
- <!-- 操作 -->
- <div class="operate">
- <!-- <div style="margin: 0 10px 0 0">
- <img
- v-if="microphone"
- src="../../assets/images/realTime/10.png"
- alt=""
- />
- <img v-else src="../../assets/images/realTime/9.png" alt="" />
- </div> -->
- <!-- <div v-if="audioShow" style="margin: 0 10px 0 0">
- <img
- v-if="camera"
- @click="notPlugFlow"
- src="../../assets/images/realTime/12.png"
- alt=""
- />
- <img
- @click="openVideo"
- v-else
- src="../../assets/images/realTime/11.png"
- alt=""
- />
- </div> -->
- <el-button
- type="danger"
- style="padding: 2px 10px; margin: -2px 10px 0 0"
- @click="notLogin"
- size="mini"
- >挂断</el-button
- >
- </div>
- </div>
- </el-dialog>
- <!-- 文本聊天框 -->
- <el-dialog
- custom-class="msgFrame"
- :title="tltData"
- v-dialogDrag
- :close-on-click-modal="false"
- :modal="false"
- :close-on-press-escape="false"
- :visible.sync="dialogVisible"
- width="500px"
- >
- <!-- 聊天内容 -->
- <ul class="ul_list test-5" id="msgBox">
- <!-- 查看更多 -->
- <!-- <li class="">
- <div
- v-if="iconShow"
- style="color: #0400ff; cursor: pointer; text-align: center"
- @click="moreMsg"
- >
- 查看更多消息
- </div>
- </li> -->
- <li class="list" v-for="(item, index) in msgList.msg_list" :key="index">
- <!-- me -->
- <div class="list_msgBox1" v-if="item.is_right == true">
- <!-- <div class="el-icon-loading"></div> -->
- <div
- style="
- background: #60fba5;
- font-weight: 550;
- color: #000;
- padding: 12px 15px 0 15px;
- line-height: 25px;
- border-radius: 8px;
- "
- >
- {{ item.msg_info }}
- </div>
- <div
- style="
- width: 0;
- height: 0;
- border-top: 6px solid transparent;
- border-left: 10px solid #60fba5;
- border-bottom: 5px solid transparent;
- margin: 23px 0 0 0;
- "
- ></div>
- <div
- style="
- width: 45px;
- height: 45px;
- border-radius: 10px;
- line-height: 45px;
- text-align: center;
- font-size: 16px;
- background-image: linear-gradient(#a7fbc6, #60fba5);
- color: #fff;
- "
- >
- 我
- </div>
- </div>
- <!-- you -->
- <div v-else-if="item.is_right == false" class="list_msgBox2">
- <div
- style="
- width: 45px;
- height: 45px;
- border-radius: 10px;
- line-height: 45px;
- text-align: center;
- font-size: 16px;
- background-image: linear-gradient(#addbff, #5cacff);
- color: #fff;
- "
- >
- {{ getUserObj.real_name.slice(0, 1) }}
- </div>
- <div
- style="
- width: 0;
- height: 0;
- border-top: 5px solid transparent;
- border-right: 10px solid #eae9eb;
- border-bottom: 6px solid transparent;
- margin: 24px 0 0 0;
- "
- ></div>
- <div
- style="
- background: #eae9eb;
- padding: 12px 15px 0 15px;
- line-height: 25px;
- border-radius: 8px;
- font-weight: 550;
- color: #000;
- "
- >
- {{ item.msg_info }}
- </div>
- </div>
- </li>
- </ul>
- <!-- 发送框 -->
- <el-input
- style="border: 0"
- resize="none"
- type="textarea"
- v-model="input"
- @keyup.enter.native="submit()"
- @keydown.native="pushKeyword($event)"
- maxlength="30"
- show-word-limit
- placeholder="按下enter按键发送消息"
- ></el-input>
- </el-dialog>
- <el-dialog
- title="视频通话通知"
- :close-on-click-modal="false"
- :modal="false"
- :close-on-press-escape="false"
- :visible.sync="videoNoticeDialogVisible"
- @close="handleCloseNoticeDialog"
- width="500px"
- >
- <div style="padding:40px 20px;">{{ videoNoticeTitle }}</div>
- <span slot="footer" class="dialog-footer">
- <el-button @click="handleCloseNoticeDialog">取 消</el-button>
- <el-button type="primary" @click="confrimNoticeClick">确 定</el-button>
- </span>
- </el-dialog>
- </div>
- </template>
- <script>
- import { mapState, mapMutations } from 'vuex';
- import { color } from 'highcharts';
- import { ZegoExpressEngine } from 'zego-express-engine-webrtc';
- export default {
- //import引入的组件需要注入到对象中才能使用
- components: {},
- data() {
- //这里存放数据
- return {
- fullHeight: document.documentElement.clientHeight - 116, //
- // 搜索
- nameVal: '',
- phoneVal: '',
- // 表格
- tableData: [],
- websockMsgList: [], // websocket传递来的数组数据
- loading: false, // 加载
- org_id: '', //组织id
- // 树形图
- data: [],
- loading2: false,
- defaultProps: {
- children: 'childrens',
- label: 'org_name'
- },
- // 分页
- pageSum: 0,
- page: 1,
- // 视频通话弹框
- idName: 'remoteVideo', // ref 副视频
- idName1: 'localVideo', // ref 主视频
- videoVisible: false,
- videoShow: true, //主视频是否展示画面
- audioShow: true, // 判断当前是语音通话还是视频通话 true为视频 false为语音
- microphone: true, // 是否打开麦克风
- camera: true, // 是否打开摄像头
- // appID: 2672645646, //项目唯一标识 AppID
- appID: 3215939266, //项目唯一标识 AppID
- // server: "1e8344b2a193220e5e96338ba53c3dcc", // 接入服务器地址Server
- // videoUrl: "wss://webliveroom2672645646-api.imzego.com/ws", // 请求 - 测试
- videoUrl: 'wss://webliveroom3215939266-api.imzego.com/ws', // 请求 - 正式
- zg: null,
- // UserID: "sample" + Math.floor(Math.random() * 10000000000000).toString(),
- UserID: 'user00002',
- // UserID: "168",
- // StreamID: "web-4796754531236",
- StreamID: 'web-' + Math.floor(Math.random() * 10000000000000).toString(),
- localStream: null,
- Token: '',
- RoomID: '',
- videoTle: '正在和云飞-卢万里视频通话',
- timer: null, // 接听
- // 文本聊天框
- tltData: '',
- dialogVisible: false,
- input: '', //发送框
- iconShow: true,
- // 文本消息功能
- url:
- // "ws://192.168.1.17:12345/api/api_gateway?method=control_center.real_time.im_message",
- // "ws" +
- // "https" +
- this.$wsUrl +
- '/api/api_gateway?method=control_center.real_time.im_message',
- websock: null,
- getUserObj: {}, // 获取到当前点击的行数据
- msgList: [], //当前点击的账号消息列表
- userName: '',
- // 心跳、重连机制
- ws: null, //建立的连接
- lockReconnect: false, //是否真正建立连接
- timeout: 5 * 1000, //30秒一次心跳
- timeoutObj: null, //心跳心跳倒计时
- serverTimeoutObj: null, //心跳倒计时
- timeoutnum: null, //断开 重连倒计时
- // 视频通话窗口缩放
- zoomShow: false, // 默认最小化
- // websocket断开加载框
- fullscreenLoading: null,
- // 消息发送加载
- msgSetTime: null,
- socketTimeoutLogs: [],
- currentVideoCallUserID: '',
- videoNoticeTitle: '',
- videoNoticeDialogVisible: false,
- currentVideoNoticeData: null,
- isNoticeDialogConfirm: false
- };
- },
- //监听属性 类似于data概念
- computed: {
- ...mapState({
- isVideoCallRefused: state => state.isVideoCallRefused,
- isVideoTalking: state => state.isVideoTalking
- })
- },
- //监控data中的数据变化
- watch: {
- fullHeight(val) {
- //监控浏览器高度变化
- if (!this.timer) {
- this.fullHeight = val;
- this.timer = true;
- let that = this;
- setTimeout(function() {
- //防止过度调用监测事件,导致卡顿
- that.timer = false;
- }, 400);
- }
- },
- // 聊天列表
- msgList(val) {
- // 实现打开聊天框后滚动条定位到最下方
- this.$nextTick(() => {
- var div = document.getElementById('msgBox');
- div.scrollTop = div.scrollHeight;
- });
- },
- // 聊天框
- dialogVisible(val) {
- if (val == false) {
- console.log('update table read status');
- this.updateTableUnreadStatus();
- this.msgList = [];
- }
- },
- // 音视频弹框
- videoVisible(val) {
- if (val == false) {
- this.notPlugFlow(); // 停止推流
- this.notTensile(); // 停止拉流
- this.notLogin(); //退出房间
- }
- },
- // 给表格文本消息增加提示,数据从webscoket中获取
- tableData(val) {
- var that = this;
- this.updateTableUnreadStatus();
- },
- deep: true,
- // websocket列表变化
- websockMsgList(val) {
- console.log(
- '-----------------有变化了啊---------------------------',
- val
- );
- // var that = this
- // for (var i = 0; i < this.tableData.length; i++) {
- // let sdhkj = this.tableData[i];
- // for (var k = 0; k < val.length; k++) {
- // let sdjaks = val[k];
- // if (sdhkj.real_name == sdjaks.real_name && sdjaks.unread !== 0 && sdjaks.unread) {
- // this.tableData[i]['unread'] = sdjaks.unread
- // }
- // }
- // }
- this.userListData(); // 获取右侧用户列表
- },
- isVideoCallRefused(val) {
- console.warn(val, 'isVideoCallRefused video');
- if (val) {
- if (this.videoNoticeDialogVisible) {
- this.$message({
- message: '对方已挂断',
- type: 'warning',
- duration: 1500
- });
- this.videoNoticeDialogVisible = false;
- } else {
- this.$message({
- message: '对方正忙,请稍后再试!',
- type: 'warning',
- duration: 1500
- });
- }
- this.notLogin();
- }
- }
- },
- //方法集合
- methods: {
- ...mapMutations([
- 'updateVideoSelfRoomInfo',
- 'updateIsRefusedCall',
- 'updateVideoInfo',
- 'updateTalkingStatus'
- ]),
- updateTableUnreadStatus() {
- for (var i = 0; i < this.tableData.length; i++) {
- let sdhkj = this.tableData[i];
- for (var k = 0; k < this.websockMsgList.length; k++) {
- let sdjaks = this.websockMsgList[k];
- if (sdhkj.real_name == sdjaks.real_name) {
- this.tableData[i]['unread'] = Number(sdjaks.unread) || 0;
- }
- }
- }
- },
- //动态获取浏览器高度
- get_boderHeight() {
- const that = this;
- window.onresize = () => {
- return (() => {
- window.fullHeight = document.documentElement.clientHeight;
- that.fullHeight = window.fullHeight;
- })();
- };
- },
- // 树形图
- handleNodeClick(data) {
- console.log(data);
- this.org_id = data.id;
- this.userListData();
- },
- // 搜索
- searchData() {
- this.page = 1;
- this.userListData();
- },
- // 分页
- changeList(page) {
- this.page = page;
- this.loading = true;
- this.tableData = [];
- this.userListData();
- },
- // 获取左侧组织列表
- organizationData() {
- this.$axios({
- method: 'POST',
- url: '/api/api_gateway?method=sysmenage.usermanager.org_list',
- data: this.qs.stringify({
- page: this.page,
- page_item: '100000000',
- org_name: '',
- is_mails: 1
- })
- })
- .then(res => {
- if (res.data.data.page_list.length !== 0) {
- var obj = {
- org_name: '全部',
- id: ''
- };
- var data = res.data.data.page_list;
- this.data = [obj, ...data]; // 左侧组织列表
- }
- this.loading2 = false;
- })
- .catch(err => {
- this.loading2 = false;
- });
- },
- // 获取右侧用户列表
- userListData() {
- this.$axios({
- method: 'POST',
- url: '/api/api_gateway?method=sysmenage.usermanager.user_list',
- data: this.qs.stringify({
- page: this.page,
- page_item: '20',
- real_name: this.nameVal, //用户名称
- mobile: this.phoneVal, // 电话
- org_id: this.org_id,
- is_mails: 1
- })
- })
- .then(res => {
- if (res.data.data.total_item !== 0) {
- var data = res.data.data.page_list;
- var list = [];
- data.forEach((item, index) => {
- item.ind = index + 1;
- list.push(item);
- });
- this.tableData = list;
- this.pageSum = res.data.data.total_item;
- this.loading = false;
- }
- })
- .catch(err => {
- this.loading = false;
- });
- },
- // 文本消息
- msgAxios(data) {
- this.getUserObj = data;
- this.redtf = data;
- this.userName = localStorage.getItem('username');
- console.log(this.userName);
- this.tltData = '与' + data.real_name + '的对话';
- var obj = {};
- obj = {
- action: 'list',
- recv_user_id: data.user_id,
- data: {
- msg_status: false,
- msg_info: ''
- }
- };
- this.websock.send(JSON.stringify(obj)); // 获取聊天记录
- // var readObj = {};
- // readObj = {
- // action: "read",
- // send_user_id: data.user_id,
- // data: {
- // msg_status: false,
- // msg_info: "",
- // },
- // };
- // this.websock.send(JSON.stringify(readObj)); // 消息已读
- this.dialogVisible = true;
- },
- // 视频消息
- videoAxios(data) {
- this.getUserObj = data;
- this.userName = localStorage.getItem('username');
- this.videoTle = '正在和' + data.real_name + '视频通话';
- this.UserID = localStorage.getItem('userID');
- console.warn(data.user_id, '------------------------------ vidoe ');
- // 先获取当前用户的房间号和登录所需的Token
- var obj = {};
- obj = {
- action: 'send_video',
- recv_user_id: data.user_id,
- data: {
- msg_status: false,
- msg_info: ''
- }
- };
- // obj = {
- // action: 'reject_video', // 动作标识,必填
- // recv_user_id: data.user_id, // 接收人用户id, 非必填
- // data: {}
- // };
- console.warn(obj, '------------------------------ vidoe ');
- this.currentVideoCallUserID = data.user_id;
- // console.log('gdahjdgjasgd', data.user_id)
- this.websock.send(JSON.stringify(obj));
- // // 登录房间
- // this.zg.loginRoom(
- // this.RoomID,
- // this.Token,
- // { userID: this.UserID, userName: this.UserID },
- // { userUpdate: true }
- // ).then((result) => {
- // if (result == true) {
- // this.plugFlow(); //推流
- // this.videoVisible = true
- // }
- // });
- },
- // 打开视频画面
- openVideo() {
- this.plugFlow();
- },
- // 音视频推流
- async plugFlow() {
- this.videoShow = false;
- this.camera = true;
- const localStream = await this.zg.createStream();
- // stream 为 MediaStream 对象,开发者可通过赋值给 video 或 audio 的 srcObject 属性进行渲染
- this.$refs['localVideo'].srcObject = localStream;
- this.localStream = localStream;
- // localStream 为创建流获取的 MediaStream 对象
- this.zg.startPublishingStream(this.StreamID, localStream);
- },
- // 音视频停止推流
- notPlugFlow() {
- this.videoShow = true;
- this.camera = false;
- this.zg.stopPublishingStream(this.StreamID);
- this.zg.destroyStream(this.localStream);
- },
- // 拉流
- async tensile(streamID) {
- var playOption = { videoDecodeType: 'H264', playType: 'video' };
- const remoteStream = await this.zg.startPlayingStream(
- streamID,
- playOption
- );
- // remoteVideo 为本地 <video> 或 <audio> 对象
- this.$refs['remoteVideo'].srcObject = remoteStream;
- },
- notTensile(streamID) {
- this.zg.stopPlayingStream(streamID);
- },
- // 切换视频窗口
- switchWindow() {
- // 副视频画面 remoteVideo
- // 主视频画面 localVideo
- this.idName = this.idName == 'remoteVideo' ? 'localVideo' : 'remoteVideo';
- this.idName =
- this.idName1 == 'remoteVideo' ? 'localVideo' : 'remoteVideo';
- },
- // 退出房间
- notLogin() {
- this.updateTalkingStatus(false);
- this.updateIsRefusedCall(false);
- clearTimeout(this.timer);
- this.zg.logoutRoom(this.RoomID);
- this.videoVisible = false;
- this.hangUp();
- },
- // 当音视频通话关闭时的回调
- handleClose(done) {
- var that = this;
- this.$confirm('目前正在音视频通话中,确认关闭?')
- .then(_ => {
- done();
- that.notLogin(); // 退出房间
- this.currentVideoCallUserID = '';
- })
- .catch(_ => {});
- },
- // 发送消息
- async submit() {
- if (this.input.split(' ').join('').length == 0) {
- if (document.getElementsByClassName('el-message').length == 0) {
- this.$message({
- message: '不能发送空白消息!',
- type: 'warning',
- duration: 1500
- });
- }
- this.input = '';
- } else {
- try {
- var obj = {};
- obj = {
- action: 'send',
- recv_user_id: this.getUserObj.user_id,
- data: {
- msg_status: false,
- msg_info: this.input
- }
- };
- var v = JSON.stringify(obj);
- this.websock.send(v);
- this.input = '';
- this.msgSetTime = setTimeout(() => {
- // 设定十秒内没有接收到服务器返回的list数据就表明消息没有发送成
- if (document.getElementsByClassName('el-message').length == 0) {
- this.$message({
- message: '消息发送失败,请刷新界面重试!',
- type: 'warning',
- duration: 1500
- });
- }
- }, 10000);
- } catch (error) {
- console.log('>>> sendMsg, error: ', error);
- }
- }
- },
- // 取消回车换行行为
- pushKeyword(event) {
- if (event.keyCode === 13) {
- event.preventDefault(); // 阻止浏览器默认换行操作
- this.keyword = '';
- return false;
- }
- },
- // 更多消息
- moreMsg() {},
- // 文本消息功能初始化
- async msgInit() {
- var that = this;
- if (typeof WebSocket === 'undefined') {
- alert('您的浏览器不支持socket!');
- } else {
- console.log(this.url, 'msg int 00000000000000000 12313441');
- this.websock = new window.WebSocket(
- this.url + '&token=' + localStorage.getItem('session')
- );
- this.websock.onopen = event => {
- console.log('WebSocket:已连接');
- console.log(event);
- // 发送消息 - 获取对话列表
- var obj1 = {};
- obj1 = {
- action: 'list',
- recv_user_id: '',
- data: {
- msg_status: false,
- msg_info: ''
- }
- };
- this.start(); //开启心跳
- if (this.fullscreenLoading !== null) {
- this.websock.send(JSON.stringify(obj1));
- }
- // this.fullscreenLoading.close(); // 加载关闭
- };
- this.websock.onmessage = event => {
- var data = JSON.parse(event.data);
- console.warn('WebSocket:消息---------------------------', data);
- if (data.action == 'none') {
- // 获取聊天记录
- var datArr = data.data; // 总数据
- this.websockMsgList = datArr; // 把数据定义在data中
- if (datArr.length !== 0) {
- for (var i = 0; i < datArr.length; i++) {
- if (this.getUserObj.user_id == datArr[i].user_id) {
- data.data[i].msg_list = data.data[i].msg_list.reverse();
- that.msgList = data.data[i];
- console.log(data.data[i]);
- }
- }
- }
- // if (data.data[0] !== undefined) {
- // data.data[0].msg_list = data.data[0].msg_list.reverse();
- // this.msgList = data.data[0];
- // }
- } else if (data.action == 'list') {
- console.log('消息发送成功了----------------------------------');
- clearTimeout(this.msgSetTime); // 清除发送失败定时器
- // 返回list为发送消息成功后需要再次调用聊天列表
- var obj = {};
- obj = {
- action: 'list',
- // recv_user_id: this.getUserObj.user_id,
- recv_user_id: '',
- data: {
- msg_status: false,
- msg_info: ''
- }
- };
- this.websock.send(JSON.stringify(obj));
- } else if (data.action == 'recv_video') {
- console.warn('recv_video', data.data, this.isVideoCallRefused);
- if (this.isVideoCallRefused) {
- this.updateIsRefusedCall(false);
- return;
- }
- this.updateIsRefusedCall(false);
- if (this.isVideoTalking) {
- // 占线中
- var obj = {
- action: 'occupy', // 动作标识,必填
- recv_user_id: data.data.user_id, // 接收人用户id, 非必填
- data: {}
- };
- this.websock.send(JSON.stringify(obj));
- return;
- }
- this.updateTalkingStatus(true);
- // 获取当前点击用户的房间号以及登录房间所需的Token
- // console.log(JSON.parse(event.data));
- console.log(data);
- var data = JSON.parse(event.data);
- this.RoomID = data.data.room_id; // 房间号
- this.Token = data.data.room_token; // Token
- this.videoNoticeDialogVisible = true;
- this.currentVideoNoticeData = data.data;
- //TODO 提示弹窗,确认后登录房间
- // const confrimInstance = this.$confirm(
- // `${data.data.real_name}邀请你进行视频通话...`
- // )
- // .then(_ => {
- // this.videoTle = '正在和' + data.data.real_name + '视频通话';
- // this.loginRoom();
- // })
- // .catch(_ => {
- // console.log('------------------------ confirm catch', _);
- // const socketData = {
- // action: 'reject_video', // 动作标识,必填
- // recv_user_id: data.data.user_id, // 接收人用户id, 非必填
- // data: {}
- // };
- // console.warn('send reject_video', socketData);
- // this.websock.send(JSON.stringify(socketData));
- // });
- this.videoNoticeTitle = `${data.data.real_name}邀请你进行视频通话...`;
- console.log('confirm instance', this.$confirm.done);
- //
- } else if (data.action == 'send_video_rsp') {
- console.warn('recv_video---------------- response', data.data);
- if (this.isVideoCallRefused) {
- this.updateIsRefusedCall(false);
- this.updateTalkingStatus(false);
- return;
- }
- this.updateIsRefusedCall(false);
- this.updateVideoSelfRoomInfo(data.data);
- this.RoomID = data.data.room_id; // 房间号
- this.Token = data.data.room_token; // Token
- console.warn(
- 'into romm ready ------------------',
- data.data.room_token
- );
- console.warn('into romm ------------------');
- this.updateTalkingStatus(true);
- // 唤起视频弹窗
- this.loginRoom();
- } else if (data.action == 'reject_video_rsp') {
- // 被拒绝后挂断视频通话
- this.updateIsRefusedCall(true);
- } else if (data.action == 'occupy_rsp') {
- console.warn('----------------------- is refused');
- this.currentVideoCallUserID = '';
- // 占线中,挂断发起通话
- this.updateIsRefusedCall(true);
- } else if (data.action == 'ok') {
- // 心跳机制 - 当前状态是websocket连接没有问题
- this.start(); // 心跳机制
- } else if (data.action == 'error') {
- if (data.code == 403) {
- this.websock.close(); // 关闭socket
- localStorage.removeItem('session');
- this.$message({
- message:
- (data.data && data.data.error_info) ||
- '账号异地登陆,请重新登陆',
- type: 'warning',
- duration: 2500
- });
- setTimeout(() => {
- this.$router.push('/Login');
- }, 3000);
- }
- }
- };
- this.websock.onerror = event => {
- console.log('WebSocket:发生错误 ', Date.now());
- this.socketTimeoutLogs.push(Date.now());
- console.log(event);
- if (that.timeoutnum !== null) {
- setTimeout(() => {
- that.websock.close(); // 先关闭
- clearTimeout(that.timeoutObj); //清除时间
- that.reconnect(); // 重连
- }, 5000);
- }
- };
- this.websock.onclose = event => {
- console.log('WebSocket:已关闭');
- console.log(event);
- console.log(that.timeoutnum);
- if (that.timeoutnum !== null) {
- // this.openFullScreen2();
- setTimeout(() => {
- // that.websock.close(); // 先关闭
- clearTimeout(that.timeoutObj); //清除时间
- // that.reconnect(); // 重连
- }, 5000);
- }
- };
- }
- },
- confrimNoticeClick() {
- this.isNoticeDialogConfirm = true;
- this.videoNoticeDialogVisible = false;
- if (!this.currentVideoNoticeData) {
- return;
- }
- this.videoTle =
- '正在和' + this.currentVideoNoticeData.real_name + '视频通话';
- this.loginRoom();
- },
- handleCloseNoticeDialog() {
- console.warn('close notice dialog', this.isNoticeDialogConfirm);
- this.videoNoticeDialogVisible = false;
- if (!this.isNoticeDialogConfirm) {
- this.cancleNoticeClik();
- }
- this.isNoticeDialogConfirm = false;
- },
- cancleNoticeClik() {
- const socketData = {
- action: 'reject_video', // 动作标识,必填
- recv_user_id: this.currentVideoNoticeData.user_id, // 接收人用户id, 非必填
- data: {}
- };
- console.warn('send reject_video', socketData);
- this.websock.send(JSON.stringify(socketData));
- this.updateTalkingStatus(false);
- this.currentVideoNoticeData = null;
- },
- loginRoom() {
- const userID = localStorage.getItem('userID');
- console.warn(`userID:${userID};userName:${this.userName}`);
- // 登录房间
- this.zg
- .loginRoom(
- this.RoomID,
- this.Token,
- { userID: userID, userName: this.userName },
- { userUpdate: true }
- )
- .then(result => {
- console.log(result);
- if (result == true) {
- console.log('0000000000000000000000000000000000000登录成功');
- this.plugFlow(); //推流
- this.videoVisible = true;
- }
- });
- },
- // 音视频消息功能初始化
- videoInit() {
- this.zg = new ZegoExpressEngine(this.appID, this.videoUrl);
- this.soundOn(); // 监听房间
- this.detection(); // 检测是否兼容当前浏览器
- },
- // 监听房间
- soundOn() {
- // 房间状态更新回调
- this.zg.on(
- 'roomStateUpdate',
- (roomID, state, errorCode, extendedData) => {
- if (state == 'CONNECTED') {
- // 与房间连接成功,只有当房间状态是连接成功时,才能进行推流、拉流等操作。
- // 接下来的“预览并推流”的代码写在这里
- console.log('房间连接成功');
- //定时器
- this.timer = setTimeout(() => {
- if (document.getElementsByClassName('el-message').length == 0) {
- this.$message({
- message: '没人接听,请稍后重试',
- type: 'warning',
- duration: 2500
- });
- }
- this.videoVisible = false;
- // }, 5000);
- }, 30000);
- }
- if (state == 'DISCONNECTED') {
- // 与房间断开了连接
- console.log('与房间断开连接');
- }
- if (state == 'CONNECTING') {
- // 与房间尝试连接中
- console.log('与房间尝试连接中');
- }
- }
- );
- this.zg.on('roomUserUpdate', (roomID, updateType, userList) => {
- // 其他用户进出房间的通知
- console.log(updateType);
- console.log(userList);
- console.log('有其他用户进出房间');
- if (updateType == 'DELETE') {
- if (document.getElementsByClassName('el-message').length == 0) {
- this.$message({
- message: '对方已挂断,结束通话!',
- type: 'warning',
- duration: 1500
- });
- }
- setTimeout(() => {
- this.notLogin(); // 退出房间
- }, 2000);
- } else if (updateType == 'ADD') {
- clearTimeout(this.timer); // 关闭无人接听回调
- }
- });
- this.zg.on(
- 'roomStreamUpdate',
- async (roomID, updateType, streamList, extendedData) => {
- console.log(roomID);
- console.log(updateType);
- console.log(streamList);
- console.log(extendedData);
- // 房间内其他用户音视频流变化的通知
- console.log('有其他用户开启或关闭音频');
- if (updateType == 'ADD') {
- // 流新增,开始拉流
- console.log('这是拉流的ID', streamList);
- this.tensile(streamList[0].streamID);
- } else if (updateType == 'DELETE') {
- // 流删除,停止拉流
- this.notTensile(streamList[0].streamID);
- }
- }
- );
- this.zg.on('publisherStateUpdate', result => {
- // 推流状态更新回调
- console.log('推流状态更新');
- console.log(result);
- });
- this.zg.on('publishQualityUpdate', (streamID, stats) => {
- // 推流质量回调
- console.log('推流质量更新');
- console.log(streamID, stats);
- });
- this.zg.on('playerStateUpdate', result => {
- // 拉流状态回调
- console.log('拉流状态的回调');
- console.log(result);
- });
- this.zg.on('playQualityUpdate', (streamID, stats) => {
- // 拉流成功后回调
- console.log('拉流质量回调');
- console.log(streamID);
- console.log(stats);
- });
- },
- // 检测是否兼容当前浏览器
- async detection() {
- const result = await this.zg.checkSystemRequirements();
- // 返回的 result 为兼容性检测结果。 webRTC 为 true 时表示支持 webRTC,其他属性含义可以参考接口 API 文档
- console.log(result);
- if (result.webRTC == true) {
- console.log('兼容');
- } else {
- console.log('不兼容');
- }
- },
- // 心跳机制
- start() {
- //开启心跳
- var self = this;
- self.timeoutObj && clearTimeout(self.timeoutObj);
- self.serverTimeoutObj && clearTimeout(self.serverTimeoutObj);
- self.timeoutObj = setTimeout(function() {
- //这里发送一个心跳,后端收到后,返回一个心跳消息,
- console.log('心跳中。。。', self.websock.readyState);
- if (self.websock.readyState == 1) {
- //如果连接正常
- var obj1 = {};
- obj1 = {
- action: 'keepalive',
- type: 'PC',
- recv_user_id: '',
- data: {
- msg_status: false,
- msg_info: ''
- }
- };
- self.websock.send(JSON.stringify(obj1));
- } else {
- //否则重连
- self.reconnect();
- }
- self.serverTimeoutObj = setTimeout(function() {
- //超时关闭
- console.log('又进入了?');
- self.websock.close();
- self.reconnect(); // 重连
- }, self.timeout);
- }, self.timeout);
- },
- reset() {
- //重置心跳
- var that = this;
- clearTimeout(that.timeoutObj); //清除时间
- clearTimeout(that.serverTimeoutObj); //清除时间
- that.start(); //重启心跳
- },
- // 重连机制
- reconnect() {
- var that = this;
- console.log('进入重连了');
- clearTimeout(that.timeoutObj); //清除时间
- if (that.lockReconnect) {
- return;
- }
- that.lockReconnect = true;
- // 没连接上会一直重连,设置延迟避免请求过多
- that.timeoutnum && clearTimeout(that.timeoutnum);
- that.timeoutnum = setTimeout(function() {
- that.msgInit(); //新连接
- that.lockReconnect = false;
- }, 5000);
- },
- // 视频通话窗口最小化、最大化
- zoomOperate(ref) {
- if (ref == 'small') {
- // 最小化
- this.zoomShow = false;
- } else if (ref == 'big') {
- // 最大化
- this.zoomShow = true;
- }
- },
- // websocket断开加载框
- openFullScreen2() {
- // this.fullscreenLoading = this.$loading({
- // lock: true,
- // text: "通信异常,连接中...",
- // spinner: "el-icon-loading",
- // background: "rgba(0, 0, 0, 0.7)",
- // });
- },
- hangUp() {
- if (!this.currentVideoCallUserID) {
- return;
- }
- if (this.zg) {
- this.zg.logoutRoom(this.RoomID);
- }
- const socketData = {
- action: 'reject_video', // 动作标识,必填
- recv_user_id: this.currentVideoCallUserID, // 接收人用户id, 非必填
- data: {}
- };
- console.warn('send reject_video', socketData);
- this.websock.send(JSON.stringify(socketData));
- },
- handleTextDialogClose() {
- console.log('text dialog close ');
- }
- },
- //生命周期 - 创建完成(可以访问当前this实例)
- created() {
- this.loading = true;
- this.loading2 = true;
- this.organizationData(); //获取左侧组织列表
- this.userListData(); // 获取右侧用户列表
- },
- //生命周期 - 挂载完成(可以访问DOM元素)
- mounted() {
- // this.loading = true;
- // this.loading2 = true;
- // this.organizationData(); //获取左侧组织列表
- // this.userListData(); // 获取右侧用户列表
- // console.log("---------------------------");
- // console.log(this.websock);
- // console.log("---------------------------");
- if (this.websock == null) {
- this.msgInit(); // 文本消息功能初始化
- }
- this.videoInit(); // 视频消息功能初始化
- window.socketTimeoutLogs = this.socketTimeoutLogs;
- },
- beforeCreate() {}, //生命周期 - 创建之前
- beforeMount() {}, //生命周期 - 挂载之前
- beforeUpdate() {}, //生命周期 - 更新之前
- updated() {}, //生命周期 - 更新之后
- beforeDestroy() {
- console.log('即时通讯挂断 销毁页面');
- this.hangUp();
- // var that = this;
- // clearTimeout(that.timeoutnum); // 清除重连
- // clearTimeout(that.timeoutObj); // 清除心跳
- // that.websock.close(); // 关闭websocket
- }, //生命周期 - 销毁之前
- destroyed() {
- console.log('我进入到销毁里喽');
- this.updateTalkingStatus(false);
- this.updateIsRefusedCall(false);
- var that = this;
- clearTimeout(that.timeoutnum); // 清除重连
- clearTimeout(that.timeoutObj); // 清除心跳
- that.websock.close(); // 关闭websocket
- that.websock = null; // 清空
- console.log(that.websock);
- }, //生命周期 - 销毁完成
- activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
- };
- </script>
- <style lang="less" scoped>
- .realTime_box {
- // 搜索
- .search_box {
- display: flex;
- /deep/.el-input {
- width: 20%;
- margin: 0 15px 0 0;
- }
- .btn_box {
- display: flex;
- justify-content: start;
- }
- }
- .card_box {
- display: flex;
- width: 100%;
- // 树形图
- .userManger_left {
- width: 19%;
- margin: 0 15px 0 0;
- padding: 5px;
- border: 1px solid #eeeeee;
- border-radius: 5px;
- overflow: hidden;
- overflow-y: auto;
- }
- // 搜索和表格
- .userManger_right {
- width: 80%;
- // border: 1px solid red;
- a {
- text-decoration: none;
- }
- .reset {
- color: #1890ff;
- }
- .delete {
- color: #f56c6c;
- }
- .line {
- display: inline-block;
- width: 1px;
- background: rgba(0, 0, 0, 0.09);
- margin: 0 11px;
- height: 14px;
- }
- }
- }
- /deep/.el-select {
- width: 80%;
- }
- /deep/.el-cascader {
- width: 80%;
- }
- // 视频通话 - 最小化
- .video_box {
- border: 1px solid #000;
- background: #2c2c2c;
- height: 325px;
- position: relative;
- // 主视频画面
- .host_video {
- height: 326px;
- vertical-align: middle;
- text-align: center;
- position: relative;
- .video_img {
- margin: 120px auto;
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- margin: auto;
- }
- }
- // 副视频画面
- .deputy_video {
- width: 90px;
- height: 90px;
- position: absolute;
- top: 0;
- right: 0;
- background: #fff;
- }
- // 操作
- .operate {
- height: 25px;
- width: 100%;
- background: #000;
- padding: 5px 0 5px 0;
- margin: -13px 0 0 0px;
- position: absolute;
- bottom: 0;
- left: 0;
- display: flex;
- justify-content: flex-end;
- }
- }
- // 视频通话 - 最大化
- .video_box-big {
- border: 1px solid #000;
- background: #2c2c2c;
- height: 100%;
- position: relative;
- // 主视频画面
- .host_video {
- height: 100%;
- vertical-align: middle;
- text-align: center;
- position: relative;
- .video_img {
- margin: 120px auto;
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- margin: auto;
- }
- }
- // 副视频画面
- .deputy_video {
- width: 190px;
- height: 190px;
- position: absolute;
- top: 0;
- right: 0;
- background: #fff;
- }
- // 操作
- .operate {
- height: 25px;
- width: 100%;
- background: #000;
- padding: 5px 0 5px 0;
- margin: -13px 0 0 0px;
- position: absolute;
- bottom: 3%;
- left: 0;
- display: flex;
- justify-content: flex-end;
- }
- }
- /* 聊天内容 */
- .ul_list {
- height: 350px;
- // margin-left: -41px;
- overflow: hidden;
- overflow-y: auto;
- }
- .list_msgBox1 {
- padding: 0 10px;
- display: flex;
- margin-top: 22px;
- justify-content: flex-end;
- }
- .list_msgBox2 {
- padding: 0 10px;
- display: flex;
- margin-top: 22px;
- }
- .test-5::-webkit-scrollbar {
- /*滚动条整体样式*/
- width: 10px; /*高宽分别对应横竖滚动条的尺寸*/
- height: 1px;
- }
- .test-5::-webkit-scrollbar-thumb {
- /*滚动条里面小方块*/
- border-radius: 10px;
- background-color: skyblue;
- background-image: -webkit-linear-gradient(
- 45deg,
- rgba(255, 255, 255, 0.2) 25%,
- transparent 25%,
- transparent 50%,
- rgba(255, 255, 255, 0.2) 50%,
- rgba(255, 255, 255, 0.2) 75%,
- transparent 75%,
- transparent
- );
- }
- .test-5::-webkit-scrollbar-track {
- /*滚动条里面轨道*/
- box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
- background: #ededed;
- border-radius: 10px;
- }
- .list {
- /* border: 1px solid red; */
- width: 100%;
- height: 100px;
- }
- /* ElementUI 样式 */
- // 聊天框
- /deep/.el-dialog {
- overflow: hidden;
- }
- /deep/.el-dialog__header {
- background: #f2f2f2;
- border-bottom: 1px solid #cacaca;
- }
- /deep/.el-dialog__title {
- font-weight: 550;
- font-size: 15px;
- line-height: 0;
- float: left;
- }
- /deep/.el-dialog__headerbtn {
- top: 10px;
- right: 10px;
- }
- /deep/.el-dialog__headerbtn .el-dialog__close {
- font-size: 20px;
- line-height: 15px;
- }
- /deep/.el-dialog__body {
- padding: 0;
- // margin-top: -15px;
- height: 100%;
- }
- /deep/.el-textarea__inner {
- border: 0;
- border-top: 1px solid #dcdfe6;
- border-radius: 0;
- height: 95px;
- }
- /deep/.el-card {
- overflow: hidden;
- overflow-y: auto;
- }
- }
- /deep/.el-button--info {
- background-color: #409eff;
- border-color: #409eff;
- }
- </style>
|