From d52c5bb169e770c5e3d45301348d4b93f1443676 Mon Sep 17 00:00:00 2001 From: SunTao <328867980@qq.com> Date: Fri, 29 Nov 2024 15:34:10 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9Awebscoket=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E6=94=B9=E4=BF=A1=E6=81=AF=E4=B8=AD=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/xj/inspection/center.js | 34 +++ src/main.js | 125 ++++---- src/plugins/websocket.js | 270 ++++-------------- src/views/big-screen/index.vue | 127 ++++---- .../inspection-warn/warning-center/index.vue | 128 +++++---- vue.config.js | 4 +- 6 files changed, 292 insertions(+), 396 deletions(-) create mode 100644 src/api/xj/inspection/center.js diff --git a/src/api/xj/inspection/center.js b/src/api/xj/inspection/center.js new file mode 100644 index 0000000..4040817 --- /dev/null +++ b/src/api/xj/inspection/center.js @@ -0,0 +1,34 @@ +/* + * @Author: SunTao 328867980@qq.com + * @Date: 2024-11-28 10:26:24 + * @LastEditors: SunTao 328867980@qq.com + * @LastEditTime: 2024-11-28 10:26:24 + * @FilePath: \znxjxt-ui\src\api\xj\inspection\center.js + * @Description: 巡检中心-预警中心接口 + */ +import request from "@/utils/request"; + +// 获取预警中心列表 +export function getWarningList(query) { + return request({ + url: "/notice/list", + method: "get", + params: query, + }); +} + +// 获取预警中心导航栏 +export function getWarningNav() { + return request({ + url: "/notice/unreadCount", + method: "get", + }); +} + +// 根据id标记通知为已读 +export function markNoticeRead(id) { + return request({ + url: `/notice/read/${id}`, + method: "post", + }); +} diff --git a/src/main.js b/src/main.js index fadbc9b..9c5b2af 100644 --- a/src/main.js +++ b/src/main.js @@ -1,72 +1,83 @@ -import Vue from 'vue' +import Vue from "vue"; -import Cookies from 'js-cookie' +import Cookies from "js-cookie"; -import Element from 'element-ui' -import './assets/styles/element-variables.scss' +import Element from "element-ui"; +import "./assets/styles/element-variables.scss"; -import '@/assets/styles/index.scss' // global css -import '@/assets/styles/ruoyi.scss' // ruoyi css -import App from './App' -import store from './store' -import router from './router' -import directive from './directive' // directive -import plugins from './plugins' // plugins -import { download } from '@/utils/request' +import "@/assets/styles/index.scss"; // global css +import "@/assets/styles/ruoyi.scss"; // ruoyi css +import App from "./App"; +import store from "./store"; +import router from "./router"; +import directive from "./directive"; // directive +import plugins from "./plugins"; // plugins +import { download } from "@/utils/request"; -import './assets/icons' // icon -import './permission' // permission control +import "./assets/icons"; // icon +import "./permission"; // permission control import { getDicts } from "@/api/system/dict/data"; import { getConfigKey } from "@/api/system/config"; -import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi"; +import { + parseTime, + resetForm, + addDateRange, + selectDictLabel, + selectDictLabels, + handleTree, +} from "@/utils/ruoyi"; // 分页组件 import Pagination from "@/components/Pagination"; // 自定义表格工具组件 -import RightToolbar from "@/components/RightToolbar" +import RightToolbar from "@/components/RightToolbar"; // 富文本组件 -import Editor from "@/components/Editor" +import Editor from "@/components/Editor"; // 文件上传组件 -import FileUpload from "@/components/FileUpload" +import FileUpload from "@/components/FileUpload"; // 图片上传组件 -import ImageUpload from "@/components/ImageUpload" +import ImageUpload from "@/components/ImageUpload"; // 图片预览组件 -import ImagePreview from "@/components/ImagePreview" +import ImagePreview from "@/components/ImagePreview"; // 字典标签组件 -import DictTag from '@/components/DictTag' +import DictTag from "@/components/DictTag"; // 头部标签组件 -import VueMeta from 'vue-meta' +import VueMeta from "vue-meta"; // 字典数据组件 -import DictData from '@/components/DictData' +import DictData from "@/components/DictData"; +import { getToken } from "@/utils/auth.js"; //工作流 -import Tinymce from '@/components/tinymce/index.vue' -import "./assets/text/font.css" +import Tinymce from "@/components/tinymce/index.vue"; +import "./assets/text/font.css"; +import WebSocketService from "@/plugins/websocket"; +const wsService = new WebSocketService(); // 全局方法挂载 -Vue.prototype.getDicts = getDicts -Vue.prototype.getConfigKey = getConfigKey -Vue.prototype.parseTime = parseTime -Vue.prototype.resetForm = resetForm -Vue.prototype.addDateRange = addDateRange -Vue.prototype.selectDictLabel = selectDictLabel -Vue.prototype.selectDictLabels = selectDictLabels -Vue.prototype.download = download -Vue.prototype.handleTree = handleTree +Vue.prototype.getDicts = getDicts; +Vue.prototype.getConfigKey = getConfigKey; +Vue.prototype.parseTime = parseTime; +Vue.prototype.resetForm = resetForm; +Vue.prototype.addDateRange = addDateRange; +Vue.prototype.selectDictLabel = selectDictLabel; +Vue.prototype.selectDictLabels = selectDictLabels; +Vue.prototype.download = download; +Vue.prototype.handleTree = handleTree; +Vue.prototype.$ws = wsService; // 全局组件挂载 -Vue.component('DictTag', DictTag) -Vue.component('Pagination', Pagination) -Vue.component('RightToolbar', RightToolbar) -Vue.component('Editor', Editor) -Vue.component('FileUpload', FileUpload) -Vue.component('ImageUpload', ImageUpload) -Vue.component('ImagePreview', ImagePreview) -Vue.component('tinymce',Tinymce) +Vue.component("DictTag", DictTag); +Vue.component("Pagination", Pagination); +Vue.component("RightToolbar", RightToolbar); +Vue.component("Editor", Editor); +Vue.component("FileUpload", FileUpload); +Vue.component("ImageUpload", ImageUpload); +Vue.component("ImagePreview", ImagePreview); +Vue.component("tinymce", Tinymce); -Vue.use(directive) -Vue.use(plugins) -Vue.use(VueMeta) -DictData.install() +Vue.use(directive); +Vue.use(plugins); +Vue.use(VueMeta); +DictData.install(); /** * If you don't want to use mock-server @@ -78,14 +89,24 @@ DictData.install() */ Vue.use(Element, { - size: Cookies.get('size') || 'medium' // set element-ui default size -}) + size: Cookies.get("size") || "medium", // set element-ui default size +}); -Vue.config.productionTip = false +Vue.config.productionTip = false; new Vue({ - el: '#app', + el: "#app", router, store, - render: h => h(App) -}) + render: (h) => h(App), +}); +// 确保WebSocket连接在获取到token时建立 +store.watch( + () => store.getters.token, + (newToken) => { + if (newToken) { + wsService.connect(`ws://192.168.1.188:8080/websocket?token=${newToken}`); + } + }, + { immediate: true } +); diff --git a/src/plugins/websocket.js b/src/plugins/websocket.js index f5f467c..a08dd74 100644 --- a/src/plugins/websocket.js +++ b/src/plugins/websocket.js @@ -1,229 +1,61 @@ -/* - * @Author: SunTao 328867980@qq.com - * @Date: 2024-11-13 17:13:54 - * @LastEditors: SunTao 328867980@qq.com - * @LastEditTime: 2024-11-13 17:14:13 - * @FilePath: \znxjxt-ui\src\plugins\websocket.js - * @Description: websocket传输 - */ +// WebSocketService.js +class WebSocketService { + constructor() { + this.ws = null; + this.reconnectInterval = 3000; + this.eventListeners = {}; + } -// websocket实例 -let wsObj = null; -// ws连接地址 -let wsUrl = null; -// let userId = null; -// 是否执行重连 true/不执行 ; false/执行 -let lockReconnect = false; -// 重连定时器 -let wsCreateHandler = null; -// 连接成功,执行回调函数 -let messageCallback = null; -// 连接失败,执行回调函数 -let errorCallback = null; -// 发送给后台的数据 -let sendDatas = {}; - -/** - * 发起websocket请求函数 - * @param {string} url ws连接地址 - * @param {Object} agentData 传给后台的参数 - * @param {function} successCallback 接收到ws数据,对数据进行处理的回调函数 - * @param {function} errCallback ws连接错误的回调函数 - */ -export const connectWebsocket = (url, agentData, successCallback, errCallback) => { - wsUrl = url; - createWebSoket(); - messageCallback = successCallback; - errorCallback = errCallback; - sendDatas = agentData; -}; - -// 手动关闭websocket (这里手动关闭会执行onclose事件) -export const closeWebsocket = () => { - if (wsObj) { - writeToScreen("手动关闭websocket"); - wsObj.close(); // 关闭websocket - // wsObj.onclose() // 关闭websocket(如果上面的关闭不生效就加上这一条) - // 关闭重连 - lockReconnect = true; - wsCreateHandler && clearTimeout(wsCreateHandler); - // 关闭心跳检查 - heartCheck.stop(); - } -}; - -//向服务器端发送消息 -export const sendMsg = value => { - wsObj.send(JSON.stringify(value)); -}; - -// 创建ws函数 -const createWebSoket = () => { - //判断浏览器是否支持websocket - if (typeof WebSocket === "undefined") { - writeToScreen("您的浏览器不支持WebSocket,无法获取数据"); - return false; - } - - // const host = window.location.host; //获取端口 - // userId = GetQueryString("userId"); - // wsUrl = "ws://" + host + "/websoket" + userId; - - try { - wsObj = new WebSocket(wsUrl); - initWsEventHandle(); - } catch (e) { - writeToScreen("连接异常,开始重连"); - reconnect(); - } -}; - - -const initWsEventHandle = () => { - try { - // 连接成功 - wsObj.onopen = event => { - // heartCheck.start(); //开启心跳 - onWsOpen(event); //客户端与服务器端通信 + connect(url) { + this.ws = new WebSocket(url); + + this.ws.onopen = () => { + console.log('WebSocket connected'); + this.emit('open'); }; - - // 监听服务器端返回的信息 - wsObj.onmessage = event => { - onWsMessage(event); //接收数据,抛出 - // heartCheck.reset(); //重置心跳 + + this.ws.onmessage = (event) => { + this.emit('message', JSON.parse(event.data)); }; - - wsObj.onclose = event => { - writeToScreen("onclose执行关闭事件"); - onWsClose(event); //关闭事件 + + this.ws.onerror = (error) => { + console.error('WebSocket error:', error); + this.emit('error', error); }; - - wsObj.onerror = event => { - writeToScreen("onerror执行error事件,开始重连"); - onWsError(event); //error事件 - reconnect(); //重连 + + this.ws.onclose = () => { + console.log('WebSocket closed'); + this.emit('close'); + setTimeout(() => this.connect(url), this.reconnectInterval); }; - } catch (err) { - writeToScreen("绑定事件没有成功,开始重连"); - reconnect(); } -}; - -//open事件 WebSocket连接成功时触发 -const onWsOpen = event => { - // 客户端与服务器端通信 - // wsObj.send('我发送消息给服务端'); - // 添加状态判断,当为OPEN时,发送消息 - if (wsObj.readyState === wsObj.OPEN) { - // wsObj.OPEN = 1 - // 发给后端的数据需要字符串化 - wsObj.send(JSON.stringify(sendDatas)); + + send(data) { + if (this.ws && this.ws.readyState === WebSocket.OPEN) { + this.ws.send(JSON.stringify(data)); + } else { + console.error('WebSocket is not connected'); + } } - if (wsObj.readyState === wsObj.CLOSED) { - // wsObj.CLOSED = 3 - writeToScreen("wsObj.readyState=3, ws连接异常,开始重连"); - reconnect(); - errorCallback(event); + + on(event, callback) { + if (!this.eventListeners[event]) { + this.eventListeners[event] = []; + } + this.eventListeners[event].push(callback); } -}; - -//message事件 接收到WebSocket服务器发送的消息时触发 -const onWsMessage = event => { - const jsonStr = event.data; - // writeToScreen("onWsMessage接收到服务器的数据: ", jsonStr); - messageCallback(jsonStr); -}; - -//close事件 WebSocket连接关闭时触发 -const onWsClose = event => { - writeToScreen("DISCONNECT"); - // e.code === 1000 表示正常关闭。 无论为何目的而创建, 该链接都已成功完成任务。 - // e.code !== 1000 表示非正常关闭。 - console.log("onclose event: ", event); - if (event && event.code !== 1000) { - writeToScreen("非正常关闭"); - errorCallback(event); - // 如果不是手动关闭,这里的重连会执行;如果调用了手动关闭函数,这里重连不会执行 - reconnect(); + + emit(event, data) { + if (this.eventListeners[event]) { + this.eventListeners[event].forEach(callback => callback(data)); + } } -}; - -//error事件 WebSocket连接出错时触发 -const onWsError = event => { - writeToScreen("onWsError: ", event.data); - errorCallback(event);//抛出错误 -}; - -//封装console.log() -const writeToScreen = massage => { - console.log(massage); -}; - -// 重连函数 -const reconnect = () => { - if (lockReconnect) { - return; + + close() { + if (this.ws) { + this.ws.close(); + } } - writeToScreen("3秒后重连"); - lockReconnect = true; - // 没连接上会一直重连,设置延迟避免请求过多 - wsCreateHandler && clearTimeout(wsCreateHandler); - wsCreateHandler = setTimeout(() => { - writeToScreen("重连..." + wsUrl); - createWebSoket(); - lockReconnect = false; - writeToScreen("重连完成"); - }, 5000); -}; - -// 从浏览器地址中获取对应参数 -const GetQueryString = name => { - let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); - // 获取url中 ? 符后的字符串并正则匹配 - let r = window.location.search.substr(1).match(reg); - let context = ""; - r && (context = r[2]); - reg = null; - r = null; - return context; -}; - -// 心跳检查(看看websocket是否还在正常连接中) -let heartCheck = { - timeout: 15 * 1000, - timeoutObj: null, - serverTimeoutObj: null, - // 重启 - reset() { - clearTimeout(this.timeoutObj); - clearTimeout(this.serverTimeoutObj); - this.start(); - }, - // 停止 - stop() { - clearTimeout(this.timeoutObj); - clearTimeout(this.serverTimeoutObj); - }, - // 开启定时器 - start() { - this.timeoutObj && clearTimeout(this.timeoutObj); - this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj); - // 15s之内如果没有收到后台的消息,则认为是连接断开了,需要重连 - this.timeoutObj = setTimeout(() => { - writeToScreen("心跳检查,发送ping到后台"); - try { - const datas = { content: "心跳检测" }; - wsObj.send(JSON.stringify(datas)); - } catch (err) { - writeToScreen("发送ping异常,重连"); - reconnect(); - } - // console.log("内嵌定时器this.serverTimeoutObj: ", this.serverTimeoutObj); - // 内嵌定时器 - this.serverTimeoutObj = setTimeout(() => { - writeToScreen("没有收到后台的数据,重新连接"); - reconnect(); - }, this.timeout); - }, this.timeout); - }, -}; \ No newline at end of file +} + +export default WebSocketService; \ No newline at end of file diff --git a/src/views/big-screen/index.vue b/src/views/big-screen/index.vue index 3b03506..5dc6551 100644 --- a/src/views/big-screen/index.vue +++ b/src/views/big-screen/index.vue @@ -2,7 +2,7 @@ * @Author: SunTao 328867980@qq.com * @Date: 2024-10-17 11:34:00 * @LastEditors: SunTao 328867980@qq.com - * @LastEditTime: 2024-11-21 09:12:52 + * @LastEditTime: 2024-11-29 15:31:24 * @FilePath: \znxjxt-ui\src\views\big-screen\index.vue * @Description: 大屏首页 --> @@ -508,9 +508,20 @@ export default { this.getRoadList(); this.getDieaseTypeList(); this.getMessageList(); - // this.initWebSocket(); + this.handleMessage(); }, + mounted() {}, methods: { + /** + * @description: 处理websocket消息 + * @return {*} + */ + handleMessage() { + this.$ws.on("message", (data) => { + console.log("收到xxxxxxxx消息:", data); + }); + }, + /** * @description: 获取当前时间 * @return {*} @@ -549,7 +560,7 @@ export default { /** * @description: 获取消息中心数据 - * @return {void} + * @return {void} */ getMessageList() { // getMessageList().then(({ code, data }) => { @@ -600,7 +611,7 @@ export default { /** * @description: 获取数据栏右上角选项数据 - * @return {void} + * @return {void} */ getSelect() { selectTypeList().then(({ code, data }) => { @@ -609,10 +620,10 @@ export default { } }); }, - + /** * @description: 获取路段下拉数据 - * @return {void} + * @return {void} */ getRoadList() { getRoadListTypes().then(({ code, data }) => { @@ -624,7 +635,7 @@ export default { /** * @description: 获取图片背景左上角病害类型下拉 - * @return {void} + * @return {void} */ getDieaseTypeList() { getDefectTypes().then(({ code, data }) => { @@ -636,7 +647,7 @@ export default { /** * @description: 获取图片背景下坐标数据 - * @return {void} + * @return {void} */ getMapCare(value) { const data = { @@ -652,7 +663,7 @@ export default { /** * @description: 点击病害日志详情打开弹窗进行地图打点 - * @return {void} + * @return {void} */ getimagePoint(item) { this.imgTitle = "查看"; @@ -673,7 +684,7 @@ export default { /** * @description: 关闭图片查看弹窗 - * @return {void} + * @return {void} */ imgCancel() { this.imgTitle = ""; @@ -683,7 +694,7 @@ export default { /** * @description: 关闭查看点位大图弹窗 - * @return {void} + * @return {void} */ screenImgCancel() { this.$refs.roadMap.removeSelectClick(); @@ -692,7 +703,7 @@ export default { /** * @description: 切换icon类型多选框事件 - * @return {void} + * @return {void} */ handleChecked(value) { this.centerPiont = this.drawPointList.filter( @@ -708,7 +719,7 @@ export default { /** * @description: 获取地图点位信息 - * @return {void} + * @return {void} */ getCenterPiont() { // 如果当前已经有打点坐标 @@ -754,7 +765,7 @@ export default { /** * @description: 绘制地图点位 - * @return {void} + * @return {void} */ drawPoint() { const features = []; @@ -853,7 +864,7 @@ export default { /** * @description: 获取地图线段点位信息 - * @return {void} + * @return {void} */ getLinePoint() { if (!this.markLayerLines) { @@ -868,7 +879,7 @@ export default { /** * @description: 绘制地图线段 - * @return {void} + * @return {void} */ drawLine() { const features = []; @@ -929,8 +940,8 @@ export default { /** * @description: 地图线段颜色区分 - * @param {number} value - * @return {string} + * @param {number} value + * @return {string} */ getLineColor(value) { if (value > 92) { @@ -945,11 +956,11 @@ export default { return "#E64548"; } }, - + /** * @description: 地图下方4图标类别切换点击事件 - * @param {string} value - * @return {void} + * @param {string} value + * @return {void} */ changeIconType(value) { if (this.bottomTipClick !== value) { @@ -970,7 +981,7 @@ export default { /** * @description: 获取icon多选数据 - * @return {void} + * @return {void} */ getIconType() { this.mapLogeList = {}; @@ -992,8 +1003,8 @@ export default { /** * @description: 传回来的地图图层 - * @param {number} zoom - * @return {void} + * @param {number} zoom + * @return {void} */ getZoom(zoom) { this.mapZoom = zoom; @@ -1006,8 +1017,8 @@ export default { /** * @description: 地图选中feature事件 - * @param {object} e - * @return {void} + * @param {object} e + * @return {void} */ featureSelect(e) { const map = this.$refs.roadMap.instance.get("map"); @@ -1043,8 +1054,8 @@ export default { /** * @description: 数据栏切换事件 - * @param {object} item - * @return {void} + * @param {object} item + * @return {void} */ changeElement(item) { if (this.elementDiv !== item) { @@ -1098,6 +1109,9 @@ export default { class: "twe", }, ]; + // 不接收小车位置消息 + const data = { type: "carLocation", status: false }; + this.$ws.send(data); // 地图右上角多选按钮隐藏 this.showIconList = false; // 图层恢复 @@ -1141,6 +1155,10 @@ export default { class: "twe", }, ]; + // 发送小车位置消息 + const data = { type: "carLocation", status: true }; + this.$ws.send(data); + // 将地图层级初始化 this.$nextTick(() => { const map = this.$refs.roadMap.instance.get("map"); map.getView().setZoom(10); @@ -1191,6 +1209,9 @@ export default { class: "one", }, ]; + // 发送小车位置消息 + const data = { type: "carLocation", status: true }; + this.$ws.send(data); // 地图右上角多选按钮隐藏 this.showIconList = false; // 图层恢复 @@ -1202,11 +1223,6 @@ export default { map.getView().setZoom(10); map.getView().setCenter([123.30297096718999, 41.87942945541742]); }); - // 地图右上角多选按钮显示 - // this.getIconType(); - // this.showIconList = true; - // 进行地图打点 - // this.getCenterPiont(); this.getLinePoint(); } } @@ -1214,7 +1230,7 @@ export default { /** * @description: 跳转系统首页 - * @return {void} + * @return {void} */ goIndex() { this.$router.push("/index"); @@ -1222,7 +1238,7 @@ export default { /** * @description: 图片位置信息获取 - * @return {void} + * @return {void} */ updateScreenRects() { 1; @@ -1241,11 +1257,11 @@ export default { /** * @description: 图片红框位置 - * @param {object} left - * @param {object} top - * @param {object} width - * @param {object} height - * @return {object} + * @param {object} left + * @param {object} top + * @param {object} width + * @param {object} height + * @return {object} */ getScreenRectStyle({ left, top, width, height }) { const image = this.$refs.mainImage; @@ -1272,39 +1288,10 @@ export default { boxSizing: "border-box", }; }, - - /** - * @description: 初始化websocket - * @return {void} - */ - initWebSocket() { - const url = `ws://192.168.1.188:8080/websocket?token=${getToken()}`; - const data = { type: "carLocation", status: true }; - connectWebsocket( - url, - data, - (res) => { - // console.log("onWsMessage接收到服务器的数据: ", res); - console.log(JSON.parse(res)); - }, - (err) => { - console.log("断开重连"); - } - ); - }, - - /** - * @description: 发送消息 - * @return {void} - */ - sendMsg() { - sendMsg(5555); //value是发送的值 - // this.value = ""; - }, }, beforeDestroy() { clearInterval(this.timeFlag); - closeWebsocket(); + this.$ws.close(); }, }; diff --git a/src/views/xj/inspection-warn/warning-center/index.vue b/src/views/xj/inspection-warn/warning-center/index.vue index 1c74809..a1cf726 100644 --- a/src/views/xj/inspection-warn/warning-center/index.vue +++ b/src/views/xj/inspection-warn/warning-center/index.vue @@ -17,17 +17,10 @@ :inline="true" label-width="5rem" > - - - - + @@ -42,17 +35,17 @@ - + @@ -108,27 +101,27 @@ v-for="(item, index) in editableTabs" :key="`tabs-${index}`" :label="item.title" - :name="item.value" + :name="item.code" > - - + prop="title" + > + + +
@@ -184,18 +187,21 @@