zkl vor 4 Jahren
Ursprung
Commit
f2413e46bd

+ 1 - 1
minggao/config/index.js

@@ -22,7 +22,7 @@ module.exports = {
     },
 
     // Various Dev Server settings
-    host: '192.168.1.4', // can be overwritten by process.env.HOST
+    host: 'localhost', // can be overwritten by process.env.HOST
     port: 8888, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
     autoOpenBrowser: false,
     errorOverlay: true,

+ 24 - 3
minggao/package-lock.json

@@ -10928,9 +10928,9 @@
       "dev": true
     },
     "v-viewer": {
-      "version": "1.5.1",
-      "resolved": "https://registry.nlark.com/v-viewer/download/v-viewer-1.5.1.tgz?cache=0&sync_timestamp=1632202257840&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fv-viewer%2Fdownload%2Fv-viewer-1.5.1.tgz",
-      "integrity": "sha1-lIBNg4FMylZuaRM5/h5keTSG8gA=",
+      "version": "1.6.4",
+      "resolved": "https://registry.npmmirror.com/v-viewer/download/v-viewer-1.6.4.tgz",
+      "integrity": "sha1-OeNrU0uqs0B2+4FnBManNN4Nxy8=",
       "dev": true,
       "requires": {
         "throttle-debounce": "^2.0.1",
@@ -12130,6 +12130,27 @@
         }
       }
     },
+    "zego-express-engine-webrtc": {
+      "version": "2.13.0",
+      "resolved": "https://registry.npmmirror.com/zego-express-engine-webrtc/download/zego-express-engine-webrtc-2.13.0.tgz",
+      "integrity": "sha512-f7zSVYlMdFK1TiS+z/XmbkM8JaC0Xio1GOyMERb0rgV617krj0KS3+ZnrGaEVPhfCZnc7/PHArRcLM7jqBKHvw==",
+      "requires": {
+        "zego-express-engine-webrtm": "1.12.0"
+      }
+    },
+    "zego-express-engine-webrtm": {
+      "version": "1.12.0",
+      "resolved": "https://registry.npmmirror.com/zego-express-engine-webrtm/download/zego-express-engine-webrtm-1.12.0.tgz",
+      "integrity": "sha512-Yp7S1EFJ00OyJ+q+pXKI7WVzBe3b9Nb7JGB6xw4DmkkyJ69IHG+hy3B+z/eqZf/ejvkkY1+nElDefjLlSbLElA==",
+      "requires": {
+        "zego-express-logger": "1.2.0"
+      }
+    },
+    "zego-express-logger": {
+      "version": "1.2.0",
+      "resolved": "https://registry.nlark.com/zego-express-logger/download/zego-express-logger-1.2.0.tgz",
+      "integrity": "sha1-/pkxS+xAjXP22EuYguEh+lPLh/I="
+    },
     "zrender": {
       "version": "4.3.2",
       "resolved": "https://registry.nlark.com/zrender/download/zrender-4.3.2.tgz",

+ 3 - 2
minggao/package.json

@@ -15,7 +15,8 @@
     "less": "^4.1.2",
     "vue": "^2.5.2",
     "vue-router": "^3.0.1",
-    "vuex": "^3.6.2"
+    "vuex": "^3.6.2",
+    "zego-express-engine-webrtc": "^2.13.0"
   },
   "devDependencies": {
     "autoprefixer": "^7.1.2",
@@ -52,7 +53,7 @@
     "swiper": "^3.4.2",
     "uglifyjs-webpack-plugin": "^1.1.1",
     "url-loader": "^0.5.8",
-    "v-viewer": "^1.5.1",
+    "v-viewer": "^1.6.4",
     "vue-highcharts": "^0.1.0",
     "vue-lazyload": "^1.3.3",
     "vue-loader": "^13.3.0",

+ 9 - 0
minggao/src/App.vue

@@ -7,6 +7,15 @@
 <script>
 export default {
   name: "App",
+  data() {
+    return {
+    }
+  },
+  methods: {
+
+  },
+  mounted() {
+  }
 };
 </script>
 

BIN
minggao/src/assets/images/realTime/10.png


BIN
minggao/src/assets/images/realTime/11.png


BIN
minggao/src/assets/images/realTime/12.png


BIN
minggao/src/assets/images/realTime/13.png


BIN
minggao/src/assets/images/realTime/6.png


BIN
minggao/src/assets/images/realTime/7.png


BIN
minggao/src/assets/images/realTime/8.png


BIN
minggao/src/assets/images/realTime/9.png


BIN
minggao/src/assets/images/realTime/B.png


BIN
minggao/src/assets/images/realTime/shipin.png


BIN
minggao/src/assets/images/realTime/xiaoxi.png


+ 80 - 0
minggao/src/assets/js/directives.js

@@ -0,0 +1,80 @@
+import Vue from 'vue';
+  
+// v-dialogDrag: 弹窗拖拽属性
+Vue.directive('dialogDrag', {
+  bind(el, binding, vnode, oldVnode) {
+    const dialogHeaderEl = el.querySelector('.el-dialog__header');
+    const dragDom = el.querySelector('.el-dialog');
+    //dialogHeaderEl.style.cursor = 'move';
+    dialogHeaderEl.style.cssText += ';cursor:move;'
+    dragDom.style.cssText += ';top:0px;'
+  
+    // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
+    const sty = (function() {
+        if (window.document.currentStyle) {
+            return (dom, attr) => dom.currentStyle[attr];
+        } else{
+            return (dom, attr) => getComputedStyle(dom, false)[attr];
+        }
+    })()    
+     
+    dialogHeaderEl.onmousedown = (e) => {
+      // 鼠标按下,计算当前元素距离可视区的距离
+      const disX = e.clientX - dialogHeaderEl.offsetLeft;
+      const disY = e.clientY - dialogHeaderEl.offsetTop;
+       
+      const screenWidth = document.body.clientWidth; // body当前宽度
+        const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取) 
+         
+        const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
+        const dragDomheight = dragDom.offsetHeight; // 对话框高度
+         
+        const minDragDomLeft = dragDom.offsetLeft;
+        const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
+         
+        const minDragDomTop = dragDom.offsetTop;
+        const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
+  
+       
+      // 获取到的值带px 正则匹配替换
+      let styL = sty(dragDom, 'left');
+      let styT = sty(dragDom, 'top');
+  
+      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
+      if(styL.includes('%')) {
+        styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100);
+        styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100);
+      }else {
+        styL = +styL.replace(/\px/g, '');
+        styT = +styT.replace(/\px/g, '');
+      };
+       
+      document.onmousemove = function (e) {
+        // 通过事件委托,计算移动的距离 
+                let left = e.clientX - disX;
+                let top = e.clientY - disY;
+                 
+                // 边界处理
+                if (-(left) > minDragDomLeft) {
+                    left = -(minDragDomLeft);
+                } else if (left > maxDragDomLeft) {
+                    left = maxDragDomLeft;
+                }
+                 
+                if (-(top) > minDragDomTop) {
+                    top = -(minDragDomTop);
+                } else if (top > maxDragDomTop) {
+                    top = maxDragDomTop;
+                }
+  
+        // 移动当前元素 
+                dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
+      };
+  
+      document.onmouseup = function (e) {
+        document.onmousemove = null;
+        document.onmouseup = null;
+      };
+    } 
+  }
+})

+ 28 - 0
minggao/src/assets/js/drag.js

@@ -0,0 +1,28 @@
+import Vue from 'vue';
+//使用Vue.directive()定义一个全局指令
+//1.参数一:指令的名称,定义时指令前面不需要写v-
+//2.参数二:是一个对象,该对象中有相关的操作函数
+//3.在调用的时候必须写v-
+const drag = Vue.directive('drag', {
+    //1.指令绑定到元素上回立刻执行bind函数,只执行一次
+    //2.每个函数中第一个参数永远是el,表示绑定指令的元素,el参数是原生js对象
+    //3.通过el.focus()是无法获取焦点的,因为只有插入DOM后才生效
+    bind: function (el) { },
+    //inserted表示一个元素,插入到DOM中会执行inserted函数,只触发一次
+    inserted: function (el) {
+        el.onmousedown = function (e) {
+            var disx = e.pageX - el.offsetLeft;
+            var disy = e.pageY - el.offsetTop;
+            document.onmousemove = function (e) {
+                el.style.left = e.pageX - disx + 'px';
+                el.style.top = e.pageY - disy + 'px';
+            }
+            document.onmouseup = function () {
+                document.onmousemove = document.onmouseup = null;
+            }
+        }
+    },
+    //当VNode更新的时候会执行updated,可以触发多次
+    updated: function (el) { }
+})
+export default drag;

+ 24 - 0
minggao/src/main.js

@@ -6,6 +6,9 @@ import store from '@/store/store.js'
 import less from 'less'
 Vue.use(less)
 
+import './assets/js/directives'; // 实现elementui对话框可拖拽功能
+import drag from '@/assets/js/drag'; // 实现div弹框可拖拽功能
+
 import Viewer from 'v-viewer'
 import 'viewerjs/dist/viewer.css'
 Vue.use(Viewer, {
@@ -13,6 +16,27 @@ Vue.use(Viewer, {
     "zIndex": 9999,
   }
 })
+// import Viewer from 'v-viewer'
+// import 'viewerjs/dist/viewer.css'
+// Vue.use(Viewer)
+// Viewer.setDefaults({
+//   Options: {
+//     inline: true,
+//     button: false,
+//     navbar: true,
+//     title: true,
+//     toolbar: true,
+//     tooltip: true,
+//     movable: true,
+//     zoomable: true,
+//     rotatable: true,
+//     scalable: true,
+//     transition: true,
+//     fullscreen: true,
+//     keyboard: true,
+//     url: 'data-source'
+//   }
+// })
 
 Vue.prototype.$store = store
 

+ 951 - 6
minggao/src/page/commandCenter/realTime.vue

@@ -1,29 +1,788 @@
 <!--  -->
 <template>
-  <div class="">
-      实时通信
+  <div class="realTime_box">
+    <el-card :style="'height:' + fullHeight + 'px'">
+      <div class="card_box">
+        <!-- 组织 -->
+        <div
+          class="userManger_left"
+          :style="'height:' + (fullHeight - 100) + 'px'"
+        >
+          <el-tree
+            :data="data"
+            :props="defaultProps"
+            @node-click="handleNodeClick"
+          ></el-tree>
+        </div>
+
+        <!-- 表格 -->
+        <div
+          class="userManger_right"
+          :style="'height:' + (fullHeight - 100) + 'px'"
+        >
+          <!-- 搜索 -->
+          <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="primary" size="mini" @click="searchData"
+                    >搜索</el-button
+                  >
+                </div>
+              </div>
+            </el-col>
+          </el-row>
+
+          <el-table :data="tableData" stripe style="width: 100%">
+            <el-table-column prop="ind" label="序号" width="180">
+            </el-table-column>
+            <el-table-column prop="real_name" label="组织成员" width="250">
+            </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 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
+                >
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" width="280">
+              <template slot-scope="scope">
+                <span>
+                  <img
+                    @click="msgAxios(scope.row)"
+                    src="../../assets/images/realTime/xiaoxi.png"
+                    alt=""
+                  />
+                </span>
+                <!-- <span>
+                  <img src="../../assets/images/realTime/B.png" alt="">
+                </span> -->
+                <span>
+                  <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"
+          >
+          </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
+      :title="videoTle"
+      :visible.sync="videoVisible"
+      width="40%"
+    >
+      <div class="video_box">
+        <div class="host_video" v-if="videoShow">
+          <img
+            v-if="audioShow"
+            src="../../assets/images/realTime/8.png"
+            alt=""
+            class=""
+          />
+          <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>
+        </div>
+
+        <!-- 副视频画面 -->
+        <div v-if="audioShow" class="deputy_video" v-drag>
+          <video
+            style="width: 100%; height: 100%"
+            ref="remoteVideo"
+            autoplay
+            playsinline
+            :muted="true"
+          ></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="PlugFlow" 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="30%"
+    >
+      <!-- 聊天内容 -->
+      <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.send_real_name == userName">
+            <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>
+              <img
+                src="../../../static/images/realTime/7.png"
+                alt=""
+                class=""
+              />
+            </div>
+          </div>
+          <!-- you -->
+          <div
+            v-else-if="item.recv_user_id == this.getUserObj.user_id"
+            class="list_msgBox2"
+          >
+            <div>
+              <img
+                src="../../../static/images/realTime/6.png"
+                alt=""
+                class=""
+              />
+            </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>
   </div>
 </template>
 
 <script>
+import { color } from "highcharts";
+import { ZegoExpressEngine } from "zego-express-engine-webrtc";
 
 export default {
   //import引入的组件需要注入到对象中才能使用
   components: {},
   data() {
     //这里存放数据
-    return {};
+    return {
+      fullHeight: document.documentElement.clientHeight - 116, //
+
+      // 搜索
+      nameVal: "",
+      phoneVal: "",
+
+      // 表格
+      tableData: [],
+      org_id: "", //组织id
+
+      // 树形图
+      data: [],
+      defaultProps: {
+        children: "childrens",
+        label: "org_name",
+      },
+
+      // 分页
+      pageSum: 0,
+      page: 1,
+
+      // 视频通话弹框
+      videoVisible: false,
+      videoShow: true, //主视频是否展示画面
+      audioShow: true, // 判断当前是语音通话还是视频通话  true为视频 false为语音
+      microphone: true, // 是否打开麦克风
+      camera: true, // 是否打开摄像头
+      appID: 2672645646, //项目唯一标识 AppID
+      server: "1e8344b2a193220e5e96338ba53c3dcc", // 接入服务器地址Server
+      videoUrl: "wss://webliveroom2672645646-api.imzego.com/ws", // 请求
+      zg: null,
+      // UserID: "sample" + Math.floor(Math.random() * 10000000000000).toString(),
+      UserID: "user00002",
+      StreamID: "web-4796754531236",
+      // StreamID: "web-" + Math.floor(Math.random() * 10000000000000).toString(),
+      localStream: null,
+      Token:
+        "03AAAAAGHyVbgAEGdqdmp0cnpia2lvcnd2aGUAsEZBIPxO1xCGkPt7alN2ldHRSJ/di1xoXhw5hxlwTyXUFb4FUPCg6xbKyjEK8ne34apzy0JiJw7V5apRQSCrxv3k+fYYVtM8IgaXza1yD77x50HIAe3raJLvCGDn56jC5ElN//QdspBKkjHBQ0GFHKAlRN5oYDYja7sv3wKsCGRAQW26cxnbVm8SUOU/r71kB4C+qXrb7UE1/IgdkS87RJtJfwiafpOvya0nK9wTTZOY",
+      RoomID: "00002",
+      videoTle: "正在和云飞-卢万里视频通话",
+
+      // 文本聊天框
+      tltData: "",
+      dialogVisible: false,
+      input: "", //发送框
+      iconShow: true,
+      // 文本消息功能
+      url: "ws://192.168.1.17:12345/api/api_gateway?method=control_center.real_time.im_message",
+      websock: null,
+      getUserObj: {}, // 获取到当前点击的行数据
+      msgList: [], //当前点击的账号消息列表
+      userName: "",
+    };
   },
   //监听属性 类似于data概念
   computed: {},
   //监控data中的数据变化
-  watch: {},
+  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) {
+        this.msgList = [];
+      }
+    },
+
+    // 音视频弹框
+    videoVisible(val) {
+      if (val == false) {
+        this.notPlugFlow(); // 停止推流
+        this.notTensile(); // 停止拉流
+        this.notLogin(); //退出房间
+      }
+    },
+  },
   //方法集合
-  methods: {},
+  methods: {
+    //动态获取浏览器高度
+    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.userListData();
+    },
+
+    // 分页
+    changeList(page) {},
+
+    // 获取左侧组织列表
+    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: "",
+        }),
+      })
+        .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]; // 左侧组织列表
+          }
+        })
+        .catch((err) => {
+          console.log(err);
+        });
+    },
+
+    // 获取右侧用户列表
+    userListData() {
+      this.$axios({
+        method: "POST",
+        url: "/api/api_gateway?method=sysmenage.usermanager.user_list",
+        data: this.qs.stringify({
+          page: this.page,
+          page_item: "10",
+          real_name: this.nameVal, //用户名称
+          mobile: this.phoneVal, // 电话
+          org_id: this.org_id,
+        }),
+      }).then((res) => {
+        if (res.data.data.page_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.page_item;
+        }
+      });
+    },
+
+    // 文本消息
+    msgAxios(data) {
+      this.getUserObj = data;
+      this.userName = localStorage.getItem("usernme");
+      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: "",
+        },
+      };
+      console.log(data);
+      this.websock.send(JSON.stringify(obj));
+      this.dialogVisible = true;
+    },
+
+    // 视频消息
+    videoAxios(data) {
+      this.getUserObj = data;
+      this.userName = localStorage.getItem("username");
+      this.videoTle = "正在和" + data.real_name + "视频通话";
+
+
+      // 先获取当前用户的房间号和登录所需的Token
+      var obj = {}
+      obj = {
+        action: 'send_video',
+        recv_user_id: data.user_id,
+        data: {
+          msg_status: false,
+          msg_info: '',
+        }
+      }
+      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
+      //   }
+      // });
+    },
+    // 音视频推流
+    async plugFlow() {
+      this.videoShow = false
+      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.zg.stopPublishingStream(this.streamID);
+      this.zg.destroyStream(this.localStream);
+    },
+    // 拉流
+    async tensile(streamID) {
+      const remoteStream = await this.zg.startPlayingStream(streamID);
+      // remoteVideo 为本地 <video> 或 <audio> 对象
+      this.$refs["remoteVideo"].srcObject = remoteStream;
+    },
+    notTensile(streamID) {
+      this.zg.stopPlayingStream(streamID);
+    },
+    // 退出房间
+    notLogin() {
+      this.zg.logoutRoom(this.RoomID);
+      this.videoVisible = false
+    },
+
+    // 当音视频通话关闭时的回调
+    handleClose(done) {
+      this.$confirm("目前正在音视频通话中,确认关闭?")
+        .then((_) => {
+          done();
+        })
+        .catch((_) => {});
+    },
+
+    // 发送消息
+    async submit() {
+      if (this.input.split(" ").join("").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 = "";
+        } catch (error) {
+          console.log(">>> sendMsg, error: ", error);
+        }
+      }
+
+      // var obj = {};
+      // obj = {
+      //   action: "send",
+      //   recv_user_id: "167",
+      //   data: {
+      //     msg_status: false,
+      //     msg_info: this.input,
+      //   },
+      // };
+      // var v = JSON.stringify(obj);
+      // this.websock.send(v);
+    },
+
+    // 取消回车换行行为
+    pushKeyword(event) {
+      if (event.keyCode === 13) {
+        event.preventDefault(); // 阻止浏览器默认换行操作
+        this.keyword = "";
+        return false;
+      }
+    },
+
+    // 更多消息
+    moreMsg() {},
+
+    // 文本消息功能初始化
+    msgInit() {
+      if (typeof WebSocket === "undefined") {
+        alert("您的浏览器不支持socket!");
+      } else {
+        console.log(location.host);
+        console.log(window.WebSocket);
+        this.websock = new window.WebSocket(
+          this.url + "&token=" + localStorage.getItem("session")
+        );
+        // window.s = this.websock
+
+        this.websock.onopen = (event) => {
+          console.log("WebSocket:已连接");
+          console.log(event);
+        };
+        this.websock.onmessage = (event) => {
+          console.log("WebSocket:消息");
+          // console.log(JSON.parse(event.data));
+          var data = JSON.parse(event.data);
+          if (data.action == "none") {
+            // 获取聊天记录
+            console.log(data.data[0]);
+            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") {
+            // 返回list为发送消息成功后需要再次调用聊天列表
+            var obj = {};
+            obj = {
+              action: "list",
+              recv_user_id: this.getUserObj.user_id,
+              data: {
+                msg_status: false,
+                msg_info: "",
+              },
+            };
+            this.websock.send(JSON.stringify(obj));
+          } else if (data.action == 'recv_video') {
+            // 获取当前点击用户的房间号以及登录房间所需的Token
+            console.log(JSON.parse(event.data));
+          }
+        };
+        this.websock.onerror = (event) => {
+          console.log("WebSocket:发生错误 ");
+          console.log(event);
+        };
+        this.websock.onclose = (event) => {
+          console.log("WebSocket:已关闭");
+          console.log(event);
+        };
+      }
+    },
+
+    // 音视频消息功能初始化
+    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("房间连接成功");
+          }
+          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("有其他用户进出房间");
+      });
+
+      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") {
+            // 流新增,开始拉流
+            this.tensile(streamList[0].streamID);
+          } else if (updateType == "DELETE") {
+            // 流删除,停止拉流
+            this.notTensile(streamList[0].streamID);
+          }
+        }
+      );
+
+      this.zg.on("publisherStateUpdate", (result) => {
+        // 推流状态更新回调
+        console.log("推流状态更新");
+      });
+
+      this.zg.on("publishQualityUpdate", (streamID, stats) => {
+        // 推流质量回调
+        console.log("推流质量更新");
+      });
+
+      // this.zg.on("IMRecvBroadcastMessage", (roomID, chatData) => {
+      //   console.log("IMRecvBroadcastMessage", roomID, chatData);
+      //   console.log(">>>>>>>>>>>>>>>>>>>>>接收到文本消息了");
+      //   var list = {
+      //     id: "2", //模拟数据中使用id来区分当前是发送人还是接收人  1为接收人  2为发送人
+      //     msg: chatData[0].message, // 文本消息
+      //     img: "../../../static/img/6.png", // 头像url
+      //   };
+      //   this.msgList = [...this.msgList, list];
+      //   // let message = {
+      //   //     ID: 'zego' + chatData[0].fromUser.userID + chatData[0].sendTime,
+      //   //     name: chatData[0].fromUser.userName,
+      //   //     time: format(chatData[0].sendTime),
+      //   //     content: chatData[0].message + '(广播发送)'
+      //   // }
+      // });
+    },
+    // 检测是否兼容当前浏览器
+    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("不兼容");
+      }
+    },
+  },
   //生命周期 - 创建完成(可以访问当前this实例)
   created() {},
   //生命周期 - 挂载完成(可以访问DOM元素)
-  mounted() {},
+  mounted() {
+    this.organizationData(); //获取左侧组织列表
+    this.userListData(); // 获取右侧用户列表
+
+    this.msgInit(); // 文本消息功能初始化
+    this.videoInit(); // 视频消息功能初始化
+  },
   beforeCreate() {}, //生命周期 - 创建之前
   beforeMount() {}, //生命周期 - 挂载之前
   beforeUpdate() {}, //生命周期 - 更新之前
@@ -34,4 +793,190 @@ export default {
 };
 </script>
 <style lang='less' scoped>
+.realTime_box {
+  .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;
+
+      // 搜索
+      .search_box {
+        display: flex;
+        /deep/.el-input {
+          width: 20%;
+          margin: 0 15px 0 0;
+        }
+        .btn_box {
+        }
+      }
+      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;
+    // margin: 15px 0 0 0;
+    background: #2c2c2c;
+    height: 325px;
+    position: relative;
+
+    // 主视频画面
+    .host_video {
+      width: 100%;
+      height: 300px;
+      display: table-cell;
+      vertical-align: middle;
+      text-align: center;
+      img {
+        margin: 0 atuo;
+      }
+    }
+
+    // 副视频画面
+    .deputy_video {
+      width: 90px;
+      height: 90px;
+      position: absolute;
+      top: 0;
+      right: 0;
+      background: #fff;
+    }
+
+    // 操作
+    .operate {
+      height: 25px;
+      background: #000;
+      padding: 5px 0 5px 0;
+      margin: -13px 0 0 0px;
+      display: flex;
+      justify-content: flex-end;
+    }
+  }
+
+  /* 聊天内容 */
+  .ul_list {
+    height: 350px;
+    // margin-left: -41px;
+    overflow: hidden;
+    overflow-y: auto;
+  }
+
+  .list_msgBox1 {
+    display: flex;
+    margin-top: 22px;
+    justify-content: end;
+  }
+  .list_msgBox2 {
+    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;
+  }
+  /deep/.el-textarea__inner {
+    border: 0;
+    border-top: 1px solid #dcdfe6;
+    border-radius: 0;
+    height: 95px;
+  }
+
+  // /deep/.el-dialog__body {
+  //   padding: 0;
+  // }
+}
 </style>

+ 5 - 4
minggao/src/page/systemmanger/userManger.vue

@@ -200,6 +200,7 @@
               value: 'id',
               emitPath: false,
               children: 'childrens',
+              checkStrictly: true,
             }"
             clearable
           ></el-cascader>
@@ -216,6 +217,7 @@
               label: 'role_name',
               multiple: true,
               emitPath: false,
+              checkStrictly: true,
             }"
             clearable
           ></el-cascader>
@@ -311,9 +313,7 @@ export default {
         user: "", //账号
         password: "", // 密码
         name: "", // 姓名
-        judge: [
-          [3, 4, 7],
-        ], // 隶属海关
+        judge: [], // 隶属海关
         role: [], //角色
         phone: "", // 手机号码
         mailbox: "", //邮箱
@@ -698,8 +698,9 @@ export default {
         .catch((err) => {});
     },
     judgeData(item) {
-      console.log(item);
+      // console.log(item);
       this.form.judge = item;
+      console.log(this.form.judge)
     },
     roleChange(item) {
       this.form.role = item;

BIN
minggao/static/images/realTime/6.png


BIN
minggao/static/images/realTime/7.png