Selaa lähdekoodia

学校设备管理socket对接

gzb
wangzhonglu 9 kuukautta sitten
vanhempi
commit
2ef457ff4b

+ 95
- 12341
package-lock.json
File diff suppressed because it is too large
Näytä tiedosto


+ 3
- 2
package.json Näytä tiedosto

@@ -8,9 +8,10 @@
8 8
     "report": "vue-cli-service build --report"
9 9
   },
10 10
   "dependencies": {
11
-    "vue-simple-uploader": "^0.7.6",
11
+    "md5": "^2.3.0",
12 12
     "qrcode": "^1.5.1",
13
-    "qrcodejs2": "^0.0.2"
13
+    "qrcodejs2": "^0.0.2",
14
+    "vue-simple-uploader": "^0.7.6"
14 15
   },
15 16
   "devDependencies": {
16 17
     "@babel/core": "~7.12.16",

+ 2
- 0
public/config.js Näytä tiedosto

@@ -5,6 +5,7 @@ window._config = window.isTest
5 5
       baseUrl: "https://mcapitest.xhkjedu.com/",
6 6
       baseImageUrl: "https://mcapitest.xhkjedu.com/",
7 7
       showImageUrl: "https://mcapitest.xhkjedu.com/",
8
+      wsUrl: "wss://mcwstest.xhkjedu.com/ws",
8 9
       axiosApiTimeout: 60, // 接口超时时间 单位秒
9 10
       axiosFileTimeout: 120, // 上传文件超时时间 单位秒
10 11
       // 开发者
@@ -19,6 +20,7 @@ window._config = window.isTest
19 20
       baseUrl: "https://mcapi.xhkjedu.com/",
20 21
       baseImageUrl: "https://mcapi.xhkjedu.com/",
21 22
       showImageUrl: "https://mcapi.xhkjedu.com/",
23
+      wsUrl: "wss://mcws.xhkjedu.com/ws",
22 24
       axiosApiTimeout: 20, // 接口超时时间 单位秒
23 25
       axiosFileTimeout: 30, // 上传文件超时时间 单位秒
24 26
       // 开发者

+ 76
- 0
src/utils/ControlWSMsg.js Näytä tiedosto

@@ -0,0 +1,76 @@
1
+import md5 from "md5";
2
+class ControlWSMsg {
3
+  constructor(ws, fid, tids = []) {
4
+    // 消息ID
5
+    this.mid = URL.createObjectURL(new Blob()).substr(-36).replace(/-/g, "");
6
+    // 精确到秒
7
+    this.timeunix = Math.floor(Date.now() / 1000);
8
+    // 发送者ID
9
+    this.fid = String(fid);
10
+    // 接收收者数组 空数组则发送给其他所有人
11
+    this.tids = tids;
12
+    this.ws = ws;
13
+  }
14
+  // 连接后回发(确认)
15
+  afterConnection(body) {
16
+    this.code = 1001;
17
+    // {userid, username, schoolid, usertype(1管理员, 2学生)}
18
+    this.body = body;
19
+  }
20
+  // 获取在线用户列表
21
+  onlineUsers(body) {
22
+    this.code = 1002;
23
+    // {schoolid}
24
+    this.body = body;
25
+  }
26
+  // 发送消息
27
+  msgContent(body) {
28
+    this.code = 2001;
29
+    // {userid, username, msg}
30
+    this.body = body;
31
+  }
32
+  // 限制使用
33
+  limitedUse() {
34
+    this.code = 2002;
35
+  }
36
+  // 解除限制
37
+  removeLimit() {
38
+    this.code = 2003;
39
+  }
40
+  // 更新策略
41
+  updateStrategy() {
42
+    this.code = 2004;
43
+  }
44
+  // 重启设备
45
+  rebootDevice() {
46
+    this.code = 2005;
47
+  }
48
+  // 恢复出厂
49
+  restoreFactory() {
50
+    this.code = 2006;
51
+  }
52
+  toJSONString() {
53
+    let tempObj = {};
54
+    for (let key in this) {
55
+      if (key !== "ws") {
56
+        tempObj[key] = this[key];
57
+      }
58
+    }
59
+    return JSON.stringify(tempObj);
60
+  }
61
+  send(cb) {
62
+    // 计算方式(md5(md5(code_mid_timeunix)))
63
+    let code_mid_timeunix = `${this.code}_${this.mid}_${this.timeunix}`;
64
+    this.sign = md5(md5(code_mid_timeunix));
65
+    let ws = this.ws;
66
+    if (ws && ws.readyState === 1) {
67
+      let msg = this.toJSONString();
68
+      console.log("发送消息", msg);
69
+      ws.send(msg);
70
+      cb && cb(true);
71
+    } else {
72
+      cb && cb(false);
73
+    }
74
+  }
75
+}
76
+export default ControlWSMsg;

+ 99
- 0
src/utils/ControlWs.js Näytä tiedosto

@@ -0,0 +1,99 @@
1
+import Vue from "vue";
2
+class ControlWs {
3
+  constructor() {
4
+    this.wsUrl = window._config.wsUrl;
5
+    // 心跳计时器
6
+    this.timer = null;
7
+    // ws实例对象
8
+    this.ws = null;
9
+    // 避免ws重复连接
10
+    this.lockReconnect = false;
11
+    // 可以继续连接 false 不能再连接 true 可以请求连接
12
+    this.needreconnect = true;
13
+    this.myevent = new Vue();
14
+  }
15
+  setWs() {
16
+    if (this.wsUrl) {
17
+      this.needreconnect = true;
18
+      this.createWebSocket(); //连接ws
19
+    }
20
+  }
21
+  closeWs() {
22
+    if (this.ws) {
23
+      this.needreconnect = false;
24
+      this.ws.close();
25
+      this.myevent.$off("ws_error");
26
+      this.myevent.$off("ws_open");
27
+      this.myevent.$off("ws_getmsg");
28
+      if (this.timer) {
29
+        clearInterval(this.timer);
30
+      }
31
+    }
32
+  }
33
+  createWebSocket() {
34
+    try {
35
+      if ("WebSocket" in window) {
36
+        this.ws = new WebSocket(this.wsUrl);
37
+      } else {
38
+        alert(
39
+          "您的浏览器不支持websocket协议,建议使用新版谷歌、火狐等浏览器,请勿使用IE10以下浏览器,360浏览器请使用极速模式,不要使用兼容模式!"
40
+        );
41
+      }
42
+      this.initEventHandle();
43
+    } catch (e) {
44
+      this.reconnect();
45
+    }
46
+  }
47
+  initEventHandle() {
48
+    this.ws.onclose = () => {
49
+      console.log("ws连接关闭!");
50
+      if (this.needreconnect) {
51
+        this.reconnect();
52
+      }
53
+    };
54
+    this.ws.onerror = () => {
55
+      console.error("ws连接错误!");
56
+      if (this.timer) {
57
+        clearInterval(this.timer);
58
+      }
59
+      if (this.needreconnect === true) {
60
+        this.myevent.$emit("ws_error");
61
+        this.reconnect();
62
+      }
63
+    };
64
+    this.ws.onopen = () => {
65
+      if (this.timer) {
66
+        clearInterval(this.timer);
67
+      }
68
+      this.timer = setInterval(() => {
69
+        let blob = new Blob(["ping"], { type: "text/plain" });
70
+        if (this.ws && this.ws.readyState === 1) {
71
+          this.ws.send(blob);
72
+        } else {
73
+          clearInterval(this.timer);
74
+        }
75
+      }, 1000 * 10);
76
+      console.log("ws连接成功!");
77
+      this.myevent.$emit("ws_open");
78
+    };
79
+    this.ws.onmessage = (event) => {
80
+      //如果获取到消息,心跳检测重置
81
+      if (event.data && JSON.parse(event.data)) {
82
+        console.info("接收到消息", event.data);
83
+        this.myevent.$emit("ws_getmsg", JSON.parse(event.data));
84
+      } else {
85
+        console.error(event.data);
86
+      }
87
+    };
88
+  }
89
+  reconnect() {
90
+    if (this.lockReconnect) return;
91
+    this.lockReconnect = true;
92
+    setTimeout(() => {
93
+      //没连接上会一直重连,设置延迟避免请求过多
94
+      this.createWebSocket();
95
+      this.lockReconnect = false;
96
+    }, 2000);
97
+  }
98
+}
99
+export default new ControlWs();

+ 14
- 4
src/views/schoolSection/applicationStrategy/appStrategyTemplate.vue Näytä tiedosto

@@ -60,7 +60,11 @@
60 60
       title="授权"
61 61
     >
62 62
       <div class="strategy_name">{{ empowerInfo.name }}</div>
63
-      <div class="school_name">{{ schoolInfo.schoolname }}</div>
63
+      <div class="school_name">
64
+        <Checkbox v-model="empowerInfo.school">{{
65
+          schoolInfo.schoolname
66
+        }}</Checkbox>
67
+      </div>
64 68
       <CheckboxGroup v-model="empowerInfo.classids">
65 69
         <Checkbox
66 70
           :label="classItem.id"
@@ -102,6 +106,7 @@ export default {
102 106
         show: false,
103 107
         name: "",
104 108
         stappid: null,
109
+        school: false,
105 110
         classids: []
106 111
       },
107 112
       schoolInfo: {
@@ -263,23 +268,28 @@ export default {
263 268
         show: true,
264 269
         name: row.name,
265 270
         stappid: row.stappid,
271
+        school: false,
266 272
         classids: []
267 273
       };
268 274
       this.getClassList();
269 275
     },
270 276
     // 保存授权
271 277
     saveEmpowerInfo() {
272
-      if (this.empowerInfo.classids.length === 0) {
278
+      if (this.empowerInfo.classids.length === 0 && !this.empowerInfo.school) {
273 279
         this.$Message.error("请选择班级");
274 280
         return;
275 281
       }
276
-      stApp_add_empower({
282
+      let form = {
277 283
         // rtype: this.powerParams.rtype,
278 284
         // objectid: this.powerParams.objectid,
279 285
         // schoolid: this.powerParams.objectid,
280 286
         stappid: this.empowerInfo.stappid,
281 287
         classids: this.empowerInfo.classids
282
-      }).then((data) => {
288
+      };
289
+      if (this.empowerInfo.school) {
290
+        form.schoolid = this.powerParams.objectid;
291
+      }
292
+      stApp_add_empower(form).then((data) => {
283 293
         if (data.code === 0) {
284 294
           this.empowerInfo.show = false;
285 295
           this.$Message.success(data.msg);

+ 4
- 3
src/views/schoolSection/deviceManage/breakRuleDevice.vue Näytä tiedosto

@@ -8,6 +8,7 @@
8 8
           placeholder="关注状态"
9 9
           @on-change="searchList()"
10 10
         >
11
+          <Option :value="-1">全部</Option>
11 12
           <Option :value="0">未处理</Option>
12 13
           <Option :value="1">已处理</Option>
13 14
         </Select>
@@ -25,13 +26,13 @@
25 26
             其他操作<Icon type="ios-arrow-down" class="arrow_down" />
26 27
           </div>
27 28
           <DropdownMenu slot="list">
28
-            <DropdownItem name="1">恢复出厂设置</DropdownItem>
29
+            <DropdownItem name="1">恢复出厂</DropdownItem>
29 30
             <DropdownItem name="2">发送消息</DropdownItem>
30 31
             <DropdownItem name="3">解除限制</DropdownItem>
31 32
             <DropdownItem name="4">限制使用</DropdownItem>
32 33
             <DropdownItem name="5">更新策略</DropdownItem>
33 34
             <DropdownItem name="6">重启设备</DropdownItem>
34
-            <DropdownItem name="7">合处理</DropdownItem>
35
+            <DropdownItem name="7">合处理</DropdownItem>
35 36
           </DropdownMenu>
36 37
         </Dropdown>
37 38
       </div>
@@ -75,7 +76,7 @@ export default {
75 76
       searchForm: {
76 77
         name: "",
77 78
         // 是否处理:0否 1是
78
-        handled: 0,
79
+        handled: -1,
79 80
         page: 1,
80 81
         size: 10,
81 82
         list: [],

+ 74
- 2
src/views/schoolSection/deviceManage/deviceManage.vue Näytä tiedosto

@@ -63,9 +63,11 @@
63 63
               其他操作<Icon type="ios-arrow-down" class="arrow_down" />
64 64
             </div>
65 65
             <DropdownMenu slot="list">
66
-              <DropdownItem name="1">恢复出厂设置</DropdownItem>
66
+              <DropdownItem name="1">恢复出厂</DropdownItem>
67 67
               <DropdownItem name="2">发送消息</DropdownItem>
68
-              <DropdownItem name="3">整班发送消息</DropdownItem>
68
+              <DropdownItem name="3" v-if="searchForm.classid"
69
+                >整班发送消息</DropdownItem
70
+              >
69 71
               <DropdownItem name="4">解除限制</DropdownItem>
70 72
               <DropdownItem name="5">限制使用</DropdownItem>
71 73
               <DropdownItem name="6">更新策略</DropdownItem>
@@ -112,6 +114,8 @@
112 114
 </template>
113 115
 
114 116
 <script>
117
+import controlWs from "@/utils/ControlWs";
118
+import ControlWSMsg from "@/utils/ControlWSMsg";
115 119
 import { exportToExcel } from "@/utils/exportToExcel";
116 120
 import { class_list } from "@/api/school";
117 121
 import {
@@ -122,6 +126,14 @@ import {
122 126
 export default {
123 127
   data() {
124 128
     return {
129
+      userInfo: {},
130
+      // ws连接后回发信息
131
+      wsEnterInfo: {
132
+        // 消息ID
133
+        mid: null,
134
+        // 是否进入
135
+        isEnter: false
136
+      },
125 137
       searchForm: {
126 138
         classid: null,
127 139
         atype: 0,
@@ -202,14 +214,62 @@ export default {
202 214
     };
203 215
   },
204 216
   created() {
217
+    this.userInfo = JSON.parse(
218
+      localStorage.getItem("xh_control_userInfo")
219
+    ).content;
205 220
     this.getClassList();
221
+    this.initMonitorWS();
206 222
   },
207 223
   computed: {
208 224
     powerParams() {
209 225
       return this.$store.getters.powerParams;
210 226
     }
211 227
   },
228
+  beforeDestroy() {
229
+    controlWs.myevent.$off("ws_error");
230
+    controlWs.myevent.$off("ws_open");
231
+    controlWs.myevent.$off("ws_getmsg");
232
+    controlWs.closeWs();
233
+  },
212 234
   methods: {
235
+    initMonitorWS() {
236
+      controlWs.setWs();
237
+      this.getWSEventInfo();
238
+    },
239
+    getWSEventInfo() {
240
+      controlWs.myevent.$on("ws_error", () => {
241
+        this.$Message.error("网络异常,请检查网络");
242
+      });
243
+      controlWs.myevent.$on("ws_open", () => {
244
+        this.sendAfterConnection();
245
+      });
246
+      controlWs.myevent.$on("ws_getmsg", (data) => {
247
+        if (data.code === 666) {
248
+          if (this.wsEnterInfo.mid === data.mid) {
249
+            this.wsEnterInfo.isEnter = true;
250
+          }
251
+        }
252
+        if (!this.wsEnterInfo.isEnter) {
253
+          return;
254
+        }
255
+      });
256
+    },
257
+    // 连接后回发(需确认)
258
+    sendAfterConnection() {
259
+      this.wsEnterInfo.isEnter = false;
260
+      let controlWSMsg = new ControlWSMsg(controlWs.ws, this.userInfo.adminid);
261
+      controlWSMsg.afterConnection({
262
+        userid: String(this.userInfo.adminid),
263
+        username: this.userInfo.aname,
264
+        schoolid: this.powerParams.objectid,
265
+        usertype: 1
266
+      });
267
+      controlWSMsg.send((isSend) => {
268
+        if (isSend) {
269
+          this.wsEnterInfo.mid = controlWSMsg.mid;
270
+        }
271
+      });
272
+    },
213 273
     classChange(classid) {
214 274
       this.searchForm.classid = classid;
215 275
       this.searchList();
@@ -356,12 +416,24 @@ export default {
356 416
     },
357 417
     // 其他操作
358 418
     dropdownSelected(name) {
419
+      if (!this.wsEnterInfo.isEnter) {
420
+        this.$Message.error("推送服务不可用");
421
+        return;
422
+      }
423
+      let snList = this.tableSelection.map((row) => row.sn).filter(Boolean);
359 424
       if (name === "1") {
360 425
         // 恢复出厂设置
361 426
         if (this.tableSelection.length === 0) {
362 427
           this.$Message.error("请选择列表");
363 428
           return;
364 429
         }
430
+        let controlWSMsg = new ControlWSMsg(
431
+          controlWs.ws,
432
+          this.userInfo.adminid,
433
+          snList
434
+        );
435
+        controlWSMsg.restoreFactory();
436
+        controlWSMsg.send();
365 437
       } else if (name === "2") {
366 438
         // 发送消息
367 439
         if (this.tableSelection.length === 0) {

Loading…
Peruuta
Tallenna