From 55ed081b8414698e6e3b7d76c5c4032f264fb53f Mon Sep 17 00:00:00 2001
From: wwl <xchao828@163.com>
Date: 星期四, 24 七月 2025 09:10:27 +0800
Subject: [PATCH] 1

---
 /dev/null                |  142 -----------------
 src/main.js              |  146 +++++++++++++++++-
 src/111.js               |   97 ++++++++++++
 src/api/system/config.js |    9 +
 src/utils/websocket.js   |   45 +++-
 5 files changed, 272 insertions(+), 167 deletions(-)

diff --git a/src/111.js b/src/111.js
new file mode 100644
index 0000000..9ebec6f
--- /dev/null
+++ b/src/111.js
@@ -0,0 +1,97 @@
+// src/main.js
+import Vue from "vue";
+import Cookies from "js-cookie";
+import "babel-polyfill";
+import Element from "element-ui";
+import "./assets/styles/element-variables.scss";
+import "@/assets/styles/index.scss";
+import "@/assets/styles/ruoyi.scss";
+import App from "./App";
+import store from "./store";
+import router from "./router";
+import directive from "./directive";
+import plugins from "./plugins";
+import { download } from "@/utils/request";
+import Print from "vue-print-nb";
+import JsonExcel from "vue-json-excel";
+import "./assets/icons";
+import "./permission";
+import { getDicts } from "@/api/system/dict/data";
+import { getConfigKey } from "@/api/system/config";
+import {
+  parseTime,
+  resetForm,
+  addDateRange,
+  selectDictLabel,
+  selectDictLabels,
+  handleTree,
+} from "@/utils/ruoyi";
+import Pagination from "@/components/Pagination";
+import Editor from "@/components/Editor";
+import FileUpload from "@/components/FileUpload";
+import ImageUpload from "@/components/ImageUpload";
+import ImagePreview from "@/components/ImagePreview";
+import DictTag from "@/components/DictTag";
+import VueMeta from "vue-meta";
+import DictData from "@/components/DictData";
+import * as echarts from "echarts";
+import VueBarcode from "vue-barcode";
+
+Vue.component("downloadExcel", JsonExcel);
+Vue.component("barcode", VueBarcode);
+Vue.component("DictTag", DictTag);
+Vue.component("Pagination", Pagination);
+Vue.component("Editor", Editor);
+Vue.component("FileUpload", FileUpload);
+Vue.component("ImageUpload", ImageUpload);
+Vue.component("ImagePreview", ImagePreview);
+
+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.$echarts = echarts;
+
+// 淇濈暀 $showNotification锛屾敮鎸佹墜鍔ㄨЕ鍙戦�氱煡
+Vue.prototype.$showNotification = function (type, title, message, onClick) {
+  console.log('瑙﹀彂閫氱煡:', { type, title, message }, new Date().toLocaleString());
+  Vue.prototype.$notify({
+    title,
+    message,
+    type,
+    duration: 5000,
+    position: 'top-right',
+    offset: 50,
+    onClick,
+    customClass: 'global-notification',
+    appendTo: document.body
+  });
+};
+
+// 鐩戝惉璺敱鍙樺寲
+router.afterEach(() => {
+  console.log('璺敱鍒囨崲瀹屾垚锛屽綋鍓嶈矾寰�:', router.currentRoute.path);
+});
+
+const app = new Vue({
+  el: "#app",
+  router,
+  store,
+  render: (h) => h(App)
+});
+
+Vue.use(directive);
+Vue.use(plugins);
+Vue.use(VueMeta);
+Vue.use(Print);
+Vue.use(Element, {
+  size: Cookies.get("size") || "medium",
+});
+DictData.install();
+
+Vue.config.productionTip = false;
\ No newline at end of file
diff --git a/src/api/system/config.js b/src/api/system/config.js
index a404d82..6a60353 100644
--- a/src/api/system/config.js
+++ b/src/api/system/config.js
@@ -33,7 +33,14 @@
     data: data
   })
 }
-
+// 鏂板鍙傛暟閰嶇疆
+export function yidu(data) {
+  return request({
+    url: '/system/notice/readNotice ',
+    method: 'post',
+    data: data
+  })
+}
 // 淇敼鍙傛暟閰嶇疆
 export function updateConfig(data) {
   return request({
diff --git a/src/main copy.js b/src/main copy.js
deleted file mode 100644
index 30d4cab..0000000
--- a/src/main copy.js
+++ /dev/null
@@ -1,142 +0,0 @@
-// src/main.js
-import Vue from "vue";
-import Cookies from "js-cookie";
-import "babel-polyfill";
-import Element from "element-ui";
-import "./assets/styles/element-variables.scss";
-import "@/assets/styles/index.scss";
-import "@/assets/styles/ruoyi.scss";
-import App from "./App";
-import store from "./store";
-import router from "./router";
-import directive from "./directive";
-import plugins from "./plugins";
-import { download } from "@/utils/request";
-import Print from "vue-print-nb";
-import JsonExcel from "vue-json-excel";
-import "./assets/icons";
-import "./permission";
-import { getDicts } from "@/api/system/dict/data";
-import { getConfigKey } from "@/api/system/config";
-import {
-  parseTime,
-  resetForm,
-  addDateRange,
-  selectDictLabel,
-  selectDictLabels,
-  handleTree,
-} from "@/utils/ruoyi";
-import Pagination from "@/components/Pagination";
-import Editor from "@/components/Editor";
-import FileUpload from "@/components/FileUpload";
-import ImageUpload from "@/components/ImageUpload";
-import ImagePreview from "@/components/ImagePreview";
-import DictTag from "@/components/DictTag";
-import VueMeta from "vue-meta";
-import DictData from "@/components/DictData";
-import * as echarts from "echarts";
-import VueBarcode from "vue-barcode";
-import { initWebSocket, closeWebSocket } from "@/utils/websocket";
-
-Vue.component("downloadExcel", JsonExcel);
-Vue.component("barcode", VueBarcode);
-Vue.component("DictTag", DictTag);
-Vue.component("Pagination", Pagination);
-Vue.component("Editor", Editor);
-Vue.component("FileUpload", FileUpload);
-Vue.component("ImageUpload", ImageUpload);
-Vue.component("ImagePreview", ImagePreview);
-
-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.$echarts = echarts;
-
-Vue.prototype.$showNotification = function (type, title, message, onClick) {
-  console.log('瑙﹀彂閫氱煡:', { type, title, message });
-  Vue.prototype.$notify({
-    title,
-    message,
-    type,
-    duration: 5000,
-    position: 'top-right',
-    offset: 50,
-    onClick,
-    customClass: 'global-notification',
-    appendTo: document.body
-  });
-};
-
-// 鐩戝惉璺敱鍙樺寲
-router.afterEach(() => {
-  console.log('璺敱鍒囨崲瀹屾垚锛屽綋鍓嶈矾寰�:', router.currentRoute.path);
-});
-
-const app = new Vue({
-  el: "#app",
-  router,
-  store,
-  render: (h) => h(App),
-  mounted() {
-    const token = store.state.user.token || Cookies.get('token') || '';
-    if (token) {
-      console.log('Token:', token);
-      initWebSocket(token, (type, data) => {
-        if (type === 'error') {
-          Vue.prototype.$showNotification('error', '閿欒', data);
-          return;
-        }
-        try {
-          const message = JSON.parse(data);
-          console.log('WebSocket 瑙f瀽鍚庢秷鎭�:', message);
-          if (message.noticeId && message.noticeTitle) {
-            const noticeTypeLabel = message.noticeType === '1' ? '閫氱煡' : '鍏憡';
-            const contentPreview = message.noticeContent
-              ? message.noticeContent.replace(/<[^>]+>/g, '').substring(0, 20) + '...'
-              : '鏃犲唴瀹�';
-            Vue.prototype.$showNotification(
-              'success',
-              `鏂�${noticeTypeLabel}`,
-              `${message.noticeTitle} - ${contentPreview}`,
-              () => {
-          
-                router.push({
-                  path: '/redirect/notice',
-                  query: { noticeId: message.noticeId }
-                });
-              }
-            );
-          } else {
-            console.log('鏈煡娑堟伅绫诲瀷:', message);
-            Vue.prototype.$showNotification('info', '娑堟伅', '鏀跺埌鏈煡鏍煎紡鐨勬秷鎭�');
-          }
-        } catch (error) {
-          console.error('娑堟伅瑙f瀽澶辫触:', error, '鍘熷鏁版嵁:', data);
-          Vue.prototype.$showNotification('info', '娑堟伅', `鏈嶅姟鍣ㄥ洖搴斿瓧绗︿覆: ${data}`);
-        }
-      });
-    } else {
-      console.error('鏈壘鍒� token锛屾棤娉曞垵濮嬪寲 WebSocket');
-    }
-  },
-  beforeDestroy() {
-    closeWebSocket(); // 娓呯悊 WebSocket
-  }
-});
-
-Vue.use(directive);
-Vue.use(plugins);
-Vue.use(VueMeta);
-Vue.use(Print);
-Vue.use(Element, {
-  size: Cookies.get("size") || "medium",
-});
-DictData.install();
-
-Vue.config.productionTip = false;
\ No newline at end of file
diff --git a/src/main.js b/src/main.js
index 9ebec6f..37508ae 100644
--- a/src/main.js
+++ b/src/main.js
@@ -17,7 +17,7 @@
 import "./assets/icons";
 import "./permission";
 import { getDicts } from "@/api/system/dict/data";
-import { getConfigKey } from "@/api/system/config";
+import { getConfigKey, yidu } from "@/api/system/config";
 import {
   parseTime,
   resetForm,
@@ -36,7 +36,10 @@
 import DictData from "@/components/DictData";
 import * as echarts from "echarts";
 import VueBarcode from "vue-barcode";
+import { initWebSocket, closeWebSocket } from "@/utils/websocket";
+import RightToolbar from "@/components/RightToolbar"
 
+// 娉ㄥ唽鍏ㄥ眬缁勪欢
 Vue.component("downloadExcel", JsonExcel);
 Vue.component("barcode", VueBarcode);
 Vue.component("DictTag", DictTag);
@@ -45,7 +48,9 @@
 Vue.component("FileUpload", FileUpload);
 Vue.component("ImageUpload", ImageUpload);
 Vue.component("ImagePreview", ImagePreview);
+Vue.component("RightToolbar", RightToolbar);
 
+// 娉ㄥ唽鍏ㄥ眬鏂规硶
 Vue.prototype.getDicts = getDicts;
 Vue.prototype.getConfigKey = getConfigKey;
 Vue.prototype.parseTime = parseTime;
@@ -57,20 +62,78 @@
 Vue.prototype.handleTree = handleTree;
 Vue.prototype.$echarts = echarts;
 
-// 淇濈暀 $showNotification锛屾敮鎸佹墜鍔ㄨЕ鍙戦�氱煡
-Vue.prototype.$showNotification = function (type, title, message, onClick) {
-  console.log('瑙﹀彂閫氱煡:', { type, title, message }, new Date().toLocaleString());
-  Vue.prototype.$notify({
-    title,
-    message,
+// 閫氱煡绠$悊锛氳窡韪綋鍓嶉�氱煡鍜屽亸绉婚噺
+const notificationManager = {
+  notifications: [], // 瀛樺偍褰撳墠鏄剧ず鐨勯�氱煡瀹炰緥
+  baseOffset: 50, // 鍩虹鍋忕Щ閲�
+  notificationHeight: 80, // 姣忎釜閫氱煡鐨勪及璁¢珮搴︼紙鍖呮嫭闂磋窛锛�
+  maxNotifications: 5, // 鏈�澶у悓鏃舵樉绀虹殑閫氱煡鏁伴噺
+  addNotification(notification) {
+    if (this.notifications.length >= this.maxNotifications) {
+      // 鍏抽棴鏈�鏃╃殑閫氱煡
+      const oldest = this.notifications.shift();
+      oldest.close();
+    }
+    this.notifications.push(notification);
+    // 璁剧疆鍔ㄦ�� offset 鍜� z-index
+    notification.offset = this.baseOffset + this.notifications.length * this.notificationHeight;
+    notification.customClass += ` notification-${this.notifications.length}`; // 涓� z-index 娣诲姞鍞竴绫�
+    // 鐩戝惉閫氱煡鍏抽棴
+    notification.onClose = () => {
+      const index = this.notifications.indexOf(notification);
+      if (index > -1) {
+        this.notifications.splice(index, 1);
+        // 鏇存柊鍚庣画閫氱煡鐨� offset
+        this.updateOffsets();
+      }
+    };
+  },
+  updateOffsets() {
+    this.notifications.forEach((notification, index) => {
+      notification.offset = this.baseOffset + (index + 1) * this.notificationHeight;
+      notification.customClass = notification.customClass.replace(/notification-\d+/, `notification-${index + 1}`);
+    });
+  }
+};
+
+// 鍏ㄥ眬閫氱煡鏂规硶锛屾坊鍔犫�滃凡璇烩�濇寜閽�
+Vue.prototype.$showNotification = function (type, title, message, onClick, noticeId) {
+  console.log('瑙﹀彂閫氱煡:', { type, title, message, noticeId, noticeIdType: typeof noticeId }); // 璋冭瘯锛氳褰� noticeId 鍜岀被鍨�
+  const h = this.$createElement;
+  const notification = this.$notify({
+    title: title,
+    message: h('div', { class: 'notification-content' }, [
+      h('div', { class: 'notification-message' }, message),
+      noticeId ? h('el-button', {
+        class: 'read-button',
+        style: { marginLeft: '10px', float: 'right', cursor: 'pointer' },
+        props: { type: 'primary', size: 'mini' },
+        on: {
+          click: async () => {
+            console.log('鐐瑰嚮鈥滃凡璇烩�濇寜閽紝noticeId:', noticeId, 'type:', typeof noticeId); // 璋冭瘯锛氳褰曠偣鍑绘椂鐨� noticeId
+            try {
+              await yidu({ noticeId: String(noticeId) });
+              console.log(`閫氱煡 ${noticeId} 宸叉爣璁颁负宸茶`);
+              this.$message.success('鏍囪涓哄凡璇绘垚鍔�');
+              notification.close();
+            } catch (error) {
+              console.error('鏍囪宸茶澶辫触:', error, 'noticeId:', noticeId);
+              this.$message.error('鏍囪宸茶澶辫触');
+            }
+          }
+        }
+      }, '宸茶') : null
+    ]),
     type,
     duration: 5000,
     position: 'top-right',
-    offset: 50,
-    onClick,
+    offset: notificationManager.baseOffset, // 鍒濆 offset
+    onClick: null, // 涓嶈烦杞�
     customClass: 'global-notification',
+    dangerouslyUseHTMLString: false,
     appendTo: document.body
   });
+  notificationManager.addNotification(notification); // 娣诲姞鍒伴�氱煡绠$悊
 };
 
 // 鐩戝惉璺敱鍙樺寲
@@ -78,13 +141,76 @@
   console.log('璺敱鍒囨崲瀹屾垚锛屽綋鍓嶈矾寰�:', router.currentRoute.path);
 });
 
+// 瀹氫箟 WebSocket 鍒濆鍖栨爣蹇楋紝闃叉閲嶅杩炴帴
+let isWebSocketInitialized = false;
+
 const app = new Vue({
   el: "#app",
   router,
   store,
-  render: (h) => h(App)
+  render: (h) => h(App),
+  mounted() {
+    const token = store.state.user.token || Cookies.get('token') || '';
+    if (token && !isWebSocketInitialized) {
+      console.log('鍒濆鍖� WebSocket锛孴oken:', token);
+      isWebSocketInitialized = true;
+      initWebSocket(token, (type, data) => {
+        console.log('WebSocket 鏀跺埌娑堟伅:', { type, data }); // 璋冭瘯锛氳褰曞師濮嬫暟鎹�
+        if (type === 'error') {
+          Vue.prototype.$showNotification.call(this, 'error', '閿欒', data);
+          return;
+        }
+        try {
+          if (typeof data === 'string' && data.trim().startsWith('{')) {
+            // 鏇挎崲澶ф暣鏁板瓧娈碉紝闃叉绮惧害涓㈠け
+            const normalizedData = data.replace(/"(noticeId|notice_id|id)":\s*(\d+)/g, '"$1":"$2"');
+            const message = JSON.parse(normalizedData);
+            console.log('WebSocket 瑙f瀽鍚庢秷鎭�:', message); // 璋冭瘯锛氳褰曡В鏋愬悗鐨勬秷鎭�
+            if (message.noticeId || message.notice_id || message.id) {
+              const noticeTypeLabel = message.noticeType === '1' ? '閫氱煡' : '鍏憡';
+              const noticeTitle = message.noticeTitle ? message.noticeTitle.replace(/<[^>]+>/g, '') : '鏃犳爣棰�';
+              const contentPreview = message.noticeContent
+                ? message.noticeContent.replace(/<[^>]+>/g, '').substring(0, 20) + '...'
+                : '鏃犲唴瀹�';
+              const noticeId = String(message.noticeId || message.notice_id || message.id);
+              console.log('鍑嗗瑙﹀彂閫氱煡锛宯oticeId:', noticeId, 'type:', typeof noticeId); // 璋冭瘯锛氳褰曚紶閫掔殑 noticeId
+              Vue.prototype.$showNotification.call(
+                this,
+                'success',
+                `鏂�${noticeTypeLabel}`,
+                `${noticeTitle} - ${contentPreview}`,
+                null,
+                noticeId
+              );
+            } else {
+              console.log('鏈煡娑堟伅绫诲瀷:', message);
+              Vue.prototype.$showNotification.call(this, 'info', '娑堟伅', '鏀跺埌鏈煡鏍煎紡鐨勬秷鎭�');
+            }
+          } else {
+            console.log('WebSocket 闈� JSON 娑堟伅:', data);
+   
+          }
+        } catch (error) {
+          console.error('娑堟伅瑙f瀽澶辫触:', error, '鍘熷鏁版嵁:', data);
+          Vue.prototype.$showNotification.call(this, 'error', '娑堟伅瑙f瀽澶辫触', `鏈嶅姟鍣ㄥ洖搴斿瓧绗︿覆: ${data}`);
+        }
+      });
+    } else if (!token) {
+      console.error('鏈壘鍒� token锛屾棤娉曞垵濮嬪寲 WebSocket');
+    } else {
+      console.log('WebSocket 宸插垵濮嬪寲锛岃烦杩囬噸澶嶈繛鎺�');
+    }
+  },
+  beforeDestroy() {
+    if (isWebSocketInitialized) {
+      closeWebSocket();
+      isWebSocketInitialized = false;
+      console.log('Vue 瀹炰緥閿�姣侊紝WebSocket 宸叉竻鐞�');
+    }
+  }
 });
 
+// 娉ㄥ唽鎻掍欢
 Vue.use(directive);
 Vue.use(plugins);
 Vue.use(VueMeta);
diff --git a/src/utils/websocket.js b/src/utils/websocket.js
index 40529b1..9d956c5 100644
--- a/src/utils/websocket.js
+++ b/src/utils/websocket.js
@@ -1,6 +1,7 @@
 // src/utils/websocket.js
 let ws = null;
 let reconnectAttempts = 0;
+let pingTimer = null; // 鐢ㄤ簬绠$悊蹇冭烦瀹氭椂鍣�
 const maxReconnectAttempts = 5;
 const reconnectInterval = 5000; // 5绉掗噸杩為棿闅�
 const pingInterval = 5000; // 5绉掑彂閫乸ing
@@ -11,21 +12,33 @@
     return;
   }
 
-  const wsUrl = `ws://192.168.1.2:5011/ws?token=${token}`;
+  // 濡傛灉 ws 宸插瓨鍦ㄤ笖杩炴帴娲昏穬锛屽垯璺宠繃鍒濆鍖�
+  if (ws && ws.readyState === WebSocket.OPEN) {
+    console.log('WebSocket 宸茶繛鎺ワ紝璺宠繃閲嶅鍒濆鍖�');
+    return;
+  }
+
+  // 濡傛灉 ws 瀛樺湪浣嗘湭鍏抽棴锛屽厛鍏抽棴鏃ц繛鎺�
+  if (ws) {
+    console.warn('鍙戠幇鏃� WebSocket 杩炴帴锛屾鍦ㄥ叧闂�...');
+    closeWebSocket();
+  }
+
+  const wsUrl = `ws://192.168.1.244:5011/ws?token=${token}`;
   ws = new WebSocket(wsUrl);
 
   ws.onopen = () => {
-    console.log('WebSocket 杩炴帴鎴愬姛');
-    reconnectAttempts = 0;
+    reconnectAttempts = 0; // 閲嶇疆閲嶈繛璁℃暟
 
     // 鍚姩蹇冭烦鏈哄埗
-    const pingTimer = setInterval(() => {
-      if (ws.readyState === WebSocket.OPEN) {
-        console.log('鍙戦�� ping 娑堟伅');
+    if (pingTimer) clearInterval(pingTimer); // 娓呯悊鏃у畾鏃跺櫒
+    pingTimer = setInterval(() => {
+      if (ws && ws.readyState === WebSocket.OPEN) {
         ws.send('ping');
       } else {
         console.warn('WebSocket 鏈繛鎺ワ紝鍋滄 ping');
         clearInterval(pingTimer);
+        pingTimer = null;
       }
     }, pingInterval);
   };
@@ -35,6 +48,7 @@
     console.log('WebSocket 鏀跺埌鍘熷娑堟伅:', data);
     if (data === 'pong') {
       console.log('鏀跺埌 pong 鍝嶅簲锛岃繛鎺ユ椿璺�');
+      reconnectAttempts = 0; // 閲嶇疆閲嶈繛璁℃暟锛岀‘淇濇椿璺冭繛鎺ヤ笉瑙﹀彂閲嶈繛
       return;
     }
     onMessage('message', data);
@@ -42,11 +56,14 @@
 
   ws.onerror = (error) => {
     console.error('WebSocket 閿欒:', error);
-    onMessage('error', 'WebSocket 杩炴帴閿欒');
   };
 
   ws.onclose = () => {
-    console.warn('WebSocket 杩炴帴鍏抽棴');
+    console.warn('WebSocket 杩炴帴鍏抽棴锛屾椂闂�:', new Date());
+    if (pingTimer) {
+      clearInterval(pingTimer);
+      pingTimer = null;
+    }
     if (reconnectAttempts < maxReconnectAttempts) {
       reconnectAttempts++;
       console.log(`灏濊瘯閲嶈繛 (${reconnectAttempts}/${maxReconnectAttempts})...`);
@@ -61,18 +78,18 @@
 
   // 娓呯悊 WebSocket
   window.addEventListener('beforeunload', () => {
-    if (ws) {
-      ws.close();
-      ws = null;
-      console.log('WebSocket 宸叉竻鐞�');
-    }
-  });
+    closeWebSocket();
+  }, { once: true }); // 纭繚浜嬩欢鐩戝惉鍙坊鍔犱竴娆�
 }
 
 export function closeWebSocket() {
   if (ws) {
     ws.close();
     ws = null;
+    if (pingTimer) {
+      clearInterval(pingTimer);
+      pingTimer = null;
+    }
     console.log('WebSocket 宸插叧闂�');
   }
 }
\ No newline at end of file

--
Gitblit v1.8.0