Browse Source

学校设备管理socket对接

gzb
wangzhonglu 9 months ago
parent
commit
2ef457ff4b

+ 95
- 12341
package-lock.json
File diff suppressed because it is too large
View File


+ 3
- 2
package.json View File

8
     "report": "vue-cli-service build --report"
8
     "report": "vue-cli-service build --report"
9
   },
9
   },
10
   "dependencies": {
10
   "dependencies": {
11
-    "vue-simple-uploader": "^0.7.6",
11
+    "md5": "^2.3.0",
12
     "qrcode": "^1.5.1",
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
   "devDependencies": {
16
   "devDependencies": {
16
     "@babel/core": "~7.12.16",
17
     "@babel/core": "~7.12.16",

+ 2
- 0
public/config.js View File

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

+ 76
- 0
src/utils/ControlWSMsg.js View File

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 View File

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 View File

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

+ 4
- 3
src/views/schoolSection/deviceManage/breakRuleDevice.vue View File

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

+ 74
- 2
src/views/schoolSection/deviceManage/deviceManage.vue View File

63
               其他操作<Icon type="ios-arrow-down" class="arrow_down" />
63
               其他操作<Icon type="ios-arrow-down" class="arrow_down" />
64
             </div>
64
             </div>
65
             <DropdownMenu slot="list">
65
             <DropdownMenu slot="list">
66
-              <DropdownItem name="1">恢复出厂设置</DropdownItem>
66
+              <DropdownItem name="1">恢复出厂</DropdownItem>
67
               <DropdownItem name="2">发送消息</DropdownItem>
67
               <DropdownItem name="2">发送消息</DropdownItem>
68
-              <DropdownItem name="3">整班发送消息</DropdownItem>
68
+              <DropdownItem name="3" v-if="searchForm.classid"
69
+                >整班发送消息</DropdownItem
70
+              >
69
               <DropdownItem name="4">解除限制</DropdownItem>
71
               <DropdownItem name="4">解除限制</DropdownItem>
70
               <DropdownItem name="5">限制使用</DropdownItem>
72
               <DropdownItem name="5">限制使用</DropdownItem>
71
               <DropdownItem name="6">更新策略</DropdownItem>
73
               <DropdownItem name="6">更新策略</DropdownItem>
112
 </template>
114
 </template>
113
 
115
 
114
 <script>
116
 <script>
117
+import controlWs from "@/utils/ControlWs";
118
+import ControlWSMsg from "@/utils/ControlWSMsg";
115
 import { exportToExcel } from "@/utils/exportToExcel";
119
 import { exportToExcel } from "@/utils/exportToExcel";
116
 import { class_list } from "@/api/school";
120
 import { class_list } from "@/api/school";
117
 import {
121
 import {
122
 export default {
126
 export default {
123
   data() {
127
   data() {
124
     return {
128
     return {
129
+      userInfo: {},
130
+      // ws连接后回发信息
131
+      wsEnterInfo: {
132
+        // 消息ID
133
+        mid: null,
134
+        // 是否进入
135
+        isEnter: false
136
+      },
125
       searchForm: {
137
       searchForm: {
126
         classid: null,
138
         classid: null,
127
         atype: 0,
139
         atype: 0,
202
     };
214
     };
203
   },
215
   },
204
   created() {
216
   created() {
217
+    this.userInfo = JSON.parse(
218
+      localStorage.getItem("xh_control_userInfo")
219
+    ).content;
205
     this.getClassList();
220
     this.getClassList();
221
+    this.initMonitorWS();
206
   },
222
   },
207
   computed: {
223
   computed: {
208
     powerParams() {
224
     powerParams() {
209
       return this.$store.getters.powerParams;
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
   methods: {
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
     classChange(classid) {
273
     classChange(classid) {
214
       this.searchForm.classid = classid;
274
       this.searchForm.classid = classid;
215
       this.searchList();
275
       this.searchList();
356
     },
416
     },
357
     // 其他操作
417
     // 其他操作
358
     dropdownSelected(name) {
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
       if (name === "1") {
424
       if (name === "1") {
360
         // 恢复出厂设置
425
         // 恢复出厂设置
361
         if (this.tableSelection.length === 0) {
426
         if (this.tableSelection.length === 0) {
362
           this.$Message.error("请选择列表");
427
           this.$Message.error("请选择列表");
363
           return;
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
       } else if (name === "2") {
437
       } else if (name === "2") {
366
         // 发送消息
438
         // 发送消息
367
         if (this.tableSelection.length === 0) {
439
         if (this.tableSelection.length === 0) {

Loading…
Cancel
Save