Explorar o código

feat: 消息通知完善&托盘图标修改

banxia hai 1 mes
pai
achega
7743c8c4c5

+ 1 - 2
.env

@@ -14,5 +14,4 @@ VITE_REPORT = false
 VITE_BUILD_GZIP = false
 
 # 是否删除生产环境 console
-VITE_DROP_CONSOLE = true
-
+VITE_DROP_CONSOLE = true

+ 2 - 2
electron/main/tray.ts

@@ -21,7 +21,7 @@ export const initTray = () => {
     icon7: handlePath("./icon-7.png"),
     icon8: handlePath("./icon-8.png"),
     icon9: handlePath("./icon-9.png"),
-    // icon9plus: handlePath("./icon-9plus.png"),
+    icon9plus: handlePath("./icon-9plus.png"),
   };
   let appIcon = new Tray(ICONS.default);
   const isFocusOpenWin: number = handeGet("isFocusOpenWin");
@@ -71,7 +71,7 @@ export const initTray = () => {
     if (msgCount <= 9) {
       appIcon.setImage(ICONS[`icon${msgCount}`]);
     } else {
-      appIcon.setImage(ICONS[`icon9`]);
+      appIcon.setImage(ICONS[`icon9plus`]);
     }
     appIcon.setToolTip(`菁苗健康-未读消息数量(${msgCount})`);
   });

+ 4 - 2
package.json

@@ -1,7 +1,7 @@
 {
   "name": "jingmiao",
   "private": true,
-  "version": "0.0.2",
+  "version": "0.0.3",
   "main": "dist-electron/main/index.js",
   "pnpm": {
     "onlyBuiltDependencies": [
@@ -37,6 +37,7 @@
     "react-router-dom": "^6.28.0",
     "sass": "1.69.5",
     "sky": "file:",
+    "socket.io-client": "2.4",
     "zustand": "^5.0.3"
   },
   "devDependencies": {
@@ -109,5 +110,6 @@
     "releaseInfo": {
       "releaseNotes": "版本更新的具体内容"
     }
-  }
+  },
+  "packageManager": "pnpm@10.5.0+sha512.11106a5916c7406fe4b8cb8e3067974b8728f47308a4f5ac5e850304afa6f57e2847d7950dfe78877d8d36bfb401d381c4215db3a4c3547ffa63c14333a6fa51"
 }

+ 238 - 9
pnpm-lock.yaml

@@ -23,6 +23,9 @@ importers:
       cors:
         specifier: ^2.8.5
         version: 2.8.5
+      dayjs:
+        specifier: ^1.11.13
+        version: 1.11.13
       electron-log:
         specifier: ^5.2.2
         version: 5.2.2
@@ -37,7 +40,10 @@ importers:
         version: 1.18.1
       jingmiao:
         specifier: 'file:'
-        version: 'file:'
+        version: file:(@types/react@18.3.12)
+      lucide-react:
+        specifier: ^0.486.0
+        version: 0.486.0(react@18.3.1)
       nprogress:
         specifier: ^0.2.0
         version: 0.2.0
@@ -58,7 +64,13 @@ importers:
         version: 1.69.5
       sky:
         specifier: 'file:'
-        version: 'jingmiao@file:'
+        version: jingmiao@file:(@types/react@18.3.12)
+      socket.io-client:
+        specifier: '2.4'
+        version: 2.4.0
+      zustand:
+        specifier: ^5.0.3
+        version: 5.0.3(@types/react@18.3.12)(react@18.3.1)
     devDependencies:
       '@eslint/js':
         specifier: ^9.14.0
@@ -80,7 +92,7 @@ importers:
         version: 22.3.27
       electron-builder:
         specifier: ^25.1.8
-        version: 25.1.8(electron-builder-squirrel-windows@25.1.8(dmg-builder@25.1.8))
+        version: 25.1.8(electron-builder-squirrel-windows@25.1.8)
       eslint:
         specifier: ^9.14.0
         version: 9.14.0(jiti@1.21.6)
@@ -856,6 +868,9 @@ packages:
     engines: {node: '>=0.4.0'}
     hasBin: true
 
+  after@0.8.2:
+    resolution: {integrity: sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA==}
+
   agent-base@6.0.2:
     resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
     engines: {node: '>= 6.0.0'}
@@ -959,6 +974,9 @@ packages:
   array-flatten@1.1.1:
     resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
 
+  arraybuffer.slice@0.0.7:
+    resolution: {integrity: sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==}
+
   assert-plus@1.0.0:
     resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
     engines: {node: '>=0.8'}
@@ -995,9 +1013,16 @@ packages:
   axios@1.7.7:
     resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==}
 
+  backo2@1.0.2:
+    resolution: {integrity: sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==}
+
   balanced-match@1.0.2:
     resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
 
+  base64-arraybuffer@0.1.4:
+    resolution: {integrity: sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg==}
+    engines: {node: '>= 0.6.0'}
+
   base64-js@1.5.1:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
 
@@ -1008,6 +1033,9 @@ packages:
   bl@4.1.0:
     resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
 
+  blob@0.0.5:
+    resolution: {integrity: sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==}
+
   bluebird-lst@1.0.9:
     resolution: {integrity: sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==}
 
@@ -1160,6 +1188,15 @@ packages:
     resolution: {integrity: sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==}
     engines: {node: '>=0.10.0'}
 
+  component-bind@1.0.0:
+    resolution: {integrity: sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw==}
+
+  component-emitter@1.3.1:
+    resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
+
+  component-inherit@0.0.3:
+    resolution: {integrity: sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA==}
+
   compress-commons@4.1.2:
     resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==}
     engines: {node: '>= 10'}
@@ -1261,6 +1298,14 @@ packages:
       supports-color:
         optional: true
 
+  debug@3.1.0:
+    resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
   debug@4.3.7:
     resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
     engines: {node: '>=6.0'}
@@ -1401,6 +1446,12 @@ packages:
   end-of-stream@1.4.4:
     resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
 
+  engine.io-client@3.5.4:
+    resolution: {integrity: sha512-ydc8uuMMDxC5KCKNJN3zZKYJk2sgyTuTZQ7Aj1DJSsLKAcizA/PzWivw8fZMIjJVBo2CJOYzntv4FSjY/Lr//g==}
+
+  engine.io-parser@2.2.1:
+    resolution: {integrity: sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==}
+
   env-paths@2.2.1:
     resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
     engines: {node: '>=6'}
@@ -1706,6 +1757,12 @@ packages:
   graphemer@1.4.0:
     resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
 
+  has-binary2@1.0.3:
+    resolution: {integrity: sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==}
+
+  has-cors@1.1.0:
+    resolution: {integrity: sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA==}
+
   has-flag@4.0.0:
     resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
     engines: {node: '>=8'}
@@ -1797,6 +1854,9 @@ packages:
     resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
     engines: {node: '>=8'}
 
+  indexof@0.0.1:
+    resolution: {integrity: sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==}
+
   infer-owner@1.0.4:
     resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==}
 
@@ -1861,6 +1921,9 @@ packages:
   isarray@1.0.0:
     resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
 
+  isarray@2.0.1:
+    resolution: {integrity: sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ==}
+
   isbinaryfile@4.0.10:
     resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==}
     engines: {node: '>= 8.0.0'}
@@ -2022,6 +2085,11 @@ packages:
     resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==}
     engines: {node: '>=12'}
 
+  lucide-react@0.486.0:
+    resolution: {integrity: sha512-xWop/wMsC1ikiEVLZrxXjPKw4vU/eAip33G2mZHgbWnr4Nr5Rt4Vx4s/q1D3B/rQVbxjOuqASkEZcUxDEKzecw==}
+    peerDependencies:
+      react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
   make-fetch-happen@10.2.1:
     resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==}
     engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
@@ -2284,6 +2352,12 @@ packages:
     resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
     engines: {node: '>=6'}
 
+  parseqs@0.0.6:
+    resolution: {integrity: sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==}
+
+  parseuri@0.0.6:
+    resolution: {integrity: sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==}
+
   parseurl@1.3.3:
     resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
     engines: {node: '>= 0.8'}
@@ -2884,6 +2958,12 @@ packages:
     resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
     engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
 
+  socket.io-client@2.4.0:
+    resolution: {integrity: sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==}
+
+  socket.io-parser@3.3.4:
+    resolution: {integrity: sha512-z/pFQB3x+EZldRRzORYW1vwVO8m/3ILkswtnpoeU6Ve3cbMWkmHEWDAVJn4QJtchiiFTo5j7UG2QvwxvaA9vow==}
+
   socks-proxy-agent@7.0.0:
     resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==}
     engines: {node: '>= 10'}
@@ -3004,6 +3084,9 @@ packages:
     resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
     engines: {node: '>=14.14'}
 
+  to-array@0.1.4:
+    resolution: {integrity: sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A==}
+
   to-regex-range@5.0.1:
     resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
     engines: {node: '>=8.0'}
@@ -3184,10 +3267,26 @@ packages:
   wrappy@1.0.2:
     resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
 
+  ws@7.5.10:
+    resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==}
+    engines: {node: '>=8.3.0'}
+    peerDependencies:
+      bufferutil: ^4.0.1
+      utf-8-validate: ^5.0.2
+    peerDependenciesMeta:
+      bufferutil:
+        optional: true
+      utf-8-validate:
+        optional: true
+
   xmlbuilder@15.1.1:
     resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
     engines: {node: '>=8.0'}
 
+  xmlhttprequest-ssl@1.6.3:
+    resolution: {integrity: sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q==}
+    engines: {node: '>=0.4.0'}
+
   y18n@5.0.8:
     resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
     engines: {node: '>=10'}
@@ -3214,6 +3313,9 @@ packages:
   yauzl@2.10.0:
     resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==}
 
+  yeast@0.1.2:
+    resolution: {integrity: sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg==}
+
   yocto-queue@0.1.0:
     resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
     engines: {node: '>=10'}
@@ -3222,6 +3324,24 @@ packages:
     resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==}
     engines: {node: '>= 10'}
 
+  zustand@5.0.3:
+    resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==}
+    engines: {node: '>=12.20.0'}
+    peerDependencies:
+      '@types/react': '>=18.0.0'
+      immer: '>=9.0.6'
+      react: '>=18.0.0'
+      use-sync-external-store: '>=1.2.0'
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      immer:
+        optional: true
+      react:
+        optional: true
+      use-sync-external-store:
+        optional: true
+
 snapshots:
 
   7zip-bin@5.2.0: {}
@@ -3988,6 +4108,8 @@ snapshots:
 
   acorn@8.14.0: {}
 
+  after@0.8.2: {}
+
   agent-base@6.0.2:
     dependencies:
       debug: 4.3.7
@@ -4108,7 +4230,7 @@ snapshots:
 
   app-builder-bin@5.0.0-alpha.10: {}
 
-  app-builder-lib@25.1.8(dmg-builder@25.1.8(electron-builder-squirrel-windows@25.1.8))(electron-builder-squirrel-windows@25.1.8(dmg-builder@25.1.8)):
+  app-builder-lib@25.1.8(dmg-builder@25.1.8)(electron-builder-squirrel-windows@25.1.8):
     dependencies:
       '@develar/schema-utils': 2.6.5
       '@electron/notarize': 2.5.0
@@ -4197,6 +4319,8 @@ snapshots:
 
   array-flatten@1.1.1: {}
 
+  arraybuffer.slice@0.0.7: {}
+
   assert-plus@1.0.0:
     optional: true
 
@@ -4231,8 +4355,12 @@ snapshots:
     transitivePeerDependencies:
       - debug
 
+  backo2@1.0.2: {}
+
   balanced-match@1.0.2: {}
 
+  base64-arraybuffer@0.1.4: {}
+
   base64-js@1.5.1: {}
 
   binary-extensions@2.3.0: {}
@@ -4243,6 +4371,8 @@ snapshots:
       inherits: 2.0.4
       readable-stream: 3.6.2
 
+  blob@0.0.5: {}
+
   bluebird-lst@1.0.9:
     dependencies:
       bluebird: 3.7.2
@@ -4446,6 +4576,12 @@ snapshots:
 
   compare-version@0.1.2: {}
 
+  component-bind@1.0.0: {}
+
+  component-emitter@1.3.1: {}
+
+  component-inherit@0.0.3: {}
+
   compress-commons@4.1.2:
     dependencies:
       buffer-crc32: 0.2.13
@@ -4544,6 +4680,10 @@ snapshots:
     dependencies:
       ms: 2.0.0
 
+  debug@3.1.0:
+    dependencies:
+      ms: 2.0.0
+
   debug@4.3.7:
     dependencies:
       ms: 2.1.3
@@ -4597,7 +4737,7 @@ snapshots:
 
   dmg-builder@25.1.8(electron-builder-squirrel-windows@25.1.8):
     dependencies:
-      app-builder-lib: 25.1.8(dmg-builder@25.1.8(electron-builder-squirrel-windows@25.1.8))(electron-builder-squirrel-windows@25.1.8(dmg-builder@25.1.8))
+      app-builder-lib: 25.1.8(dmg-builder@25.1.8)(electron-builder-squirrel-windows@25.1.8)
       builder-util: 25.1.7
       builder-util-runtime: 9.2.10
       fs-extra: 10.1.0
@@ -4642,7 +4782,7 @@ snapshots:
 
   electron-builder-squirrel-windows@25.1.8(dmg-builder@25.1.8):
     dependencies:
-      app-builder-lib: 25.1.8(dmg-builder@25.1.8(electron-builder-squirrel-windows@25.1.8))(electron-builder-squirrel-windows@25.1.8(dmg-builder@25.1.8))
+      app-builder-lib: 25.1.8(dmg-builder@25.1.8)(electron-builder-squirrel-windows@25.1.8)
       archiver: 5.3.2
       builder-util: 25.1.7
       fs-extra: 10.1.0
@@ -4651,9 +4791,9 @@ snapshots:
       - dmg-builder
       - supports-color
 
-  electron-builder@25.1.8(electron-builder-squirrel-windows@25.1.8(dmg-builder@25.1.8)):
+  electron-builder@25.1.8(electron-builder-squirrel-windows@25.1.8):
     dependencies:
-      app-builder-lib: 25.1.8(dmg-builder@25.1.8(electron-builder-squirrel-windows@25.1.8))(electron-builder-squirrel-windows@25.1.8(dmg-builder@25.1.8))
+      app-builder-lib: 25.1.8(dmg-builder@25.1.8)(electron-builder-squirrel-windows@25.1.8)
       builder-util: 25.1.7
       builder-util-runtime: 9.2.10
       chalk: 4.1.2
@@ -4714,6 +4854,32 @@ snapshots:
     dependencies:
       once: 1.4.0
 
+  engine.io-client@3.5.4:
+    dependencies:
+      component-emitter: 1.3.1
+      component-inherit: 0.0.3
+      debug: 3.1.0
+      engine.io-parser: 2.2.1
+      has-cors: 1.1.0
+      indexof: 0.0.1
+      parseqs: 0.0.6
+      parseuri: 0.0.6
+      ws: 7.5.10
+      xmlhttprequest-ssl: 1.6.3
+      yeast: 0.1.2
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+
+  engine.io-parser@2.2.1:
+    dependencies:
+      after: 0.8.2
+      arraybuffer.slice: 0.0.7
+      base64-arraybuffer: 0.1.4
+      blob: 0.0.5
+      has-binary2: 1.0.3
+
   env-paths@2.2.1: {}
 
   err-code@2.0.3: {}
@@ -5137,6 +5303,12 @@ snapshots:
 
   graphemer@1.4.0: {}
 
+  has-binary2@1.0.3:
+    dependencies:
+      isarray: 2.0.1
+
+  has-cors@1.1.0: {}
+
   has-flag@4.0.0: {}
 
   has-property-descriptors@1.0.2:
@@ -5234,6 +5406,8 @@ snapshots:
 
   indent-string@4.0.0: {}
 
+  indexof@0.0.1: {}
+
   infer-owner@1.0.4: {}
 
   inflight@1.0.6:
@@ -5282,6 +5456,8 @@ snapshots:
 
   isarray@1.0.0: {}
 
+  isarray@2.0.1: {}
+
   isbinaryfile@4.0.10: {}
 
   isbinaryfile@5.0.4: {}
@@ -5305,29 +5481,35 @@ snapshots:
       filelist: 1.0.4
       minimatch: 3.1.2
 
-  'jingmiao@file:':
+  jingmiao@file:(@types/react@18.3.12):
     dependencies:
       '@ant-design/icons': 5.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       antd: 5.22.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       axios: 1.7.7
       cookie-parser: 1.4.7
       cors: 2.8.5
+      dayjs: 1.11.13
       electron-log: 5.2.2
       electron-store: 8.2.0
       express: 4.21.1
       express-session: 1.18.1
+      lucide-react: 0.486.0(react@18.3.1)
       nprogress: 0.2.0
       qs: 6.13.0
       react: 18.3.1
       react-dom: 18.3.1(react@18.3.1)
       react-router-dom: 6.28.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
       sass: 1.69.5
+      zustand: 5.0.3(@types/react@18.3.12)(react@18.3.1)
     transitivePeerDependencies:
+      - '@types/react'
       - date-fns
       - debug
+      - immer
       - luxon
       - moment
       - supports-color
+      - use-sync-external-store
 
   jiti@1.21.6: {}
 
@@ -5439,6 +5621,10 @@ snapshots:
 
   lru-cache@7.18.3: {}
 
+  lucide-react@0.486.0(react@18.3.1):
+    dependencies:
+      react: 18.3.1
+
   make-fetch-happen@10.2.1:
     dependencies:
       agentkeepalive: 4.5.0
@@ -5695,6 +5881,10 @@ snapshots:
     dependencies:
       callsites: 3.1.0
 
+  parseqs@0.0.6: {}
+
+  parseuri@0.0.6: {}
+
   parseurl@1.3.3: {}
 
   path-exists@3.0.0: {}
@@ -6392,6 +6582,32 @@ snapshots:
 
   smart-buffer@4.2.0: {}
 
+  socket.io-client@2.4.0:
+    dependencies:
+      backo2: 1.0.2
+      component-bind: 1.0.0
+      component-emitter: 1.3.1
+      debug: 3.1.0
+      engine.io-client: 3.5.4
+      has-binary2: 1.0.3
+      indexof: 0.0.1
+      parseqs: 0.0.6
+      parseuri: 0.0.6
+      socket.io-parser: 3.3.4
+      to-array: 0.1.4
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+
+  socket.io-parser@3.3.4:
+    dependencies:
+      component-emitter: 1.3.1
+      debug: 3.1.0
+      isarray: 2.0.1
+    transitivePeerDependencies:
+      - supports-color
+
   socks-proxy-agent@7.0.0:
     dependencies:
       agent-base: 6.0.2
@@ -6547,6 +6763,8 @@ snapshots:
 
   tmp@0.2.3: {}
 
+  to-array@0.1.4: {}
+
   to-regex-range@5.0.1:
     dependencies:
       is-number: 7.0.0
@@ -6690,8 +6908,12 @@ snapshots:
 
   wrappy@1.0.2: {}
 
+  ws@7.5.10: {}
+
   xmlbuilder@15.1.1: {}
 
+  xmlhttprequest-ssl@1.6.3: {}
+
   y18n@5.0.8: {}
 
   yallist@3.1.1: {}
@@ -6717,6 +6939,8 @@ snapshots:
       buffer-crc32: 0.2.13
       fd-slicer: 1.1.0
 
+  yeast@0.1.2: {}
+
   yocto-queue@0.1.0: {}
 
   zip-stream@4.1.1:
@@ -6724,3 +6948,8 @@ snapshots:
       archiver-utils: 3.0.4
       compress-commons: 4.1.2
       readable-stream: 3.6.2
+
+  zustand@5.0.3(@types/react@18.3.12)(react@18.3.1):
+    optionalDependencies:
+      '@types/react': 18.3.12
+      react: 18.3.1

BIN=BIN
public/icon-1.png


BIN=BIN
public/icon-2.png


BIN=BIN
public/icon-3.png


BIN=BIN
public/icon-4.png


BIN=BIN
public/icon-5.png


BIN=BIN
public/icon-6.png


BIN=BIN
public/icon-7.png


BIN=BIN
public/icon-8.png


BIN=BIN
public/icon-9.png


BIN=BIN
public/icon-9plus.png


+ 91 - 62
src/components/MessageModal/MessageModal.tsx

@@ -1,8 +1,8 @@
 import React, { useState } from "react";
-import { Modal, Button, Tabs, Empty } from "antd";
-import { CheckCircleOutlined, EyeOutlined } from "@ant-design/icons";
+import { Modal, Button, Tabs, Empty, message } from "antd";
 import type { TabsProps } from "antd";
-import { MESSAGE_TAB } from "@/constants";
+import axios from "axios";
+import { MESSAGE_STATUS, MESSAGE_TAB } from "@/constants";
 import { useNotificationStore } from "@/store/NotificationStore";
 import { Eraser, FileText } from "lucide-react";
 import dayjs from "dayjs";
@@ -20,9 +20,9 @@ const MessageModal: React.FC<MessageModalProps> = ({
   socket,
 }) => {
   const [activeKey, setActiveKey] = useState(MESSAGE_TAB.UNREAD);
-  const { notifications } = useNotificationStore();
+  const { notifications, changeNotification } = useNotificationStore();
   const unreadNotifications = notifications.filter(
-    (notification) => notification.status === MESSAGE_TAB.UNREAD
+    (notification) => notification.is_read === MESSAGE_STATUS.UNREAD
   );
 
   const tabItems: TabsProps["items"] = [
@@ -34,10 +34,10 @@ const MessageModal: React.FC<MessageModalProps> = ({
       key: MESSAGE_TAB.UNREAD,
       label: (
         <span className="flex">
-          未读{" "}
-          {!!notifications.length && (
+          未读
+          {!!unreadNotifications.length && (
             <span className="ml-1 text-xs text-white bg-red-500 rounded-full w-5 h-5 flex justify-center items-center">
-              {renderNoticeLength(notifications)}
+              {renderNoticeLength(unreadNotifications)}
             </span>
           )}
         </span>
@@ -54,11 +54,19 @@ const MessageModal: React.FC<MessageModalProps> = ({
       title={
         <div className="flex items-center">
           <span>
-            消息中心{" "}
-            {!!renderNoticeLength(notifications) &&
-              `(${renderNoticeLength(notifications)})`}
+            消息中心
+            {!!renderNoticeLength(unreadNotifications) &&
+              `(${renderNoticeLength(unreadNotifications)})`}
           </span>
-          <div className="cursor-pointer hover:bg-gray-100 rounded-md px-2 py-1 ml-2 text-xs flex justify-center items-center gap-1">
+          <div
+            className="cursor-pointer hover:bg-gray-100 rounded-md px-2 py-1 ml-2 text-xs flex justify-center items-center gap-1"
+            onClick={async () => {
+              const res = await axios.post(`/notification/bazb/clearMsg`);
+              if (res.data.code === 200) {
+                changeNotification({ type: "clear" });
+              }
+            }}
+          >
             <Eraser size={12} />
             清除消息
           </div>
@@ -83,60 +91,81 @@ const MessageModal: React.FC<MessageModalProps> = ({
           }}
         />
         <div className="space-y-4 max-h-[100%] overflow-auto">
-          {
-            // .filter(
-            //   (notification) =>
-            //     activeKey === MESSAGE_TAB.ALL || notification.status === activeKey
-            // )
-            notifications.length === 0 && (
-              <div className="flex justify-center items-center h-full">
-                <Empty description="暂无消息" />
-              </div>
-            )
-          }
-          {notifications
-            // .filter(
-            //   (notification) =>
-            //     activeKey === MESSAGE_TAB.ALL ||
-            //     notification.status === activeKey
-            // )
-            .map((message, index) => (
-              <div
-                key={index}
-                className="p-4 bg-gray-50 rounded-lg shadow-sm flex flex-col gap-4"
-              >
-                <div className="flex justify-between items-center">
-                  <div className="flex gap-1 items-center">
-                    <div className="p-1 bg-blue-500 rounded-full w-6 h-6 text-white flex justify-center items-center">
-                      <FileText size={14} />
-                    </div>
-                    <div className="font-bold">病例时效提醒</div>
-                  </div>
-                  <div className="text-gray-500 text-xs">
-                    {dayjs(message.timestamp).format("YYYY-MM-DD HH:mm")}
+          {(() => {
+            let filteredNotifications = notifications;
+
+            if (activeKey === MESSAGE_TAB.UNREAD) {
+              filteredNotifications = notifications.filter(
+                (notification) => notification.is_read === MESSAGE_STATUS.UNREAD
+              );
+            } else if (activeKey === MESSAGE_TAB.HAS_READ) {
+              filteredNotifications = notifications.filter(
+                (notification) =>
+                  notification.is_read === MESSAGE_STATUS.HAS_READ
+              );
+            }
+
+            return (
+              <>
+                {filteredNotifications.length === 0 && (
+                  <div className="flex justify-center items-center h-full">
+                    <Empty description="暂无消息" />
                   </div>
-                </div>
-                <div className="flex items-start space-x-4">
-                  <div className="flex-1">
-                    <div className="flex">
-                      <h4 className="text-sm font-semibold">{message.title}</h4>
+                )}
+                {filteredNotifications.map((message, index) => (
+                  <div
+                    key={index}
+                    className="p-4 bg-gray-50 rounded-lg shadow-sm flex flex-col gap-4"
+                  >
+                    <div className="flex justify-between items-center">
+                      <div className="flex gap-1 items-center">
+                        <div className="p-1 bg-blue-500 rounded-full w-6 h-6 text-white flex justify-center items-center">
+                          <FileText size={14} />
+                        </div>
+                        <div className="font-bold">病例时效提醒</div>
+                      </div>
+                      <div className="text-gray-500 text-xs">
+                        {dayjs(message.timestamp).format("YYYY-MM-DD HH:mm")}
+                      </div>
                     </div>
-                    <p className="text-gray-700 mt-1 indent-[1.75rem]">
-                      {message.sendMsg}
-                    </p>
-                    <p className="text-gray-700 mt-1 indent-[1.75rem]">
-                      {message.footer || "祝您工作顺利!"}
-                    </p>
-                    <div className="mt-2 flex space-x-2 justify-end">
-                      <Button type="primary" size="middle">
-                        知道了
-                      </Button>
-                      <Button size="middle">去查看</Button>
+                    <div className="flex items-start space-x-4">
+                      <div className="flex-1">
+                        <div className="flex">
+                          <h4 className="text-sm font-semibold">
+                            {message.title}
+                          </h4>
+                        </div>
+                        <p className="text-gray-700 mt-1 indent-[1.75rem]">
+                          {message.content}
+                        </p>
+                        <p className="text-gray-700 mt-1 indent-[1.75rem]">
+                          {message.footer || "祝您工作顺利!"}
+                        </p>
+                        <div className="mt-2 flex space-x-2 justify-end">
+                          <Button
+                            type="primary"
+                            size="middle"
+                            onClick={() => {
+                              axios.post("/notification/bazb/clearMsg", {
+                                msgId: message.id,
+                              });
+                              changeNotification({
+                                type: "clear",
+                                notification: [message.id],
+                              });
+                            }}
+                          >
+                            知道了
+                          </Button>
+                          <Button size="middle">去查看</Button>
+                        </div>
+                      </div>
                     </div>
                   </div>
-                </div>
-              </div>
-            ))}
+                ))}
+              </>
+            );
+          })()}
         </div>
       </div>
     </Modal>

+ 8 - 3
src/constants/index.ts

@@ -1,5 +1,10 @@
 export const MESSAGE_TAB = {
-  ALL: "1",
-  HAS_READ: "2",
-  UNREAD: "3",
+  ALL: "-1",
+  HAS_READ: "1",
+  UNREAD: "0",
+};
+
+export const MESSAGE_STATUS = {
+  HAS_READ: 1,
+  UNREAD: 0,
 };

+ 28 - 4
src/layouts/Header/index.tsx

@@ -1,8 +1,12 @@
 import { useEffect, useState } from "react";
 import { Bell, Expand, Minus, Shrink, Square, X } from "lucide-react";
+import axios from "axios";
+
 import "./index.scss";
 import MessageModal from "@/components/MessageModal/MessageModal";
 import { useNotificationStore } from "@/store/NotificationStore";
+import { MESSAGE_STATUS } from "@/constants";
+import { renderNoticeLength } from "@/utils/utils";
 
 interface optType {
   maxStatus?: string; // 最大化
@@ -18,13 +22,26 @@ const LayoutHeader = (props) => {
   const [winWidth, setWinWidth] = useState(0);
   const [scaleFactor, setScaleFactor] = useState(1);
 
+  const unreadNotifications = notifications.filter(
+    (notification) => notification.is_read === MESSAGE_STATUS.UNREAD
+  );
+
   const getWinWidth = async () => {
     const { width, scaleFactor } = await window.electronAPI.getWindowWidth();
     setWinWidth(width);
     setScaleFactor(scaleFactor);
   };
 
+  const fetchNoticeList = async () => {
+    const res = await axios.get(`/notification/bazb/getMsgList`);
+    changeNotification({
+      type: "new_msg",
+      notification: res?.data?.data?.list?.data,
+    });
+  };
+
   useEffect(() => {
+    fetchNoticeList();
     getWinWidth();
     const socket = window.io("http://114.215.252.134:2120");
     setSocket(socket);
@@ -38,10 +55,17 @@ const LayoutHeader = (props) => {
       //msg= JSON.parse(msg);
       const fixedData = msg.replace(/&quot;/g, '"');
       const userObj = JSON.parse(fixedData);
-      console.log(userObj, "1-banxia");
+      if (userObj.msg_type === 2) {
+        return;
+      }
       changeNotification({
         type: "new_msg",
-        notification: userObj,
+        notification: [
+          {
+            ...userObj,
+            is_read: 0,
+          },
+        ],
       });
     });
     // 后端推送来在线数据时
@@ -90,9 +114,9 @@ const LayoutHeader = (props) => {
             className="relative cursor-pointer p-2 hover:bg-gray-100 rounded-lg"
           >
             <Bell className="text-gray-600 hover:bg-gray-5002" size={16} />
-            {!!notifications.length && (
+            {!!unreadNotifications.length && (
               <span className="absolute top-0 right-0 w-4 h-4 flex justify-center items-center text-xs bg-red-500 text-white rounded-full">
-                {notifications.length > 9 ? "9+" : notifications.length}
+                {renderNoticeLength(unreadNotifications)}
               </span>
             )}
           </div>

+ 19 - 2
src/store/NotificationStore.ts

@@ -1,3 +1,4 @@
+import { MESSAGE_STATUS } from "@/constants";
 import { create } from "zustand";
 interface NotificationStore {
   notifications: any[];
@@ -23,9 +24,25 @@ export const useNotificationStore = create<
   changeNotification: ({ type, notification }) => {
     let new_notification: any = [];
     if (["new_msg"].includes(type)) {
-      new_notification = [...get().notifications, notification];
+      new_notification = [...get().notifications, ...notification];
     } else if (type === "clear") {
-      new_notification = [];
+      if (!notification) {
+        const notifications = get().notifications;
+        new_notification = notifications.map((item) => ({
+          ...item,
+          is_read: MESSAGE_STATUS.HAS_READ,
+        }));
+      } else {
+        new_notification = get().notifications.map((item) => {
+          if (notification.includes(item.id)) {
+            return {
+              ...item,
+              is_read: MESSAGE_STATUS.HAS_READ,
+            };
+          }
+          return item;
+        });
+      }
     }
     set({ notifications: new_notification });
   },

+ 5 - 0
vite.config.ts

@@ -146,6 +146,11 @@ export default defineConfig(({ command }) => {
           changeOrigin: true,
           // rewrite: (path) => path.replace(new RegExp(`/api`), "/api"),
         },
+        "/notification": {
+          target: "http://114.215.252.134:8989",
+          changeOrigin: true,
+          rewrite: (path) => path.replace(/^\/notification/, ""),
+        },
       },
     },
     resolve: {