@robot-admin/naive-ui-components 0.10.3 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (659) hide show
  1. package/CHANGELOG.md +117 -100
  2. package/LICENSE +21 -21
  3. package/README.md +463 -318
  4. package/README_EN.md +404 -0
  5. package/SECURITY.md +23 -0
  6. package/dist/C_ActionBar.cjs +1 -1
  7. package/dist/C_ActionBar.css +46 -48
  8. package/dist/C_ActionBar.d.cts +1 -1
  9. package/dist/C_ActionBar.js +1 -1
  10. package/dist/C_ActionBar2.cjs +1 -0
  11. package/dist/C_ActionBar2.js +1 -2
  12. package/dist/C_AntV.cjs +1 -1
  13. package/dist/C_AntV.css +3 -3
  14. package/dist/C_AntV.d.cts +1 -1
  15. package/dist/C_AntV.js +1 -1
  16. package/dist/C_AntV2.cjs +4 -0
  17. package/dist/C_AntV2.js +3 -4
  18. package/dist/C_AudioPlayer.cjs +1 -1
  19. package/dist/C_AudioPlayer.css +15 -7
  20. package/dist/C_AudioPlayer.d.cts +1 -1
  21. package/dist/C_AudioPlayer.js +1 -1
  22. package/dist/C_AudioPlayer2.cjs +1 -0
  23. package/dist/C_AudioPlayer2.js +1 -2
  24. package/dist/C_AvatarGroup.cjs +1 -1
  25. package/dist/C_AvatarGroup.css +15 -7
  26. package/dist/C_AvatarGroup.d.cts +1 -1
  27. package/dist/C_AvatarGroup2.cjs +1 -0
  28. package/dist/C_AvatarGroup2.js +1 -2
  29. package/dist/C_Barcode.cjs +1 -1
  30. package/dist/C_Barcode.css +0 -2
  31. package/dist/C_Barcode.d.cts +1 -1
  32. package/dist/C_Barcode2.cjs +1 -0
  33. package/dist/C_Barcode2.js +1 -2
  34. package/dist/C_Breadcrumb.cjs +1 -1
  35. package/dist/C_Breadcrumb.css +0 -2
  36. package/dist/C_Breadcrumb.d.cts +2 -2
  37. package/dist/C_Breadcrumb.d.ts +1 -2
  38. package/dist/C_Breadcrumb.js +1 -1
  39. package/dist/C_Breadcrumb2.cjs +1 -0
  40. package/dist/C_Breadcrumb2.js +1 -2
  41. package/dist/C_Captcha.cjs +1 -1
  42. package/dist/C_Captcha.css +46 -21
  43. package/dist/C_Captcha.d.cts +3 -2
  44. package/dist/C_Captcha.d.ts +3 -2
  45. package/dist/C_Captcha.js +1 -1
  46. package/dist/C_Captcha2.cjs +1 -0
  47. package/dist/C_Captcha2.js +1 -2
  48. package/dist/C_Cascade.cjs +1 -1
  49. package/dist/C_Cascade.css +0 -2
  50. package/dist/C_Cascade.d.cts +1 -1
  51. package/dist/C_Cascade.d.ts +1 -1
  52. package/dist/C_Cascade2.cjs +1 -0
  53. package/dist/C_Cascade2.js +1 -2
  54. package/dist/C_Chat.cjs +1 -1
  55. package/dist/C_Chat.css +0 -2
  56. package/dist/C_Chat.d.cts +1 -1
  57. package/dist/C_Chat.d.ts +1 -1
  58. package/dist/C_Chat.js +1 -1
  59. package/dist/C_Chat2.cjs +1 -0
  60. package/dist/C_Chat2.js +1 -2
  61. package/dist/C_City.cjs +1 -1
  62. package/dist/C_City.css +28 -30
  63. package/dist/C_City.d.cts +1 -1
  64. package/dist/C_City.d.ts +1 -1
  65. package/dist/C_City2.cjs +1 -0
  66. package/dist/C_City2.js +1 -2
  67. package/dist/C_Code.cjs +1 -1
  68. package/dist/C_Code.css +241 -4
  69. package/dist/C_Code.d.cts +1 -1
  70. package/dist/C_Code.d.ts +1 -1
  71. package/dist/C_Code.js +1 -1
  72. package/dist/C_Code2.cjs +1 -0
  73. package/dist/C_Code2.js +1 -2
  74. package/dist/C_CollapsePanel.cjs +1 -1
  75. package/dist/C_CollapsePanel.css +12 -6
  76. package/dist/C_CollapsePanel.d.cts +1 -1
  77. package/dist/C_CollapsePanel.js +1 -1
  78. package/dist/C_CollapsePanel2.cjs +1 -0
  79. package/dist/C_CollapsePanel2.js +1 -2
  80. package/dist/C_ContextMenu.cjs +1 -1
  81. package/dist/C_ContextMenu.css +16 -18
  82. package/dist/C_ContextMenu.d.cts +1 -1
  83. package/dist/C_ContextMenu.d.ts +1 -1
  84. package/dist/C_ContextMenu.js +1 -1
  85. package/dist/C_ContextMenu2.cjs +1 -0
  86. package/dist/C_ContextMenu2.js +1 -2
  87. package/dist/C_Cron.cjs +1 -1
  88. package/dist/C_Cron.css +18 -8
  89. package/dist/C_Cron.d.cts +1 -1
  90. package/dist/C_Cron.js +1 -1
  91. package/dist/C_Cron2.cjs +1 -0
  92. package/dist/C_Cron2.js +1 -2
  93. package/dist/C_Date.cjs +1 -1
  94. package/dist/C_Date.css +1 -0
  95. package/dist/C_Date.d.cts +2 -2
  96. package/dist/C_Date.d.ts +2 -2
  97. package/dist/C_Date2.cjs +1 -0
  98. package/dist/C_Date2.js +1 -2
  99. package/dist/C_Draggable.cjs +1 -1
  100. package/dist/C_Draggable.css +9 -5
  101. package/dist/C_Draggable.d.cts +1 -1
  102. package/dist/C_Draggable2.cjs +1 -0
  103. package/dist/C_Draggable2.js +1 -2
  104. package/dist/C_Editor.cjs +1 -1
  105. package/dist/C_Editor.css +80 -67
  106. package/dist/C_Editor.d.cts +1 -1
  107. package/dist/C_Editor.d.ts +1 -1
  108. package/dist/C_Editor2.cjs +1 -0
  109. package/dist/C_Editor2.js +1 -2
  110. package/dist/C_FilePreview.cjs +1 -1
  111. package/dist/C_FilePreview.css +0 -2
  112. package/dist/C_FilePreview.d.cts +1 -1
  113. package/dist/C_FilePreview.js +1 -1
  114. package/dist/C_FilePreview2.cjs +1 -0
  115. package/dist/C_FilePreview2.js +1 -2
  116. package/dist/C_Form.base.css +980 -0
  117. package/dist/C_Form.cjs +1 -1
  118. package/dist/C_Form.css +256 -155
  119. package/dist/C_Form.d.cts +2 -2
  120. package/dist/C_Form.d.ts +2 -2
  121. package/dist/C_Form.js +1 -1
  122. package/dist/C_Form2.cjs +1 -0
  123. package/dist/C_Form2.js +1 -2
  124. package/dist/C_FormSearch.cjs +1 -1
  125. package/dist/C_FormSearch.css +47 -47
  126. package/dist/C_FormSearch.d.cts +2 -2
  127. package/dist/C_FormSearch.d.ts +2 -2
  128. package/dist/C_FormSearch2.cjs +1 -0
  129. package/dist/C_FormSearch2.js +1 -2
  130. package/dist/C_FormulaEditor.cjs +1 -1
  131. package/dist/C_FormulaEditor.css +64 -38
  132. package/dist/C_FormulaEditor.d.cts +1 -1
  133. package/dist/C_FormulaEditor.js +1 -1
  134. package/dist/C_FormulaEditor2.cjs +2 -0
  135. package/dist/C_FormulaEditor2.js +2 -2
  136. package/dist/C_FullCalendar.cjs +1 -1
  137. package/dist/C_FullCalendar.css +0 -2
  138. package/dist/C_FullCalendar.d.cts +1 -1
  139. package/dist/C_FullCalendar2.cjs +1 -0
  140. package/dist/C_FullCalendar2.js +1 -2
  141. package/dist/C_GlobalSearch.cjs +1 -1
  142. package/dist/C_GlobalSearch.css +111 -113
  143. package/dist/C_GlobalSearch.d.cts +1 -1
  144. package/dist/C_GlobalSearch2.cjs +1 -0
  145. package/dist/C_GlobalSearch2.js +1 -2
  146. package/dist/C_Guide.cjs +1 -1
  147. package/dist/C_Guide.css +1 -0
  148. package/dist/C_Guide.d.cts +1 -1
  149. package/dist/C_Guide.d.ts +1 -1
  150. package/dist/C_Guide.js +1 -1
  151. package/dist/C_Guide2.cjs +1 -0
  152. package/dist/C_Guide2.js +1 -2
  153. package/dist/C_Icon.cjs +1 -1
  154. package/dist/C_Icon.css +58 -0
  155. package/dist/C_Icon.d.cts +1 -1
  156. package/dist/C_Icon.d.ts +1 -1
  157. package/dist/C_Icon2.cjs +1 -0
  158. package/dist/C_Icon2.js +1 -2
  159. package/dist/C_ImageCropper.cjs +1 -1
  160. package/dist/C_ImageCropper.css +0 -2
  161. package/dist/C_ImageCropper.d.cts +1 -1
  162. package/dist/C_ImageCropper.js +1 -1
  163. package/dist/C_ImageCropper2.cjs +1 -0
  164. package/dist/C_ImageCropper2.js +1 -2
  165. package/dist/C_Language.cjs +1 -1
  166. package/dist/C_Language.css +1 -0
  167. package/dist/C_Language.d.cts +1 -1
  168. package/dist/C_Language.d.ts +1 -1
  169. package/dist/C_Language.js +1 -1
  170. package/dist/C_Language2.cjs +1 -0
  171. package/dist/C_Language2.js +1 -2
  172. package/dist/C_Login.cjs +1 -1
  173. package/dist/C_Login.css +92 -76
  174. package/dist/C_Login.d.cts +1 -1
  175. package/dist/C_Login.d.ts +1 -1
  176. package/dist/C_Login.js +1 -1
  177. package/dist/C_Login2.cjs +1 -0
  178. package/dist/C_Login2.js +1 -2
  179. package/dist/C_Map.cjs +1 -1
  180. package/dist/C_Map.css +665 -5
  181. package/dist/C_Map.d.cts +1 -1
  182. package/dist/C_Map2.cjs +1 -0
  183. package/dist/C_Map2.js +1 -2
  184. package/dist/C_Markdown.cjs +1 -1
  185. package/dist/C_Markdown.css +940 -35
  186. package/dist/C_Markdown.d.cts +1 -1
  187. package/dist/C_Markdown.d.ts +1 -1
  188. package/dist/C_Markdown2.cjs +1 -0
  189. package/dist/C_Markdown2.js +1 -2
  190. package/dist/C_Menu.cjs +1 -1
  191. package/dist/C_Menu.css +1 -0
  192. package/dist/C_Menu.d.cts +3 -3
  193. package/dist/C_Menu.d.ts +3 -4
  194. package/dist/C_Menu.js +1 -1
  195. package/dist/C_Menu2.cjs +1 -0
  196. package/dist/C_Menu2.js +1 -2
  197. package/dist/C_NotificationCenter.cjs +1 -1
  198. package/dist/C_NotificationCenter.css +95 -47
  199. package/dist/C_NotificationCenter.d.cts +1 -1
  200. package/dist/C_NotificationCenter.js +1 -1
  201. package/dist/C_NotificationCenter2.cjs +1 -0
  202. package/dist/C_NotificationCenter2.js +1 -2
  203. package/dist/C_OrgChart.cjs +1 -1
  204. package/dist/C_OrgChart.css +6 -4
  205. package/dist/C_OrgChart.d.cts +1 -1
  206. package/dist/C_OrgChart.d.ts +1 -1
  207. package/dist/C_OrgChart2.cjs +1 -0
  208. package/dist/C_OrgChart2.js +1 -2
  209. package/dist/C_Progress.cjs +1 -1
  210. package/dist/C_Progress.css +1 -0
  211. package/dist/C_Progress.d.cts +1 -1
  212. package/dist/C_Progress.d.ts +1 -1
  213. package/dist/C_Progress2.cjs +1 -0
  214. package/dist/C_Progress2.js +1 -2
  215. package/dist/C_QRCode.cjs +1 -1
  216. package/dist/C_QRCode.css +8 -10
  217. package/dist/C_QRCode.d.cts +1 -1
  218. package/dist/C_QRCode2.cjs +1 -0
  219. package/dist/C_QRCode2.js +1 -2
  220. package/dist/C_Signature.cjs +1 -1
  221. package/dist/C_Signature.css +0 -2
  222. package/dist/C_Signature.d.cts +1 -1
  223. package/dist/C_Signature.js +1 -1
  224. package/dist/C_Signature2.cjs +1 -0
  225. package/dist/C_Signature2.js +1 -2
  226. package/dist/C_Skeleton.cjs +1 -1
  227. package/dist/C_Skeleton.css +0 -2
  228. package/dist/C_Skeleton.d.cts +1 -1
  229. package/dist/C_Skeleton.d.ts +1 -1
  230. package/dist/C_Skeleton2.cjs +1 -0
  231. package/dist/C_Skeleton2.js +1 -2
  232. package/dist/C_SplitPane.cjs +1 -1
  233. package/dist/C_SplitPane.css +27 -11
  234. package/dist/C_SplitPane.d.cts +1 -1
  235. package/dist/C_SplitPane.js +1 -1
  236. package/dist/C_SplitPane2.cjs +1 -0
  237. package/dist/C_SplitPane2.js +1 -2
  238. package/dist/C_Steps.cjs +1 -1
  239. package/dist/C_Steps.css +37 -39
  240. package/dist/C_Steps.d.cts +1 -1
  241. package/dist/C_Steps.d.ts +1 -1
  242. package/dist/C_Steps2.cjs +1 -0
  243. package/dist/C_Steps2.js +1 -2
  244. package/dist/C_Table.base.css +1588 -0
  245. package/dist/C_Table.cjs +1 -1
  246. package/dist/C_Table.css +1202 -112
  247. package/dist/C_Table.d.cts +2 -2
  248. package/dist/C_Table.d.ts +11 -2
  249. package/dist/C_Table.js +1 -1
  250. package/dist/C_TagsView.cjs +1 -1
  251. package/dist/C_TagsView.css +44 -40
  252. package/dist/C_TagsView.d.cts +2 -2
  253. package/dist/C_TagsView.d.ts +2 -3
  254. package/dist/C_TagsView.js +1 -1
  255. package/dist/C_TagsView2.cjs +1 -0
  256. package/dist/C_TagsView2.js +1 -2
  257. package/dist/C_Theme.cjs +1 -1
  258. package/dist/C_Theme.css +1 -0
  259. package/dist/C_Theme.d.cts +1 -1
  260. package/dist/C_Theme.d.ts +1 -1
  261. package/dist/C_Theme.js +1 -1
  262. package/dist/C_Theme2.cjs +1 -0
  263. package/dist/C_Theme2.js +1 -2
  264. package/dist/C_Time.cjs +1 -1
  265. package/dist/C_Time.css +0 -2
  266. package/dist/C_Time.d.cts +2 -2
  267. package/dist/C_Time.d.ts +2 -2
  268. package/dist/C_Time2.cjs +1 -0
  269. package/dist/C_Time2.js +1 -2
  270. package/dist/C_Timeline.cjs +1 -1
  271. package/dist/C_Timeline.css +44 -46
  272. package/dist/C_Timeline.d.cts +1 -1
  273. package/dist/C_Timeline.js +1 -1
  274. package/dist/C_Timeline2.cjs +1 -0
  275. package/dist/C_Timeline2.js +1 -2
  276. package/dist/C_Transfer.cjs +1 -1
  277. package/dist/C_Transfer.css +21 -9
  278. package/dist/C_Transfer.d.cts +1 -1
  279. package/dist/C_Transfer.js +1 -1
  280. package/dist/C_Transfer2.cjs +1 -0
  281. package/dist/C_Transfer2.js +1 -2
  282. package/dist/C_Tree.cjs +1 -1
  283. package/dist/C_Tree.css +0 -2
  284. package/dist/C_Tree.d.cts +1 -1
  285. package/dist/C_Tree.js +1 -1
  286. package/dist/C_Tree2.cjs +1 -0
  287. package/dist/C_Tree2.js +1 -2
  288. package/dist/C_Upload.cjs +1 -1
  289. package/dist/C_Upload.css +17 -17
  290. package/dist/C_Upload.d.cts +1 -1
  291. package/dist/C_Upload.js +1 -1
  292. package/dist/C_Upload2.cjs +1 -0
  293. package/dist/C_Upload2.js +1 -25
  294. package/dist/C_VideoPlayer.cjs +1 -1
  295. package/dist/C_VideoPlayer.css +35 -24
  296. package/dist/C_VideoPlayer.d.cts +2 -2
  297. package/dist/C_VideoPlayer.d.ts +2 -2
  298. package/dist/C_VideoPlayer.js +1 -1
  299. package/dist/C_VideoPlayer2.cjs +3 -0
  300. package/dist/C_VideoPlayer2.js +3 -4
  301. package/dist/C_VtableGantt.cjs +1 -1
  302. package/dist/C_VtableGantt.css +15 -7
  303. package/dist/C_VtableGantt.d.cts +1 -1
  304. package/dist/C_VtableGantt.d.ts +1 -1
  305. package/dist/C_VtableGantt.js +1 -1
  306. package/dist/C_VtableGantt2.cjs +1 -0
  307. package/dist/C_VtableGantt2.js +1 -2
  308. package/dist/C_WaterFall.cjs +1 -1
  309. package/dist/C_WaterFall.css +15 -17
  310. package/dist/C_WaterFall.d.cts +1 -1
  311. package/dist/C_WaterFall2.cjs +1 -0
  312. package/dist/C_WaterFall2.js +1 -2
  313. package/dist/C_WorkFlow.cjs +1 -1
  314. package/dist/C_WorkFlow.css +257 -197
  315. package/dist/C_WorkFlow.d.cts +1 -1
  316. package/dist/C_WorkFlow.js +1 -1
  317. package/dist/C_WorkFlow2.cjs +1 -0
  318. package/dist/C_WorkFlow2.js +1 -2
  319. package/dist/city.cjs +1 -0
  320. package/dist/city.js +1 -2
  321. package/dist/config.cjs +1 -0
  322. package/dist/config.d.cts +33 -0
  323. package/dist/config.d.ts +33 -0
  324. package/dist/config.js +1 -0
  325. package/dist/constants.d.cts +272 -0
  326. package/dist/constants.d.ts +1 -2
  327. package/dist/constants2.d.cts +177 -0
  328. package/dist/constants2.d.ts +3 -4
  329. package/dist/constants3.d.cts +479 -0
  330. package/dist/constants3.d.ts +15 -11
  331. package/dist/constants4.d.cts +420 -0
  332. package/dist/constants4.d.ts +30 -40
  333. package/dist/constants5.d.cts +491 -0
  334. package/dist/constants5.d.ts +22 -6
  335. package/dist/data.cjs +1 -0
  336. package/dist/data.d.cts +67 -0
  337. package/dist/data.d.ts +11 -11
  338. package/dist/data.js +1 -0
  339. package/dist/export-helper.cjs +1 -0
  340. package/dist/helpers.cjs +1 -0
  341. package/dist/helpers.js +1 -0
  342. package/dist/html.cjs +1 -0
  343. package/dist/html.js +1 -0
  344. package/dist/images/marker-icon-2x.png +0 -0
  345. package/dist/images/marker-icon.png +0 -0
  346. package/dist/images/marker-shadow.png +0 -0
  347. package/dist/index.cjs +1 -2
  348. package/dist/index.d.cts +60 -99
  349. package/dist/index.d.ts +41 -83
  350. package/dist/index.js +1 -2
  351. package/dist/index.vue.d.cts +79 -0
  352. package/dist/index.vue.d.ts +3 -4
  353. package/dist/index10.vue.d.cts +47 -0
  354. package/dist/index10.vue.d.ts +80 -40
  355. package/dist/index11.vue.d.cts +88 -0
  356. package/dist/index11.vue.d.ts +68 -78
  357. package/dist/index12.vue.d.cts +79 -0
  358. package/dist/index12.vue.d.ts +50 -46
  359. package/dist/index13.vue.d.cts +64 -0
  360. package/dist/index13.vue.d.ts +99 -34
  361. package/dist/index14.vue.d.cts +106 -0
  362. package/dist/index14.vue.d.ts +82 -99
  363. package/dist/index15.vue.d.cts +91 -0
  364. package/dist/index15.vue.d.ts +25 -83
  365. package/dist/index16.vue.d.cts +34 -0
  366. package/dist/index16.vue.d.ts +113 -24
  367. package/dist/index17.vue.d.cts +124 -0
  368. package/dist/index17.vue.d.ts +76 -110
  369. package/dist/index18.vue.d.cts +83 -0
  370. package/dist/index18.vue.d.ts +67 -73
  371. package/dist/index19.vue.d.cts +101 -0
  372. package/dist/index19.vue.d.ts +93 -59
  373. package/dist/index2.vue.d.cts +82 -0
  374. package/dist/index2.vue.d.ts +6 -7
  375. package/dist/index20.vue.d.cts +54 -0
  376. package/dist/index20.vue.d.ts +40 -88
  377. package/dist/index21.vue.d.cts +103 -0
  378. package/dist/index21.vue.d.ts +89 -41
  379. package/dist/index22.vue.d.cts +45 -0
  380. package/dist/index22.vue.d.ts +37 -96
  381. package/dist/index23.vue.d.cts +19 -0
  382. package/dist/index23.vue.d.ts +8 -35
  383. package/dist/{index28.vue.d.ts → index24.vue.d.cts} +2 -3
  384. package/dist/index24.vue.d.ts +650 -86
  385. package/dist/index25.vue.d.cts +96 -0
  386. package/dist/index25.vue.d.ts +86 -10
  387. package/dist/index26.vue.d.cts +111 -0
  388. package/dist/index26.vue.d.ts +4 -5
  389. package/dist/index27.vue.d.cts +80 -0
  390. package/dist/index27.vue.d.ts +4 -5
  391. package/dist/index3.vue.d.cts +87 -0
  392. package/dist/index3.vue.d.ts +4 -5
  393. package/dist/index4.vue.d.cts +37 -0
  394. package/dist/index4.vue.d.ts +4 -5
  395. package/dist/index5.vue.d.cts +133 -0
  396. package/dist/index5.vue.d.ts +2 -3
  397. package/dist/index6.vue.d.cts +29 -0
  398. package/dist/index6.vue.d.ts +25 -38
  399. package/dist/index7.vue.d.cts +32 -0
  400. package/dist/index7.vue.d.ts +117 -23
  401. package/dist/index8.vue.d.cts +127 -0
  402. package/dist/index8.vue.d.ts +17 -111
  403. package/dist/index9.vue.d.cts +34 -0
  404. package/dist/index9.vue.d.ts +38 -26
  405. package/dist/{menuAdapter.d.ts → public.d.ts} +2 -3
  406. package/dist/resolver.cjs +1 -2
  407. package/dist/resolver.d.cts +6 -4
  408. package/dist/resolver.d.ts +6 -4
  409. package/dist/resolver.js +1 -2
  410. package/dist/storage.cjs +1 -0
  411. package/dist/storage.js +1 -2
  412. package/dist/style.css +9864 -7702
  413. package/dist/types.d.cts +107 -0
  414. package/dist/types.d.ts +58 -0
  415. package/dist/types2.d.cts +58 -0
  416. package/dist/useCalendarEvents.d.cts +150 -0
  417. package/dist/useCalendarEvents.d.ts +2 -3
  418. package/dist/useCollapsePanel.d.cts +133 -0
  419. package/dist/useCollapsePanel.d.ts +7 -8
  420. package/dist/useCropperCore.d.cts +104 -0
  421. package/dist/useCropperCore.d.ts +8 -6
  422. package/dist/useDraggableLayout.d.cts +196 -0
  423. package/dist/useDraggableLayout.d.ts +17 -15
  424. package/dist/useDynamicFormState.d.cts +749 -0
  425. package/dist/useDynamicFormState.d.ts +234 -112
  426. package/dist/useEdgeInteraction.d.cts +324 -0
  427. package/dist/useEdgeInteraction.d.ts +5 -6
  428. package/dist/useFullscreen.d.cts +165 -0
  429. package/dist/useFullscreen.d.ts +3 -4
  430. package/dist/useGlobalSearch.d.cts +110 -0
  431. package/dist/useGlobalSearch.d.ts +17 -9
  432. package/dist/useInfiniteScroll.d.cts +177 -0
  433. package/dist/useInfiniteScroll.d.ts +11 -3
  434. package/dist/useQRCode.d.cts +89 -0
  435. package/dist/useQRCode.d.ts +7 -5
  436. package/dist/useSearchState.d.cts +871 -0
  437. package/dist/useSearchState.d.ts +828 -28
  438. package/dist/useSignatureHistory.d.cts +197 -0
  439. package/dist/useSignatureHistory.d.ts +9 -4
  440. package/dist/useSplitResize.d.cts +157 -0
  441. package/dist/useSplitResize.d.ts +6 -7
  442. package/dist/useTableQuery.cjs +3 -0
  443. package/dist/useTableQuery.d.cts +1684 -0
  444. package/dist/useTableQuery.d.ts +1684 -0
  445. package/dist/useTableQuery.js +3 -0
  446. package/dist/useTimeSelection.d.cts +116 -0
  447. package/dist/useTimeSelection.d.ts +19 -8
  448. package/dist/useTreeOperations.d.cts +185 -0
  449. package/dist/useTreeOperations.d.ts +10 -8
  450. package/dist/useWorkflowValidation.d.cts +1051 -0
  451. package/dist/useWorkflowValidation.d.ts +62 -63
  452. package/dist/verification.d.cts +58 -0
  453. package/dist/verification.d.ts +58 -0
  454. package/package.json +102 -69
  455. package/dist/C_ActionBar-BAcfOfRw.css.map +0 -1
  456. package/dist/C_ActionBar-DG2HW7t5.css.map +0 -1
  457. package/dist/C_ActionBar.js.map +0 -1
  458. package/dist/C_ActionBar2.js.map +0 -1
  459. package/dist/C_AntV-D_jTK8oZ.css.map +0 -1
  460. package/dist/C_AntV-DanTAq22.css.map +0 -1
  461. package/dist/C_AntV.js.map +0 -1
  462. package/dist/C_AntV2.js.map +0 -1
  463. package/dist/C_AudioPlayer-BnYIJXPD.css.map +0 -1
  464. package/dist/C_AudioPlayer-ldTy9Y34.css.map +0 -1
  465. package/dist/C_AudioPlayer.js.map +0 -1
  466. package/dist/C_AudioPlayer2.js.map +0 -1
  467. package/dist/C_AvatarGroup-B9d-GJ9c.css.map +0 -1
  468. package/dist/C_AvatarGroup-SIzxuuZr.css.map +0 -1
  469. package/dist/C_AvatarGroup.js.map +0 -1
  470. package/dist/C_AvatarGroup2.js.map +0 -1
  471. package/dist/C_Barcode-CAU2mFcx.css.map +0 -1
  472. package/dist/C_Barcode-D2oJaEg3.css.map +0 -1
  473. package/dist/C_Barcode.js.map +0 -1
  474. package/dist/C_Barcode2.js.map +0 -1
  475. package/dist/C_Breadcrumb-BiH0d9AI.css.map +0 -1
  476. package/dist/C_Breadcrumb-Bqqrl8nX.css.map +0 -1
  477. package/dist/C_Breadcrumb.js.map +0 -1
  478. package/dist/C_Breadcrumb2.js.map +0 -1
  479. package/dist/C_Captcha-B3yrtG7t.css.map +0 -1
  480. package/dist/C_Captcha-D4hDdWDu.css.map +0 -1
  481. package/dist/C_Captcha.js.map +0 -1
  482. package/dist/C_Captcha2.js.map +0 -1
  483. package/dist/C_Cascade-CmCrLVft.css.map +0 -1
  484. package/dist/C_Cascade-U6A5AJJb.css.map +0 -1
  485. package/dist/C_Cascade.js.map +0 -1
  486. package/dist/C_Cascade2.js.map +0 -1
  487. package/dist/C_Chat-CF1SU7mc.css.map +0 -1
  488. package/dist/C_Chat-DeUv6uuI.css.map +0 -1
  489. package/dist/C_Chat.js.map +0 -1
  490. package/dist/C_Chat2.js.map +0 -1
  491. package/dist/C_City-DdkX6u64.css.map +0 -1
  492. package/dist/C_City-iP-dIvui.css.map +0 -1
  493. package/dist/C_City.js.map +0 -1
  494. package/dist/C_City2.js.map +0 -1
  495. package/dist/C_Code-BWmJMNS7.css.map +0 -1
  496. package/dist/C_Code-k14vjgVl.css.map +0 -1
  497. package/dist/C_Code.js.map +0 -1
  498. package/dist/C_Code2.js.map +0 -1
  499. package/dist/C_CollapsePanel-BSKcWSmY.css.map +0 -1
  500. package/dist/C_CollapsePanel-DRvRq8tB.css.map +0 -1
  501. package/dist/C_CollapsePanel.js.map +0 -1
  502. package/dist/C_CollapsePanel2.js.map +0 -1
  503. package/dist/C_ContextMenu-Bn_uhJSo.css.map +0 -1
  504. package/dist/C_ContextMenu-C9iuxG2n.css.map +0 -1
  505. package/dist/C_ContextMenu.js.map +0 -1
  506. package/dist/C_ContextMenu2.js.map +0 -1
  507. package/dist/C_Cron-BIQ2By9v.css.map +0 -1
  508. package/dist/C_Cron-BNSAGD6A.css.map +0 -1
  509. package/dist/C_Cron.js.map +0 -1
  510. package/dist/C_Cron2.js.map +0 -1
  511. package/dist/C_Date.js.map +0 -1
  512. package/dist/C_Date2.js.map +0 -1
  513. package/dist/C_Draggable-BDAbk4s5.css.map +0 -1
  514. package/dist/C_Draggable-UhfDLXop.css.map +0 -1
  515. package/dist/C_Draggable.js.map +0 -1
  516. package/dist/C_Draggable2.js.map +0 -1
  517. package/dist/C_Editor-CZOaChru.css.map +0 -1
  518. package/dist/C_Editor-kJAQq5z6.css.map +0 -1
  519. package/dist/C_Editor.js.map +0 -1
  520. package/dist/C_Editor2.js.map +0 -1
  521. package/dist/C_FilePreview-Bs_omgJS.css.map +0 -1
  522. package/dist/C_FilePreview-DbwoyF2j.css.map +0 -1
  523. package/dist/C_FilePreview.js.map +0 -1
  524. package/dist/C_FilePreview2.js.map +0 -1
  525. package/dist/C_Form-B_2ql62V.css.map +0 -1
  526. package/dist/C_Form-CX-7xsz3.css.map +0 -1
  527. package/dist/C_Form.js.map +0 -1
  528. package/dist/C_Form2.js.map +0 -1
  529. package/dist/C_FormSearch-C3F8peAY.css.map +0 -1
  530. package/dist/C_FormSearch-qW56bJPo.css.map +0 -1
  531. package/dist/C_FormSearch.js.map +0 -1
  532. package/dist/C_FormSearch2.js.map +0 -1
  533. package/dist/C_FormulaEditor-ChOvDbsZ.css.map +0 -1
  534. package/dist/C_FormulaEditor-mfiIUDY4.css.map +0 -1
  535. package/dist/C_FormulaEditor.js.map +0 -1
  536. package/dist/C_FormulaEditor2.js.map +0 -1
  537. package/dist/C_FullCalendar-C2nsqrgS.css.map +0 -1
  538. package/dist/C_FullCalendar-DdFtXVhy.css.map +0 -1
  539. package/dist/C_FullCalendar.js.map +0 -1
  540. package/dist/C_FullCalendar2.js.map +0 -1
  541. package/dist/C_GlobalSearch-Dq4Wo01K.css.map +0 -1
  542. package/dist/C_GlobalSearch-RB7J43gu.css.map +0 -1
  543. package/dist/C_GlobalSearch.js.map +0 -1
  544. package/dist/C_GlobalSearch2.js.map +0 -1
  545. package/dist/C_Guide.js.map +0 -1
  546. package/dist/C_Guide2.js.map +0 -1
  547. package/dist/C_Icon.js.map +0 -1
  548. package/dist/C_Icon2.js.map +0 -1
  549. package/dist/C_ImageCropper-CdTX2nxU.css.map +0 -1
  550. package/dist/C_ImageCropper-ilOqPklJ.css.map +0 -1
  551. package/dist/C_ImageCropper.js.map +0 -1
  552. package/dist/C_ImageCropper2.js.map +0 -1
  553. package/dist/C_Language.js.map +0 -1
  554. package/dist/C_Language2.js.map +0 -1
  555. package/dist/C_Login-B3qYkLrN.css.map +0 -1
  556. package/dist/C_Login-xMI1_ojE.css.map +0 -1
  557. package/dist/C_Login.js.map +0 -1
  558. package/dist/C_Login2.js.map +0 -1
  559. package/dist/C_Map-CWMMN58_.css.map +0 -1
  560. package/dist/C_Map-xSR2H-9o.css.map +0 -1
  561. package/dist/C_Map.js.map +0 -1
  562. package/dist/C_Map2.js.map +0 -1
  563. package/dist/C_Markdown-C7lTVJ1S.css.map +0 -1
  564. package/dist/C_Markdown-CZDm_baQ.css.map +0 -1
  565. package/dist/C_Markdown.js.map +0 -1
  566. package/dist/C_Markdown2.js.map +0 -1
  567. package/dist/C_Menu.js.map +0 -1
  568. package/dist/C_Menu2.js.map +0 -1
  569. package/dist/C_NotificationCenter-D7GGGWg7.css.map +0 -1
  570. package/dist/C_NotificationCenter-SSmeHA45.css.map +0 -1
  571. package/dist/C_NotificationCenter.js.map +0 -1
  572. package/dist/C_NotificationCenter2.js.map +0 -1
  573. package/dist/C_OrgChart-ChZu346B.css.map +0 -1
  574. package/dist/C_OrgChart-DqBufOv5.css.map +0 -1
  575. package/dist/C_OrgChart.js.map +0 -1
  576. package/dist/C_OrgChart2.js.map +0 -1
  577. package/dist/C_Progress.js.map +0 -1
  578. package/dist/C_Progress2.js.map +0 -1
  579. package/dist/C_QRCode-B_1lHUdx.css.map +0 -1
  580. package/dist/C_QRCode-CuQRYu0s.css.map +0 -1
  581. package/dist/C_QRCode.js.map +0 -1
  582. package/dist/C_QRCode2.js.map +0 -1
  583. package/dist/C_Signature-Bp-RBpAl.css.map +0 -1
  584. package/dist/C_Signature-Cb2VW1ok.css.map +0 -1
  585. package/dist/C_Signature.js.map +0 -1
  586. package/dist/C_Signature2.js.map +0 -1
  587. package/dist/C_Skeleton-BNPbaut7.css.map +0 -1
  588. package/dist/C_Skeleton-Djsjw8XY.css.map +0 -1
  589. package/dist/C_Skeleton.js.map +0 -1
  590. package/dist/C_Skeleton2.js.map +0 -1
  591. package/dist/C_SplitPane-CgGWYsRE.css.map +0 -1
  592. package/dist/C_SplitPane-DGf6WEeR.css.map +0 -1
  593. package/dist/C_SplitPane.js.map +0 -1
  594. package/dist/C_SplitPane2.js.map +0 -1
  595. package/dist/C_Steps-27Exh7PM.css.map +0 -1
  596. package/dist/C_Steps-DU9ZsFrs.css.map +0 -1
  597. package/dist/C_Steps.js.map +0 -1
  598. package/dist/C_Steps2.js.map +0 -1
  599. package/dist/C_Table--kbQaaBv.css.map +0 -1
  600. package/dist/C_Table-D631kdXC.css.map +0 -1
  601. package/dist/C_Table.js.map +0 -1
  602. package/dist/C_Table2.js +0 -4
  603. package/dist/C_Table2.js.map +0 -1
  604. package/dist/C_TagsView-BN8vMRsY.css.map +0 -1
  605. package/dist/C_TagsView-s1dnSJAV.css.map +0 -1
  606. package/dist/C_TagsView.js.map +0 -1
  607. package/dist/C_TagsView2.js.map +0 -1
  608. package/dist/C_Theme.js.map +0 -1
  609. package/dist/C_Theme2.js.map +0 -1
  610. package/dist/C_Time-BQ9PGy8B.css.map +0 -1
  611. package/dist/C_Time-CO73krC6.css.map +0 -1
  612. package/dist/C_Time.js.map +0 -1
  613. package/dist/C_Time2.js.map +0 -1
  614. package/dist/C_Timeline-4rk0Dzgn.css.map +0 -1
  615. package/dist/C_Timeline-DdESmqwL.css.map +0 -1
  616. package/dist/C_Timeline.js.map +0 -1
  617. package/dist/C_Timeline2.js.map +0 -1
  618. package/dist/C_Transfer-B-vjvhjI.css.map +0 -1
  619. package/dist/C_Transfer-EzdHzZNv.css.map +0 -1
  620. package/dist/C_Transfer.js.map +0 -1
  621. package/dist/C_Transfer2.js.map +0 -1
  622. package/dist/C_Tree-CsXgGfhn.css.map +0 -1
  623. package/dist/C_Tree-P7eRpnOa.css.map +0 -1
  624. package/dist/C_Tree.js.map +0 -1
  625. package/dist/C_Tree2.js.map +0 -1
  626. package/dist/C_Upload-CHjiVVh7.css.map +0 -1
  627. package/dist/C_Upload-DE3OLsZD.css.map +0 -1
  628. package/dist/C_Upload.js.map +0 -1
  629. package/dist/C_Upload2.js.map +0 -1
  630. package/dist/C_VideoPlayer-CnaNOFtW.css.map +0 -1
  631. package/dist/C_VideoPlayer-cnP88zNV.css.map +0 -1
  632. package/dist/C_VideoPlayer.js.map +0 -1
  633. package/dist/C_VideoPlayer2.js.map +0 -1
  634. package/dist/C_VtableGantt-CZlJgvpC.css.map +0 -1
  635. package/dist/C_VtableGantt-DKfQkql0.css.map +0 -1
  636. package/dist/C_VtableGantt.js.map +0 -1
  637. package/dist/C_VtableGantt2.js.map +0 -1
  638. package/dist/C_WaterFall-BJ_39ext.css.map +0 -1
  639. package/dist/C_WaterFall-cYc1vntE.css.map +0 -1
  640. package/dist/C_WaterFall.js.map +0 -1
  641. package/dist/C_WaterFall2.js.map +0 -1
  642. package/dist/C_WorkFlow-BFzRGGhJ.css.map +0 -1
  643. package/dist/C_WorkFlow-DSyCOvRp.css.map +0 -1
  644. package/dist/C_WorkFlow.js.map +0 -1
  645. package/dist/C_WorkFlow2.js.map +0 -1
  646. package/dist/_shared.cjs +0 -1
  647. package/dist/_shared.d.cts +0 -2
  648. package/dist/_shared.d.ts +0 -2
  649. package/dist/_shared.js +0 -1
  650. package/dist/city.js.map +0 -1
  651. package/dist/index.cjs.map +0 -1
  652. package/dist/index.js.map +0 -1
  653. package/dist/menuAdapter.js +0 -2
  654. package/dist/menuAdapter.js.map +0 -1
  655. package/dist/resolver.cjs.map +0 -1
  656. package/dist/resolver.js.map +0 -1
  657. package/dist/storage.js.map +0 -1
  658. package/dist/useModalEdit.d.ts +0 -1308
  659. /package/dist/{chunk.js → chunk.cjs} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"file":"C_NotificationCenter.js","names":["getItem","$emit","count","hasUrgent","message","$emit","activeCategory","$emit","messages","scrollHeight","loading","hasMore","$emit","message","scrollHeight"],"sources":["../src/components/C_NotificationCenter/constants.ts","../src/components/C_NotificationCenter/composables/useNotificationWS.ts","../src/components/C_NotificationCenter/composables/useNotificationCore.ts","../src/components/C_NotificationCenter/components/NotificationBadge.vue","../src/components/C_NotificationCenter/components/NotificationBadge.vue","../src/components/C_NotificationCenter/composables/useNotificationFormat.ts","../src/components/C_NotificationCenter/components/NotificationItem.vue","../src/components/C_NotificationCenter/components/NotificationItem.vue","../src/components/C_NotificationCenter/components/NotificationList.vue","../src/components/C_NotificationCenter/components/NotificationList.vue","../src/components/C_NotificationCenter/components/NotificationDetail.vue","../src/components/C_NotificationCenter/components/NotificationDetail.vue","../src/components/C_NotificationCenter/index.vue","../src/components/C_NotificationCenter/index.vue"],"sourcesContent":["/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 通知中心常量配置\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport type { NotificationCategory, NotificationPriority } from \"./types\";\r\n\r\n/* ─── 默认配置 ─────────────────────────────────── */\r\n\r\n/** 默认角标最大显示数 */\r\nexport const DEFAULT_MAX_BADGE_COUNT = 99;\r\n\r\n/** 默认每页条数 */\r\nexport const DEFAULT_PAGE_SIZE = 20;\r\n\r\n/** 默认轮询间隔(ms):60 秒 */\r\nexport const DEFAULT_POLLING_INTERVAL = 60_000;\r\n\r\n/** 默认缓存 key */\r\nexport const DEFAULT_STORAGE_KEY = \"notification_center\";\r\n\r\n/** WebSocket 默认重连间隔(ms) */\r\nexport const DEFAULT_WS_RECONNECT_INTERVAL = 3_000;\r\n\r\n/** WebSocket 默认最大重连次数 */\r\nexport const DEFAULT_WS_MAX_RECONNECT = 5;\r\n\r\n/** WebSocket 默认心跳间隔(ms) */\r\nexport const DEFAULT_WS_HEARTBEAT_INTERVAL = 30_000;\r\n\r\n/** WebSocket 默认心跳消息 */\r\nexport const DEFAULT_WS_HEARTBEAT_MESSAGE = \"ping\";\r\n\r\n/* ─── 分类配置 ─────────────────────────────────── */\r\n\r\n/** 分类标签映射 */\r\nexport const CATEGORY_MAP: Record<\r\n NotificationCategory,\r\n {\r\n label: string;\r\n icon: string;\r\n color: string;\r\n }\r\n> = {\r\n system: {\r\n label: \"系统通知\",\r\n icon: \"mdi:cog-outline\",\r\n color: \"info\",\r\n },\r\n business: {\r\n label: \"业务消息\",\r\n icon: \"mdi:briefcase-outline\",\r\n color: \"success\",\r\n },\r\n alarm: {\r\n label: \"告警预警\",\r\n icon: \"mdi:alert-outline\",\r\n color: \"warning\",\r\n },\r\n};\r\n\r\n/** 分类 Tab 列表 */\r\nexport const CATEGORY_TABS: {\r\n key: NotificationCategory | \"all\";\r\n label: string;\r\n}[] = [\r\n { key: \"all\", label: \"全部\" },\r\n { key: \"system\", label: \"系统通知\" },\r\n { key: \"business\", label: \"业务消息\" },\r\n { key: \"alarm\", label: \"告警预警\" },\r\n];\r\n\r\n/* ─── 优先级配置 ───────────────────────────────── */\r\n\r\n/** 优先级标签映射 */\r\nexport const PRIORITY_MAP: Record<\r\n NotificationPriority,\r\n {\r\n label: string;\r\n type: \"default\" | \"info\" | \"success\" | \"warning\" | \"error\";\r\n }\r\n> = {\r\n low: { label: \"低\", type: \"default\" },\r\n normal: { label: \"普通\", type: \"info\" },\r\n high: { label: \"重要\", type: \"warning\" },\r\n urgent: { label: \"紧急\", type: \"error\" },\r\n};\r\n\r\n/* ─── 模拟数据(开发环境使用) ─────────────────── */\r\n\r\n/** 模拟消息列表 */\r\nexport const MOCK_MESSAGES = [\r\n {\r\n id: \"msg-001\",\r\n title: \"系统维护通知\",\r\n summary:\r\n \"系统将于今晚 22:00-23:00 进行例行维护升级,届时部分功能可能暂时不可用。\",\r\n content:\r\n \"<p>尊敬的用户:</p><p>为了提升系统性能和稳定性,我们计划于 <strong>2026年2月27日 22:00-23:00</strong> 对系统进行例行维护升级。</p><p>维护期间,以下功能可能暂时不可用:</p><ul><li>报表导出</li><li>数据同步</li><li>定时任务</li></ul><p>请提前保存好未完成的工作,感谢您的理解与支持!</p>\",\r\n category: \"system\" as const,\r\n priority: \"high\" as const,\r\n status: \"unread\" as const,\r\n timestamp: new Date(Date.now() - 15 * 60_000).toISOString(),\r\n sender: { name: \"系统管理员\", avatar: \"\" },\r\n },\r\n {\r\n id: \"msg-002\",\r\n title: \"审批流程待处理\",\r\n summary: \"张三提交的《Q1 季度预算申请》需要您审批,请尽快处理。\",\r\n category: \"business\" as const,\r\n priority: \"urgent\" as const,\r\n status: \"unread\" as const,\r\n timestamp: new Date(Date.now() - 30 * 60_000).toISOString(),\r\n sender: {\r\n name: \"张三\",\r\n avatar:\r\n \"https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png\",\r\n },\r\n actionUrl: \"/workflow/approval\",\r\n actionText: \"去审批\",\r\n },\r\n {\r\n id: \"msg-003\",\r\n title: \"服务器 CPU 使用率告警\",\r\n summary:\r\n \"生产环境节点 prod-node-03 CPU 使用率持续超过 90%,已触发告警阈值。\",\r\n category: \"alarm\" as const,\r\n priority: \"urgent\" as const,\r\n status: \"unread\" as const,\r\n timestamp: new Date(Date.now() - 2 * 60_000).toISOString(),\r\n sender: { name: \"监控中心\" },\r\n actionUrl: \"/monitor/server\",\r\n actionText: \"查看详情\",\r\n },\r\n {\r\n id: \"msg-004\",\r\n title: \"新版本发布 v1.14.0\",\r\n summary: \"系统已升级至 v1.14.0,新增通知中心、工作流增强等多项功能。\",\r\n category: \"system\" as const,\r\n priority: \"normal\" as const,\r\n status: \"unread\" as const,\r\n timestamp: new Date(Date.now() - 3 * 3_600_000).toISOString(),\r\n sender: { name: \"系统管理员\" },\r\n },\r\n {\r\n id: \"msg-005\",\r\n title: \"项目交付提醒\",\r\n summary: \"「Robot Admin 二期」项目交付截止日期为 3月15日,当前进度 78%。\",\r\n category: \"business\" as const,\r\n priority: \"high\" as const,\r\n status: \"read\" as const,\r\n timestamp: new Date(Date.now() - 8 * 3_600_000).toISOString(),\r\n sender: { name: \"项目管理系统\" },\r\n },\r\n {\r\n id: \"msg-006\",\r\n title: \"数据库连接池告警\",\r\n summary: \"数据库连接池使用率达到 85%,建议关注并适时扩容。\",\r\n category: \"alarm\" as const,\r\n priority: \"high\" as const,\r\n status: \"read\" as const,\r\n timestamp: new Date(Date.now() - 12 * 3_600_000).toISOString(),\r\n sender: { name: \"监控中心\" },\r\n },\r\n {\r\n id: \"msg-007\",\r\n title: \"周报提交提醒\",\r\n summary: \"本周周报提交截止时间为周五 18:00,请及时提交。\",\r\n category: \"business\" as const,\r\n priority: \"normal\" as const,\r\n status: \"read\" as const,\r\n timestamp: new Date(Date.now() - 24 * 3_600_000).toISOString(),\r\n sender: { name: \"OA 系统\" },\r\n },\r\n {\r\n id: \"msg-008\",\r\n title: \"账号安全提醒\",\r\n summary: \"检测到您的账号在新设备上登录,如非本人操作请及时修改密码。\",\r\n category: \"system\" as const,\r\n priority: \"high\" as const,\r\n status: \"unread\" as const,\r\n timestamp: new Date(Date.now() - 45 * 60_000).toISOString(),\r\n sender: { name: \"安全中心\" },\r\n },\r\n];\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: WebSocket 连接管理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { ref, readonly, onBeforeUnmount } from \"vue\";\r\nimport type {\r\n NotificationWSConfig,\r\n WSConnectionStatus,\r\n WSNotificationPayload,\r\n} from \"../types\";\r\nimport {\r\n DEFAULT_WS_RECONNECT_INTERVAL,\r\n DEFAULT_WS_MAX_RECONNECT,\r\n DEFAULT_WS_HEARTBEAT_INTERVAL,\r\n DEFAULT_WS_HEARTBEAT_MESSAGE,\r\n} from \"../constants\";\r\n\r\n/**\r\n * WebSocket 连接管理\r\n *\r\n * 管理 WebSocket 的建连、重连、心跳保活、消息解析。\r\n * 支持自动重连、鉴权 token、心跳检测。\r\n */\r\nexport function useNotificationWS(\r\n onMessage: (payload: WSNotificationPayload) => void,\r\n onStatusChange?: (status: WSConnectionStatus) => void,\r\n) {\r\n /** 连接状态 */\r\n const status = ref<WSConnectionStatus>(\"disconnected\");\r\n\r\n /** WebSocket 实例 */\r\n let ws: WebSocket | null = null;\r\n /** 重连计数器 */\r\n let reconnectCount = 0;\r\n /** 重连定时器 */\r\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\r\n /** 心跳定时器 */\r\n let heartbeatTimer: ReturnType<typeof setInterval> | null = null;\r\n /** 当前配置 */\r\n let currentConfig: NotificationWSConfig | null = null;\r\n\r\n /**\r\n * 更新连接状态\r\n */\r\n function setStatus(s: WSConnectionStatus) {\r\n status.value = s;\r\n onStatusChange?.(s);\r\n }\r\n\r\n /**\r\n * 建立连接\r\n */\r\n function connect(config: NotificationWSConfig) {\r\n currentConfig = config;\r\n reconnectCount = 0;\r\n createConnection(config);\r\n }\r\n\r\n /**\r\n * 创建 WebSocket 连接\r\n */\r\n function createConnection(config: NotificationWSConfig) {\r\n cleanup();\r\n\r\n const url = buildUrl(config);\r\n setStatus(\"connecting\");\r\n\r\n try {\r\n ws = new WebSocket(url);\r\n } catch {\r\n setStatus(\"disconnected\");\r\n scheduleReconnect(config);\r\n return;\r\n }\r\n\r\n ws.addEventListener(\"open\", () => {\r\n setStatus(\"connected\");\r\n reconnectCount = 0;\r\n startHeartbeat(config);\r\n });\r\n\r\n ws.addEventListener(\"message\", (event) => {\r\n handleMessage(event.data);\r\n });\r\n\r\n ws.addEventListener(\"close\", () => {\r\n stopHeartbeat();\r\n setStatus(\"disconnected\");\r\n\r\n if (config.autoReconnect !== false) {\r\n scheduleReconnect(config);\r\n }\r\n });\r\n\r\n ws.addEventListener(\"error\", () => {\r\n /* error 事件后通常紧接 close 事件,交由 close 处理重连 */\r\n });\r\n }\r\n\r\n /**\r\n * 构建带 token 的 WebSocket URL\r\n */\r\n function buildUrl(config: NotificationWSConfig): string {\r\n const token = config.getToken?.();\r\n if (token) {\r\n const separator = config.url.includes(\"?\") ? \"&\" : \"?\";\r\n return `${config.url}${separator}token=${encodeURIComponent(token)}`;\r\n }\r\n return config.url;\r\n }\r\n\r\n /**\r\n * 处理接收到的消息\r\n */\r\n function handleMessage(raw: string | ArrayBuffer | Blob) {\r\n if (typeof raw !== \"string\") return;\r\n\r\n /* 过滤心跳响应 */\r\n if (raw === \"pong\") return;\r\n\r\n try {\r\n const payload = JSON.parse(raw) as WSNotificationPayload;\r\n onMessage(payload);\r\n } catch {\r\n /* 非标准 JSON 消息,忽略 */\r\n }\r\n }\r\n\r\n /**\r\n * 调度重连\r\n */\r\n function scheduleReconnect(config: NotificationWSConfig) {\r\n const maxAttempts = config.maxReconnectAttempts ?? DEFAULT_WS_MAX_RECONNECT;\r\n if (reconnectCount >= maxAttempts) return;\r\n\r\n reconnectCount++;\r\n setStatus(\"reconnecting\");\r\n\r\n const interval = config.reconnectInterval ?? DEFAULT_WS_RECONNECT_INTERVAL;\r\n reconnectTimer = setTimeout(() => {\r\n createConnection(config);\r\n }, interval);\r\n }\r\n\r\n /**\r\n * 启动心跳\r\n */\r\n function startHeartbeat(config: NotificationWSConfig) {\r\n const interval = config.heartbeatInterval ?? DEFAULT_WS_HEARTBEAT_INTERVAL;\r\n if (interval <= 0) return;\r\n\r\n const message = config.heartbeatMessage ?? DEFAULT_WS_HEARTBEAT_MESSAGE;\r\n heartbeatTimer = setInterval(() => {\r\n if (ws?.readyState === WebSocket.OPEN) {\r\n ws.send(message);\r\n }\r\n }, interval);\r\n }\r\n\r\n /**\r\n * 停止心跳\r\n */\r\n function stopHeartbeat() {\r\n if (heartbeatTimer) {\r\n clearInterval(heartbeatTimer);\r\n heartbeatTimer = null;\r\n }\r\n }\r\n\r\n /**\r\n * 清理资源\r\n */\r\n function cleanup() {\r\n stopHeartbeat();\r\n if (reconnectTimer) {\r\n clearTimeout(reconnectTimer);\r\n reconnectTimer = null;\r\n }\r\n if (ws) {\r\n ws.close();\r\n ws = null;\r\n }\r\n }\r\n\r\n /**\r\n * 断开连接\r\n */\r\n function disconnect() {\r\n currentConfig = null;\r\n reconnectCount = Infinity;\r\n cleanup();\r\n setStatus(\"disconnected\");\r\n }\r\n\r\n /**\r\n * 手动重新连接\r\n */\r\n function reconnect() {\r\n if (currentConfig) {\r\n reconnectCount = 0;\r\n createConnection(currentConfig);\r\n }\r\n }\r\n\r\n /* 组件卸载时自动清理 */\r\n onBeforeUnmount(cleanup);\r\n\r\n return {\r\n /** WebSocket 连接状态 */\r\n status: readonly(status),\r\n connect,\r\n disconnect,\r\n reconnect,\r\n };\r\n}\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 通知中心核心状态管理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\nimport { ref, computed, onMounted, onBeforeUnmount } from \"vue\";\r\nimport type {\r\n NotificationMessage,\r\n NotificationCategory,\r\n NotificationCenterProps,\r\n WSNotificationPayload,\r\n WSConnectionStatus,\r\n} from \"../types\";\r\nimport {\r\n DEFAULT_PAGE_SIZE,\r\n DEFAULT_POLLING_INTERVAL,\r\n DEFAULT_STORAGE_KEY,\r\n MOCK_MESSAGES,\r\n} from \"../constants\";\r\nimport { useNotificationWS } from \"./useNotificationWS\";\r\nimport { setItem, getItem } from \"../../../utils/storage\";\r\n\r\n/** 缓存数据结构 */\r\ninterface CachedState {\r\n unreadIds: string[];\r\n lastFetchTime: number;\r\n}\r\n\r\n/** 防抖持久化延迟(ms) */\r\nconst PERSIST_DEBOUNCE = 300;\r\n\r\n/**\r\n * 通知中心核心状态管理\r\n *\r\n * 统一管理消息列表、未读数、分类过滤、已读标记、\r\n * API 交互、WebSocket 桥接、轮询调度和本地缓存。\r\n */\r\nexport function useNotificationCore(props: NotificationCenterProps) {\r\n /* ─── 响应式状态 ─────────────────────────────── */\r\n\r\n /** 消息列表 */\r\n const messages = ref<NotificationMessage[]>([]);\r\n /** 当前选中分类 */\r\n const activeCategory = ref<NotificationCategory | \"all\">(\"all\");\r\n /** 是否正在加载 */\r\n const loading = ref(false);\r\n /** 总条数 */\r\n const total = ref(0);\r\n /** 当前页码 */\r\n const page = ref(1);\r\n /** 选中的消息(详情展示) */\r\n const selectedMessage = ref<NotificationMessage | null>(null);\r\n /** Popover 展开状态 */\r\n const popoverVisible = ref(false);\r\n /** WebSocket 连接状态 */\r\n const wsStatus = ref<WSConnectionStatus>(\"disconnected\");\r\n\r\n /* ─── 计算属性 ───────────────────────────────── */\r\n\r\n /** 未读消息总数 */\r\n const unreadCount = computed(\r\n () => messages.value.filter((m) => m.status === \"unread\").length,\r\n );\r\n\r\n /** 各分类未读数 */\r\n const unreadByCategory = computed(() => {\r\n const counts: Record<string, number> = { system: 0, business: 0, alarm: 0 };\r\n for (const m of messages.value) {\r\n if (m.status === \"unread\" && m.category in counts) {\r\n counts[m.category]++;\r\n }\r\n }\r\n return counts;\r\n });\r\n\r\n /** 当前分类下的消息列表 */\r\n const filteredMessages = computed(() => {\r\n if (activeCategory.value === \"all\") return messages.value;\r\n return messages.value.filter((m) => m.category === activeCategory.value);\r\n });\r\n\r\n /** 是否有更多消息可加载 */\r\n const hasMore = computed(() => messages.value.length < total.value);\r\n\r\n /* ─── 缓存管理 ───────────────────────────────── */\r\n\r\n /** 缓存 key */\r\n const storageKey = computed(() => props.storageKey ?? DEFAULT_STORAGE_KEY);\r\n\r\n /**\r\n * 从缓存恢复未读状态\r\n */\r\n function restoreFromCache() {\r\n const cached = getItem<CachedState>(storageKey.value);\r\n if (cached?.unreadIds) {\r\n const idSet = new Set(cached.unreadIds);\r\n for (const msg of messages.value) {\r\n if (idSet.has(msg.id)) {\r\n msg.status = \"unread\";\r\n }\r\n }\r\n }\r\n }\r\n\r\n /** 防抖持久化定时器 */\r\n let persistTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n /**\r\n * 持久化未读状态到本地缓存(防抖)\r\n */\r\n function persistToCache() {\r\n if (persistTimer) clearTimeout(persistTimer);\r\n persistTimer = setTimeout(() => {\r\n const unreadIds = messages.value\r\n .filter((m) => m.status === \"unread\")\r\n .map((m) => m.id);\r\n setItem<CachedState>(storageKey.value, {\r\n unreadIds,\r\n lastFetchTime: Date.now(),\r\n });\r\n persistTimer = null;\r\n }, PERSIST_DEBOUNCE);\r\n }\r\n\r\n /* ─── API 交互 ───────────────────────────────── */\r\n\r\n /**\r\n * 拉取消息列表\r\n */\r\n async function fetchMessages(reset = false) {\r\n if (reset) {\r\n page.value = 1;\r\n }\r\n\r\n loading.value = true;\r\n try {\r\n if (props.fetchNotifications) {\r\n const categoryParam =\r\n activeCategory.value === \"all\" ? undefined : activeCategory.value;\r\n const pageSize = props.pageSize ?? DEFAULT_PAGE_SIZE;\r\n const result = await props.fetchNotifications({\r\n category: categoryParam,\r\n page: page.value,\r\n pageSize,\r\n });\r\n if (reset) {\r\n messages.value = result.list;\r\n } else {\r\n messages.value.push(...result.list);\r\n }\r\n total.value = result.total;\r\n } else {\r\n /* 未配置 API 接口 → 使用模拟数据(全量加载,客户端过滤) */\r\n loadMockData();\r\n }\r\n\r\n restoreFromCache();\r\n } finally {\r\n loading.value = false;\r\n }\r\n }\r\n\r\n /**\r\n * 加载模拟数据(全量加载,filteredMessages 负责客户端过滤)\r\n */\r\n function loadMockData() {\r\n messages.value = MOCK_MESSAGES.map((m) => ({ ...m }));\r\n total.value = MOCK_MESSAGES.length;\r\n }\r\n\r\n /**\r\n * 加载更多\r\n */\r\n async function loadMore() {\r\n if (!hasMore.value || loading.value) return;\r\n page.value++;\r\n await fetchMessages();\r\n }\r\n\r\n /**\r\n * 标记指定消息为已读\r\n */\r\n async function markAsRead(ids: string[]) {\r\n const idSet = new Set(ids);\r\n\r\n /* 乐观更新 */\r\n for (const msg of messages.value) {\r\n if (idSet.has(msg.id)) {\r\n msg.status = \"read\";\r\n }\r\n }\r\n persistToCache();\r\n\r\n /* 如果配置了 API → 同步到服务端 */\r\n if (props.markAsRead) {\r\n try {\r\n await props.markAsRead(ids);\r\n } catch {\r\n /* 回滚 */\r\n for (const msg of messages.value) {\r\n if (idSet.has(msg.id)) {\r\n msg.status = \"unread\";\r\n }\r\n }\r\n persistToCache();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 标记全部已读\r\n */\r\n async function markAllAsRead(category?: NotificationCategory) {\r\n const targetMessages = category\r\n ? messages.value.filter(\r\n (m) => m.category === category && m.status === \"unread\",\r\n )\r\n : messages.value.filter((m) => m.status === \"unread\");\r\n\r\n /* 乐观更新 */\r\n for (const msg of targetMessages) {\r\n msg.status = \"read\";\r\n }\r\n persistToCache();\r\n\r\n if (props.markAllRead) {\r\n try {\r\n await props.markAllRead(category);\r\n } catch {\r\n /* 回滚 */\r\n for (const msg of targetMessages) {\r\n msg.status = \"unread\";\r\n }\r\n persistToCache();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 删除消息\r\n */\r\n async function deleteMessages(ids: string[]) {\r\n const idSet = new Set(ids);\r\n const backup = [...messages.value];\r\n\r\n /* 乐观更新 */\r\n messages.value = messages.value.filter((m) => !idSet.has(m.id));\r\n total.value = Math.max(0, total.value - ids.length);\r\n persistToCache();\r\n\r\n if (props.deleteNotification) {\r\n try {\r\n await props.deleteNotification(ids);\r\n } catch {\r\n messages.value = backup;\r\n total.value += ids.length;\r\n persistToCache();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 清空消息\r\n */\r\n async function clearMessages(category?: NotificationCategory) {\r\n const backup = [...messages.value];\r\n const backupTotal = total.value;\r\n\r\n if (category) {\r\n messages.value = messages.value.filter((m) => m.category !== category);\r\n } else {\r\n messages.value = [];\r\n }\r\n total.value = messages.value.length;\r\n persistToCache();\r\n\r\n if (props.clearNotifications) {\r\n try {\r\n await props.clearNotifications(category);\r\n } catch {\r\n messages.value = backup;\r\n total.value = backupTotal;\r\n persistToCache();\r\n }\r\n }\r\n }\r\n\r\n /* ─── WebSocket 桥接 ─────────────────────────── */\r\n\r\n /**\r\n * 处理 WebSocket 推送消息\r\n */\r\n function handleWSMessage(payload: WSNotificationPayload) {\r\n switch (payload.type) {\r\n case \"new_message\": {\r\n const msg = payload.data as NotificationMessage;\r\n /* 去重后插入到列表头部 */\r\n if (!messages.value.some((m) => m.id === msg.id)) {\r\n messages.value.unshift(msg);\r\n total.value++;\r\n persistToCache();\r\n showDesktopNotification(msg);\r\n }\r\n break;\r\n }\r\n case \"read_sync\": {\r\n const syncMessages = payload.data as NotificationMessage[];\r\n const readIds = new Set(syncMessages.map((m) => m.id));\r\n for (const msg of messages.value) {\r\n if (readIds.has(msg.id)) msg.status = \"read\";\r\n }\r\n persistToCache();\r\n break;\r\n }\r\n case \"count_update\": {\r\n /* 服务端推送的未读数可用于校准 */\r\n break;\r\n }\r\n }\r\n }\r\n\r\n /** WebSocket 连接状态变更回调 */\r\n function handleWSStatusChange(s: WSConnectionStatus) {\r\n wsStatus.value = s;\r\n }\r\n\r\n const { connect: wsConnect, disconnect: wsDisconnect } = useNotificationWS(\r\n handleWSMessage,\r\n handleWSStatusChange,\r\n );\r\n\r\n /* ─── 桌面通知 ──────────────────────────────── */\r\n\r\n /**\r\n * 发送桌面通知\r\n */\r\n function showDesktopNotification(msg: NotificationMessage) {\r\n if (!props.desktopNotification) return;\r\n if (!(\"Notification\" in window)) return;\r\n\r\n if (Notification.permission === \"granted\") {\r\n createDesktopNotification(msg);\r\n } else if (Notification.permission !== \"denied\") {\r\n Notification.requestPermission().then((permission) => {\r\n if (permission === \"granted\") {\r\n createDesktopNotification(msg);\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * 创建桌面通知实例\r\n */\r\n function createDesktopNotification(msg: NotificationMessage) {\r\n const n = new Notification(msg.title, {\r\n body: msg.summary,\r\n icon: msg.sender?.avatar || \"/robot-avatar.png\",\r\n tag: msg.id,\r\n });\r\n\r\n n.addEventListener(\"click\", () => {\r\n window.focus();\r\n selectMessage(msg);\r\n n.close();\r\n });\r\n\r\n /* 5 秒后自动关闭 */\r\n setTimeout(() => n.close(), 5000);\r\n }\r\n\r\n /* ─── 轮询 ──────────────────────────────────── */\r\n\r\n let pollingTimer: ReturnType<typeof setInterval> | null = null;\r\n\r\n /**\r\n * 启动轮询\r\n */\r\n function startPolling() {\r\n stopPolling();\r\n const interval = props.pollingInterval ?? DEFAULT_POLLING_INTERVAL;\r\n if (interval <= 0) return;\r\n\r\n pollingTimer = setInterval(() => {\r\n fetchMessages(true);\r\n }, interval);\r\n }\r\n\r\n /**\r\n * 停止轮询\r\n */\r\n function stopPolling() {\r\n if (pollingTimer) {\r\n clearInterval(pollingTimer);\r\n pollingTimer = null;\r\n }\r\n }\r\n\r\n /* ─── 交互 ──────────────────────────────────── */\r\n\r\n /**\r\n * 选中/查看消息\r\n */\r\n function selectMessage(msg: NotificationMessage) {\r\n selectedMessage.value = msg;\r\n /* 自动标记为已读 */\r\n if (msg.status === \"unread\") {\r\n markAsRead([msg.id]);\r\n }\r\n }\r\n\r\n /**\r\n * 返回列表\r\n */\r\n function clearSelection() {\r\n selectedMessage.value = null;\r\n }\r\n\r\n /**\r\n * 切换分类\r\n *\r\n * 纯客户端过滤(Mock / 已加载数据)直接切换,\r\n * API 模式则重新拉取对应分类数据。\r\n */\r\n function switchCategory(category: NotificationCategory | \"all\") {\r\n activeCategory.value = category;\r\n selectedMessage.value = null;\r\n\r\n /* 仅 API 模式需要重新拉取 */\r\n if (props.fetchNotifications) {\r\n fetchMessages(true);\r\n }\r\n }\r\n\r\n /* ─── 初始化 & 清理 ─────────────────────────── */\r\n\r\n /**\r\n * 初始化\r\n */\r\n function init() {\r\n fetchMessages(true);\r\n startPolling();\r\n\r\n /* 如果配置了 WebSocket → 建连 */\r\n if (props.wsConfig) wsConnect(props.wsConfig);\r\n }\r\n\r\n /**\r\n * 销毁\r\n */\r\n function destroy() {\r\n stopPolling();\r\n wsDisconnect();\r\n if (persistTimer) {\r\n clearTimeout(persistTimer);\r\n persistTimer = null;\r\n }\r\n }\r\n\r\n onMounted(init);\r\n onBeforeUnmount(destroy);\r\n\r\n return {\r\n /* 状态 */\r\n messages,\r\n activeCategory,\r\n loading,\r\n total,\r\n page,\r\n selectedMessage,\r\n popoverVisible,\r\n wsStatus,\r\n\r\n /* 计算 */\r\n unreadCount,\r\n unreadByCategory,\r\n filteredMessages,\r\n hasMore,\r\n\r\n /* 操作 */\r\n fetchMessages,\r\n loadMore,\r\n markAsRead,\r\n markAllAsRead,\r\n deleteMessages,\r\n clearMessages,\r\n selectMessage,\r\n clearSelection,\r\n switchCategory,\r\n\r\n /* WebSocket */\r\n connectWS: wsConnect,\r\n disconnectWS: wsDisconnect,\r\n };\r\n}\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 通知角标\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"notification-badge\">\r\n <NTooltip trigger=\"hover\" placement=\"bottom\">\r\n <template #trigger>\r\n <div class=\"notification-badge__trigger\" @click=\"$emit('click')\">\r\n <C_Icon\r\n name=\"mdi:bell-outline\"\r\n :size=\"18\"\r\n class=\"notification-badge__icon\"\r\n />\r\n <!-- 未读角标 -->\r\n <Transition name=\"badge-bounce\">\r\n <span v-if=\"count > 0\" class=\"notification-badge__count\">\r\n {{ displayCount }}\r\n </span>\r\n </Transition>\r\n <!-- 脉冲动画(有紧急消息时) -->\r\n <span v-if=\"hasUrgent\" class=\"notification-badge__pulse\" />\r\n </div>\r\n </template>\r\n 消息通知{{ count > 0 ? `(${count} 条未读)` : \"\" }}\r\n </NTooltip>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport { DEFAULT_MAX_BADGE_COUNT } from \"../constants\";\r\n\r\ninterface Props {\r\n /** 未读数量 */\r\n count?: number;\r\n /** 最大显示数 */\r\n maxCount?: number;\r\n /** 是否有紧急消息 */\r\n hasUrgent?: boolean;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n count: 0,\r\n maxCount: DEFAULT_MAX_BADGE_COUNT,\r\n hasUrgent: false,\r\n});\r\n\r\ndefineEmits<{\r\n click: [];\r\n}>();\r\n\r\n/** 显示文本 */\r\nconst displayCount = computed(() =>\r\n props.count > props.maxCount ? `${props.maxCount}+` : String(props.count),\r\n);\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.notification-badge {\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n\r\n &__trigger {\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n padding: 4px;\r\n border-radius: 6px;\r\n transition: all var(--c-transition, 0.2s ease);\r\n\r\n &:hover {\r\n background-color: var(--c-border);\r\n\r\n .notification-badge__icon {\r\n color: var(--c-primary);\r\n }\r\n }\r\n }\r\n\r\n &__icon {\r\n color: var(--c-text-2);\r\n transition: color var(--c-transition, 0.2s ease);\r\n }\r\n\r\n &__count {\r\n position: absolute;\r\n top: -2px;\r\n right: -4px;\r\n min-width: 16px;\r\n height: 16px;\r\n padding: 0 4px;\r\n border-radius: 8px;\r\n background: #f5222d;\r\n color: #fff;\r\n font-size: 10px;\r\n font-weight: 600;\r\n line-height: 16px;\r\n text-align: center;\r\n white-space: nowrap;\r\n pointer-events: none;\r\n box-shadow: 0 0 0 2px var(--c-bg);\r\n }\r\n\r\n &__pulse {\r\n position: absolute;\r\n top: -2px;\r\n right: -4px;\r\n width: 16px;\r\n height: 16px;\r\n border-radius: 50%;\r\n background: rgba(245, 34, 45, 0.4);\r\n animation: pulse-ring 1.5s cubic-bezier(0.215, 0.61, 0.355, 1) infinite;\r\n pointer-events: none;\r\n }\r\n}\r\n\r\n@keyframes pulse-ring {\r\n 0% {\r\n transform: scale(0.8);\r\n opacity: 1;\r\n }\r\n\r\n 100% {\r\n transform: scale(2.2);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n.badge-bounce-enter-active {\r\n animation: badge-in 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);\r\n}\r\n\r\n.badge-bounce-leave-active {\r\n animation: badge-in 0.2s ease reverse;\r\n}\r\n\r\n@keyframes badge-in {\r\n 0% {\r\n transform: scale(0);\r\n opacity: 0;\r\n }\r\n\r\n 100% {\r\n transform: scale(1);\r\n opacity: 1;\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 通知角标\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"notification-badge\">\r\n <NTooltip trigger=\"hover\" placement=\"bottom\">\r\n <template #trigger>\r\n <div class=\"notification-badge__trigger\" @click=\"$emit('click')\">\r\n <C_Icon\r\n name=\"mdi:bell-outline\"\r\n :size=\"18\"\r\n class=\"notification-badge__icon\"\r\n />\r\n <!-- 未读角标 -->\r\n <Transition name=\"badge-bounce\">\r\n <span v-if=\"count > 0\" class=\"notification-badge__count\">\r\n {{ displayCount }}\r\n </span>\r\n </Transition>\r\n <!-- 脉冲动画(有紧急消息时) -->\r\n <span v-if=\"hasUrgent\" class=\"notification-badge__pulse\" />\r\n </div>\r\n </template>\r\n 消息通知{{ count > 0 ? `(${count} 条未读)` : \"\" }}\r\n </NTooltip>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport { DEFAULT_MAX_BADGE_COUNT } from \"../constants\";\r\n\r\ninterface Props {\r\n /** 未读数量 */\r\n count?: number;\r\n /** 最大显示数 */\r\n maxCount?: number;\r\n /** 是否有紧急消息 */\r\n hasUrgent?: boolean;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n count: 0,\r\n maxCount: DEFAULT_MAX_BADGE_COUNT,\r\n hasUrgent: false,\r\n});\r\n\r\ndefineEmits<{\r\n click: [];\r\n}>();\r\n\r\n/** 显示文本 */\r\nconst displayCount = computed(() =>\r\n props.count > props.maxCount ? `${props.maxCount}+` : String(props.count),\r\n);\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.notification-badge {\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n\r\n &__trigger {\r\n position: relative;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n cursor: pointer;\r\n padding: 4px;\r\n border-radius: 6px;\r\n transition: all var(--c-transition, 0.2s ease);\r\n\r\n &:hover {\r\n background-color: var(--c-border);\r\n\r\n .notification-badge__icon {\r\n color: var(--c-primary);\r\n }\r\n }\r\n }\r\n\r\n &__icon {\r\n color: var(--c-text-2);\r\n transition: color var(--c-transition, 0.2s ease);\r\n }\r\n\r\n &__count {\r\n position: absolute;\r\n top: -2px;\r\n right: -4px;\r\n min-width: 16px;\r\n height: 16px;\r\n padding: 0 4px;\r\n border-radius: 8px;\r\n background: #f5222d;\r\n color: #fff;\r\n font-size: 10px;\r\n font-weight: 600;\r\n line-height: 16px;\r\n text-align: center;\r\n white-space: nowrap;\r\n pointer-events: none;\r\n box-shadow: 0 0 0 2px var(--c-bg);\r\n }\r\n\r\n &__pulse {\r\n position: absolute;\r\n top: -2px;\r\n right: -4px;\r\n width: 16px;\r\n height: 16px;\r\n border-radius: 50%;\r\n background: rgba(245, 34, 45, 0.4);\r\n animation: pulse-ring 1.5s cubic-bezier(0.215, 0.61, 0.355, 1) infinite;\r\n pointer-events: none;\r\n }\r\n}\r\n\r\n@keyframes pulse-ring {\r\n 0% {\r\n transform: scale(0.8);\r\n opacity: 1;\r\n }\r\n\r\n 100% {\r\n transform: scale(2.2);\r\n opacity: 0;\r\n }\r\n}\r\n\r\n.badge-bounce-enter-active {\r\n animation: badge-in 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);\r\n}\r\n\r\n.badge-bounce-leave-active {\r\n animation: badge-in 0.2s ease reverse;\r\n}\r\n\r\n@keyframes badge-in {\r\n 0% {\r\n transform: scale(0);\r\n opacity: 0;\r\n }\r\n\r\n 100% {\r\n transform: scale(1);\r\n opacity: 1;\r\n }\r\n}\r\n</style>\r\n","/*\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 通知时间格式化\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n */\r\n\r\n/** 时间单位阈值 */\r\nconst MINUTE = 60_000;\r\nconst HOUR = 3_600_000;\r\nconst DAY = 86_400_000;\r\nconst WEEK = 7 * DAY;\r\nconst MONTH = 30 * DAY;\r\nconst YEAR = 365 * DAY;\r\n\r\n/** 单例缓存 */\r\nlet _instance: ReturnType<typeof createNotificationFormat> | null = null;\r\n\r\n/**\r\n * 通知时间格式化(单例)\r\n *\r\n * 将 ISO 时间戳格式化为人类友好的相对时间描述,\r\n * 如 \"刚刚\"、\"5分钟前\"、\"昨天 14:30\" 等。\r\n */\r\nexport function useNotificationFormat() {\r\n if (!_instance) _instance = createNotificationFormat();\r\n return _instance;\r\n}\r\n\r\n/**\r\n * 创建格式化工具实例\r\n */\r\nfunction createNotificationFormat() {\r\n /**\r\n * 格式化相对时间\r\n * @param timestamp ISO 8601 时间字符串\r\n */\r\n function formatRelativeTime(timestamp: string): string {\r\n const date = new Date(timestamp);\r\n const now = Date.now();\r\n const diff = now - date.getTime();\r\n\r\n if (diff < 0) return \"刚刚\";\r\n if (diff < MINUTE) return \"刚刚\";\r\n if (diff < HOUR) return `${Math.floor(diff / MINUTE)}分钟前`;\r\n if (diff < DAY) return `${Math.floor(diff / HOUR)}小时前`;\r\n\r\n /* 判断是否是昨天 */\r\n const today = new Date();\r\n today.setHours(0, 0, 0, 0);\r\n const yesterday = new Date(today.getTime() - DAY);\r\n\r\n if (date >= yesterday && date < today) {\r\n return `昨天 ${padTime(date.getHours())}:${padTime(date.getMinutes())}`;\r\n }\r\n\r\n if (diff < WEEK) return `${Math.floor(diff / DAY)}天前`;\r\n if (diff < MONTH) return `${Math.floor(diff / WEEK)}周前`;\r\n if (diff < YEAR) return `${Math.floor(diff / MONTH)}个月前`;\r\n\r\n return formatFullDate(date);\r\n }\r\n\r\n /**\r\n * 格式化完整日期时间\r\n * @param date Date 对象\r\n */\r\n function formatFullDate(date: Date): string {\r\n const y = date.getFullYear();\r\n const m = padTime(date.getMonth() + 1);\r\n const d = padTime(date.getDate());\r\n const h = padTime(date.getHours());\r\n const min = padTime(date.getMinutes());\r\n return `${y}-${m}-${d} ${h}:${min}`;\r\n }\r\n\r\n /**\r\n * 补零\r\n * @param n 数字\r\n */\r\n function padTime(n: number): string {\r\n return n < 10 ? `0${n}` : String(n);\r\n }\r\n\r\n return {\r\n formatRelativeTime,\r\n formatFullDate,\r\n };\r\n}\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 单条消息\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div\r\n class=\"notification-item\"\r\n :class=\"{\r\n 'notification-item--unread': message.status === 'unread',\r\n 'notification-item--urgent': message.priority === 'urgent',\r\n }\"\r\n @click=\"$emit('click', message)\"\r\n >\r\n <!-- 未读指示点 -->\r\n <div class=\"notification-item__indicator\">\r\n <span\r\n v-if=\"message.status === 'unread'\"\r\n class=\"notification-item__dot\"\r\n :class=\"`notification-item__dot--${message.priority}`\"\r\n />\r\n </div>\r\n\r\n <!-- 头像 -->\r\n <div class=\"notification-item__avatar\">\r\n <NAvatar\r\n v-if=\"message.sender?.avatar\"\r\n :src=\"message.sender.avatar\"\r\n :size=\"36\"\r\n round\r\n />\r\n <div\r\n v-else\r\n class=\"notification-item__avatar-placeholder\"\r\n :class=\"`notification-item__avatar-placeholder--${message.category}`\"\r\n >\r\n <C_Icon :name=\"categoryIcon\" :size=\"16\" />\r\n </div>\r\n </div>\r\n\r\n <!-- 内容 -->\r\n <div class=\"notification-item__body\">\r\n <div class=\"notification-item__header\">\r\n <span class=\"notification-item__title\">{{ message.title }}</span>\r\n <NTag\r\n v-if=\"message.priority === 'urgent' || message.priority === 'high'\"\r\n :type=\"priorityConfig.type\"\r\n size=\"tiny\"\r\n :bordered=\"false\"\r\n round\r\n >\r\n {{ priorityConfig.label }}\r\n </NTag>\r\n </div>\r\n <div class=\"notification-item__summary\">\r\n {{ message.summary }}\r\n </div>\r\n <div class=\"notification-item__meta\">\r\n <span class=\"notification-item__time\">{{ formattedTime }}</span>\r\n <span v-if=\"message.sender?.name\" class=\"notification-item__sender\">\r\n {{ message.sender.name }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <!-- 操作区 -->\r\n <div class=\"notification-item__actions\" @click.stop>\r\n <NTooltip\r\n v-if=\"message.status === 'unread'\"\r\n trigger=\"hover\"\r\n placement=\"top\"\r\n >\r\n <template #trigger>\r\n <span\r\n class=\"notification-item__action\"\r\n @click=\"$emit('read', message.id)\"\r\n >\r\n <C_Icon name=\"mdi:check\" :size=\"16\" />\r\n </span>\r\n </template>\r\n 标记已读\r\n </NTooltip>\r\n <NTooltip trigger=\"hover\" placement=\"top\">\r\n <template #trigger>\r\n <span\r\n class=\"notification-item__action\"\r\n @click=\"$emit('delete', message.id)\"\r\n >\r\n <C_Icon name=\"mdi:delete-outline\" :size=\"16\" />\r\n </span>\r\n </template>\r\n 删除\r\n </NTooltip>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport type { NotificationMessage } from \"../types\";\r\nimport { CATEGORY_MAP, PRIORITY_MAP } from \"../constants\";\r\nimport { useNotificationFormat } from \"../composables/useNotificationFormat\";\r\n\r\ninterface Props {\r\n /** 消息数据 */\r\n message: NotificationMessage;\r\n}\r\n\r\nconst props = defineProps<Props>();\r\n\r\ndefineEmits<{\r\n click: [message: NotificationMessage];\r\n read: [id: string];\r\n delete: [id: string];\r\n}>();\r\n\r\nconst { formatRelativeTime } = useNotificationFormat();\r\n\r\n/** 分类图标 */\r\nconst categoryIcon = computed(\r\n () => CATEGORY_MAP[props.message.category]?.icon ?? \"mdi:bell-outline\",\r\n);\r\n\r\n/** 优先级配置 */\r\nconst priorityConfig = computed(\r\n () => PRIORITY_MAP[props.message.priority] ?? PRIORITY_MAP.normal,\r\n);\r\n\r\n/** 格式化时间 */\r\nconst formattedTime = computed(() =>\r\n formatRelativeTime(props.message.timestamp),\r\n);\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.notification-item {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 10px;\r\n padding: 12px 16px;\r\n cursor: pointer;\r\n transition: background-color var(--c-transition, 0.2s ease);\r\n border-bottom: 1px solid var(--c-border);\r\n\r\n &:last-child {\r\n border-bottom: none;\r\n }\r\n\r\n &:hover {\r\n background-color: var(--c-bg-card);\r\n\r\n .notification-item__actions {\r\n opacity: 1;\r\n }\r\n }\r\n\r\n &--unread {\r\n background-color: color-mix(in srgb, var(--c-primary) 4%, transparent);\r\n\r\n &:hover {\r\n background-color: color-mix(in srgb, var(--c-primary) 8%, transparent);\r\n }\r\n }\r\n\r\n &--urgent {\r\n border-left: 3px solid #f5222d;\r\n padding-left: 13px;\r\n }\r\n\r\n /* ─── 子元素 ──────────────────────────── */\r\n\r\n &__indicator {\r\n flex-shrink: 0;\r\n width: 8px;\r\n display: flex;\r\n align-items: center;\r\n padding-top: 14px;\r\n }\r\n\r\n &__dot {\r\n width: 6px;\r\n height: 6px;\r\n border-radius: 50%;\r\n background: var(--c-primary);\r\n\r\n &--urgent {\r\n background: #f5222d;\r\n box-shadow: 0 0 4px rgba(245, 34, 45, 0.5);\r\n }\r\n\r\n &--high {\r\n background: #fa8c16;\r\n }\r\n\r\n &--normal {\r\n background: var(--c-primary);\r\n }\r\n\r\n &--low {\r\n background: var(--c-text-4);\r\n }\r\n }\r\n\r\n &__avatar {\r\n flex-shrink: 0;\r\n }\r\n\r\n &__avatar-placeholder {\r\n width: 36px;\r\n height: 36px;\r\n border-radius: 50%;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 16px;\r\n color: #fff;\r\n\r\n &--system {\r\n background: linear-gradient(135deg, #667eea, #764ba2);\r\n }\r\n\r\n &--business {\r\n background: linear-gradient(135deg, #43e97b, #38f9d7);\r\n color: #0d1425;\r\n }\r\n\r\n &--alarm {\r\n background: linear-gradient(135deg, #f093fb, #f5576c);\r\n }\r\n }\r\n\r\n &__body {\r\n flex: 1;\r\n min-width: 0;\r\n }\r\n\r\n &__header {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n margin-bottom: 4px;\r\n }\r\n\r\n &__title {\r\n font-size: 13px;\r\n font-weight: 500;\r\n color: var(--c-text-1);\r\n line-height: 1.4;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n }\r\n\r\n &__summary {\r\n font-size: 12px;\r\n color: var(--c-text-2);\r\n line-height: 1.5;\r\n display: -webkit-box;\r\n -webkit-line-clamp: 2;\r\n line-clamp: 2;\r\n -webkit-box-orient: vertical;\r\n overflow: hidden;\r\n }\r\n\r\n &__meta {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n margin-top: 6px;\r\n }\r\n\r\n &__time,\r\n &__sender {\r\n font-size: 11px;\r\n color: var(--c-text-4);\r\n }\r\n\r\n &__sender::before {\r\n content: \"·\";\r\n margin-right: 8px;\r\n }\r\n\r\n &__actions {\r\n flex-shrink: 0;\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n opacity: 0;\r\n transition: opacity var(--c-transition, 0.2s ease);\r\n }\r\n\r\n &__action {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 24px;\r\n height: 24px;\r\n padding: 2px;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n color: var(--c-text-4);\r\n transition: all var(--c-transition, 0.2s ease);\r\n\r\n &:hover {\r\n color: var(--c-primary);\r\n background-color: var(--c-border);\r\n }\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 单条消息\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div\r\n class=\"notification-item\"\r\n :class=\"{\r\n 'notification-item--unread': message.status === 'unread',\r\n 'notification-item--urgent': message.priority === 'urgent',\r\n }\"\r\n @click=\"$emit('click', message)\"\r\n >\r\n <!-- 未读指示点 -->\r\n <div class=\"notification-item__indicator\">\r\n <span\r\n v-if=\"message.status === 'unread'\"\r\n class=\"notification-item__dot\"\r\n :class=\"`notification-item__dot--${message.priority}`\"\r\n />\r\n </div>\r\n\r\n <!-- 头像 -->\r\n <div class=\"notification-item__avatar\">\r\n <NAvatar\r\n v-if=\"message.sender?.avatar\"\r\n :src=\"message.sender.avatar\"\r\n :size=\"36\"\r\n round\r\n />\r\n <div\r\n v-else\r\n class=\"notification-item__avatar-placeholder\"\r\n :class=\"`notification-item__avatar-placeholder--${message.category}`\"\r\n >\r\n <C_Icon :name=\"categoryIcon\" :size=\"16\" />\r\n </div>\r\n </div>\r\n\r\n <!-- 内容 -->\r\n <div class=\"notification-item__body\">\r\n <div class=\"notification-item__header\">\r\n <span class=\"notification-item__title\">{{ message.title }}</span>\r\n <NTag\r\n v-if=\"message.priority === 'urgent' || message.priority === 'high'\"\r\n :type=\"priorityConfig.type\"\r\n size=\"tiny\"\r\n :bordered=\"false\"\r\n round\r\n >\r\n {{ priorityConfig.label }}\r\n </NTag>\r\n </div>\r\n <div class=\"notification-item__summary\">\r\n {{ message.summary }}\r\n </div>\r\n <div class=\"notification-item__meta\">\r\n <span class=\"notification-item__time\">{{ formattedTime }}</span>\r\n <span v-if=\"message.sender?.name\" class=\"notification-item__sender\">\r\n {{ message.sender.name }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <!-- 操作区 -->\r\n <div class=\"notification-item__actions\" @click.stop>\r\n <NTooltip\r\n v-if=\"message.status === 'unread'\"\r\n trigger=\"hover\"\r\n placement=\"top\"\r\n >\r\n <template #trigger>\r\n <span\r\n class=\"notification-item__action\"\r\n @click=\"$emit('read', message.id)\"\r\n >\r\n <C_Icon name=\"mdi:check\" :size=\"16\" />\r\n </span>\r\n </template>\r\n 标记已读\r\n </NTooltip>\r\n <NTooltip trigger=\"hover\" placement=\"top\">\r\n <template #trigger>\r\n <span\r\n class=\"notification-item__action\"\r\n @click=\"$emit('delete', message.id)\"\r\n >\r\n <C_Icon name=\"mdi:delete-outline\" :size=\"16\" />\r\n </span>\r\n </template>\r\n 删除\r\n </NTooltip>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport type { NotificationMessage } from \"../types\";\r\nimport { CATEGORY_MAP, PRIORITY_MAP } from \"../constants\";\r\nimport { useNotificationFormat } from \"../composables/useNotificationFormat\";\r\n\r\ninterface Props {\r\n /** 消息数据 */\r\n message: NotificationMessage;\r\n}\r\n\r\nconst props = defineProps<Props>();\r\n\r\ndefineEmits<{\r\n click: [message: NotificationMessage];\r\n read: [id: string];\r\n delete: [id: string];\r\n}>();\r\n\r\nconst { formatRelativeTime } = useNotificationFormat();\r\n\r\n/** 分类图标 */\r\nconst categoryIcon = computed(\r\n () => CATEGORY_MAP[props.message.category]?.icon ?? \"mdi:bell-outline\",\r\n);\r\n\r\n/** 优先级配置 */\r\nconst priorityConfig = computed(\r\n () => PRIORITY_MAP[props.message.priority] ?? PRIORITY_MAP.normal,\r\n);\r\n\r\n/** 格式化时间 */\r\nconst formattedTime = computed(() =>\r\n formatRelativeTime(props.message.timestamp),\r\n);\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.notification-item {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 10px;\r\n padding: 12px 16px;\r\n cursor: pointer;\r\n transition: background-color var(--c-transition, 0.2s ease);\r\n border-bottom: 1px solid var(--c-border);\r\n\r\n &:last-child {\r\n border-bottom: none;\r\n }\r\n\r\n &:hover {\r\n background-color: var(--c-bg-card);\r\n\r\n .notification-item__actions {\r\n opacity: 1;\r\n }\r\n }\r\n\r\n &--unread {\r\n background-color: color-mix(in srgb, var(--c-primary) 4%, transparent);\r\n\r\n &:hover {\r\n background-color: color-mix(in srgb, var(--c-primary) 8%, transparent);\r\n }\r\n }\r\n\r\n &--urgent {\r\n border-left: 3px solid #f5222d;\r\n padding-left: 13px;\r\n }\r\n\r\n /* ─── 子元素 ──────────────────────────── */\r\n\r\n &__indicator {\r\n flex-shrink: 0;\r\n width: 8px;\r\n display: flex;\r\n align-items: center;\r\n padding-top: 14px;\r\n }\r\n\r\n &__dot {\r\n width: 6px;\r\n height: 6px;\r\n border-radius: 50%;\r\n background: var(--c-primary);\r\n\r\n &--urgent {\r\n background: #f5222d;\r\n box-shadow: 0 0 4px rgba(245, 34, 45, 0.5);\r\n }\r\n\r\n &--high {\r\n background: #fa8c16;\r\n }\r\n\r\n &--normal {\r\n background: var(--c-primary);\r\n }\r\n\r\n &--low {\r\n background: var(--c-text-4);\r\n }\r\n }\r\n\r\n &__avatar {\r\n flex-shrink: 0;\r\n }\r\n\r\n &__avatar-placeholder {\r\n width: 36px;\r\n height: 36px;\r\n border-radius: 50%;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n font-size: 16px;\r\n color: #fff;\r\n\r\n &--system {\r\n background: linear-gradient(135deg, #667eea, #764ba2);\r\n }\r\n\r\n &--business {\r\n background: linear-gradient(135deg, #43e97b, #38f9d7);\r\n color: #0d1425;\r\n }\r\n\r\n &--alarm {\r\n background: linear-gradient(135deg, #f093fb, #f5576c);\r\n }\r\n }\r\n\r\n &__body {\r\n flex: 1;\r\n min-width: 0;\r\n }\r\n\r\n &__header {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n margin-bottom: 4px;\r\n }\r\n\r\n &__title {\r\n font-size: 13px;\r\n font-weight: 500;\r\n color: var(--c-text-1);\r\n line-height: 1.4;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n }\r\n\r\n &__summary {\r\n font-size: 12px;\r\n color: var(--c-text-2);\r\n line-height: 1.5;\r\n display: -webkit-box;\r\n -webkit-line-clamp: 2;\r\n line-clamp: 2;\r\n -webkit-box-orient: vertical;\r\n overflow: hidden;\r\n }\r\n\r\n &__meta {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n margin-top: 6px;\r\n }\r\n\r\n &__time,\r\n &__sender {\r\n font-size: 11px;\r\n color: var(--c-text-4);\r\n }\r\n\r\n &__sender::before {\r\n content: \"·\";\r\n margin-right: 8px;\r\n }\r\n\r\n &__actions {\r\n flex-shrink: 0;\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n opacity: 0;\r\n transition: opacity var(--c-transition, 0.2s ease);\r\n }\r\n\r\n &__action {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 24px;\r\n height: 24px;\r\n padding: 2px;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n color: var(--c-text-4);\r\n transition: all var(--c-transition, 0.2s ease);\r\n\r\n &:hover {\r\n color: var(--c-primary);\r\n background-color: var(--c-border);\r\n }\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 消息列表\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"notification-list\">\r\n <!-- 分类 Tab -->\r\n <div class=\"notification-list__tabs\">\r\n <div\r\n v-for=\"tab in CATEGORY_TABS\"\r\n :key=\"tab.key\"\r\n class=\"notification-list__tab\"\r\n :class=\"{\r\n 'notification-list__tab--active': activeCategory === tab.key,\r\n }\"\r\n @click=\"$emit('switchCategory', tab.key)\"\r\n >\r\n <span>{{ tab.label }}</span>\r\n <span\r\n v-if=\"getTabCount(tab.key) > 0\"\r\n class=\"notification-list__tab-count\"\r\n >\r\n {{ getTabCount(tab.key) }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <!-- 操作栏 -->\r\n <div v-if=\"messages.length > 0\" class=\"notification-list__toolbar\">\r\n <NButton\r\n text\r\n size=\"tiny\"\r\n :disabled=\"currentUnread === 0\"\r\n @click=\"$emit('markAllRead')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:check-all\" :size=\"14\" />\r\n </template>\r\n 全部已读\r\n </NButton>\r\n <NButton text size=\"tiny\" @click=\"$emit('clear')\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete-sweep-outline\" :size=\"14\" />\r\n </template>\r\n 清空\r\n </NButton>\r\n </div>\r\n\r\n <!-- 消息列表 -->\r\n <NScrollbar\r\n class=\"notification-list__scroll\"\r\n :style=\"{ maxHeight: `${scrollHeight}px` }\"\r\n >\r\n <template v-if=\"loading && messages.length === 0\">\r\n <!-- 骨架屏 -->\r\n <div v-for=\"i in 4\" :key=\"i\" class=\"notification-list__skeleton\">\r\n <NSkeleton circle :width=\"36\" :height=\"36\" />\r\n <div class=\"notification-list__skeleton-text\">\r\n <NSkeleton text :width=\"'60%'\" :height=\"14\" />\r\n <NSkeleton text :width=\"'90%'\" :height=\"12\" />\r\n <NSkeleton text :width=\"'40%'\" :height=\"10\" />\r\n </div>\r\n </div>\r\n </template>\r\n\r\n <template v-else-if=\"messages.length > 0\">\r\n <div class=\"notification-list__items\">\r\n <NotificationItem\r\n v-for=\"msg in messages\"\r\n :key=\"msg.id\"\r\n :message=\"msg\"\r\n @click=\"$emit('itemClick', $event)\"\r\n @read=\"$emit('read', $event)\"\r\n @delete=\"$emit('delete', $event)\"\r\n />\r\n </div>\r\n\r\n <!-- 加载更多 -->\r\n <div\r\n v-if=\"hasMore\"\r\n class=\"notification-list__load-more\"\r\n @click=\"$emit('loadMore')\"\r\n >\r\n <NSpin v-if=\"loading\" :size=\"14\" />\r\n <span>{{ loading ? \"加载中...\" : \"加载更多\" }}</span>\r\n </div>\r\n </template>\r\n\r\n <!-- 空状态 -->\r\n <div v-else class=\"notification-list__empty\">\r\n <C_Icon\r\n name=\"mdi:bell-off-outline\"\r\n :size=\"48\"\r\n class=\"notification-list__empty-icon\"\r\n />\r\n <span class=\"notification-list__empty-text\">暂无消息</span>\r\n </div>\r\n </NScrollbar>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport type { NotificationMessage, NotificationCategory } from \"../types\";\r\nimport { CATEGORY_TABS } from \"../constants\";\r\nimport NotificationItem from \"./NotificationItem.vue\";\r\n\r\ninterface Props {\r\n /** 消息列表 */\r\n messages: NotificationMessage[];\r\n /** 当前分类 */\r\n activeCategory: NotificationCategory | \"all\";\r\n /** 各分类未读数 */\r\n unreadByCategory: Record<string, number>;\r\n /** 总未读数 */\r\n unreadCount: number;\r\n /** 是否加载中 */\r\n loading: boolean;\r\n /** 是否有更多 */\r\n hasMore: boolean;\r\n /** 滚动区域高度 */\r\n scrollHeight?: number;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n scrollHeight: 420,\r\n});\r\n\r\ndefineEmits<{\r\n switchCategory: [key: NotificationCategory | \"all\"];\r\n itemClick: [message: NotificationMessage];\r\n read: [id: string];\r\n delete: [id: string];\r\n markAllRead: [];\r\n clear: [];\r\n loadMore: [];\r\n}>();\r\n\r\n/** 当前分类下的未读数 */\r\nconst currentUnread = computed(() => {\r\n if (props.activeCategory === \"all\") return props.unreadCount;\r\n return props.unreadByCategory[props.activeCategory] ?? 0;\r\n});\r\n\r\n/** 获取 Tab 的未读数 */\r\nfunction getTabCount(key: NotificationCategory | \"all\"): number {\r\n if (key === \"all\") return props.unreadCount;\r\n return props.unreadByCategory[key] ?? 0;\r\n}\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.notification-list {\r\n &__tabs {\r\n display: flex;\r\n border-bottom: 1px solid var(--c-border);\r\n padding: 0 4px;\r\n }\r\n\r\n &__tab {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 10px 12px;\r\n font-size: 13px;\r\n color: var(--c-text-2);\r\n cursor: pointer;\r\n transition: all var(--c-transition, 0.2s ease);\r\n user-select: none;\r\n\r\n &::after {\r\n content: \"\";\r\n position: absolute;\r\n bottom: 0;\r\n left: 12px;\r\n right: 12px;\r\n height: 2px;\r\n background: transparent;\r\n border-radius: 1px;\r\n transition: background var(--c-transition, 0.2s ease);\r\n }\r\n\r\n &:hover {\r\n color: var(--c-text-1);\r\n }\r\n\r\n &--active {\r\n color: var(--c-primary);\r\n font-weight: 500;\r\n\r\n &::after {\r\n background: var(--c-primary);\r\n }\r\n }\r\n }\r\n\r\n &__tab-count {\r\n min-width: 16px;\r\n height: 16px;\r\n padding: 0 4px;\r\n border-radius: 8px;\r\n background: #f5222d;\r\n color: #fff;\r\n font-size: 10px;\r\n font-weight: 600;\r\n line-height: 16px;\r\n text-align: center;\r\n }\r\n\r\n &__toolbar {\r\n display: flex;\r\n align-items: center;\r\n justify-content: flex-end;\r\n gap: 12px;\r\n padding: 6px 16px;\r\n border-bottom: 1px solid var(--c-border);\r\n }\r\n\r\n &__scroll {\r\n flex: 1;\r\n }\r\n\r\n &__skeleton {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 12px;\r\n padding: 14px 16px;\r\n }\r\n\r\n &__skeleton-text {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n }\r\n\r\n &__load-more {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 6px;\r\n padding: 12px;\r\n font-size: 12px;\r\n color: var(--c-text-2);\r\n cursor: pointer;\r\n transition: color var(--c-transition, 0.2s ease);\r\n\r\n &:hover {\r\n color: var(--c-primary);\r\n }\r\n }\r\n\r\n &__empty {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 60px 0;\r\n gap: 12px;\r\n }\r\n\r\n &__empty-icon {\r\n color: var(--c-text-4);\r\n opacity: 0.5;\r\n }\r\n\r\n &__empty-text {\r\n font-size: 13px;\r\n color: var(--c-text-4);\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 消息列表\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"notification-list\">\r\n <!-- 分类 Tab -->\r\n <div class=\"notification-list__tabs\">\r\n <div\r\n v-for=\"tab in CATEGORY_TABS\"\r\n :key=\"tab.key\"\r\n class=\"notification-list__tab\"\r\n :class=\"{\r\n 'notification-list__tab--active': activeCategory === tab.key,\r\n }\"\r\n @click=\"$emit('switchCategory', tab.key)\"\r\n >\r\n <span>{{ tab.label }}</span>\r\n <span\r\n v-if=\"getTabCount(tab.key) > 0\"\r\n class=\"notification-list__tab-count\"\r\n >\r\n {{ getTabCount(tab.key) }}\r\n </span>\r\n </div>\r\n </div>\r\n\r\n <!-- 操作栏 -->\r\n <div v-if=\"messages.length > 0\" class=\"notification-list__toolbar\">\r\n <NButton\r\n text\r\n size=\"tiny\"\r\n :disabled=\"currentUnread === 0\"\r\n @click=\"$emit('markAllRead')\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:check-all\" :size=\"14\" />\r\n </template>\r\n 全部已读\r\n </NButton>\r\n <NButton text size=\"tiny\" @click=\"$emit('clear')\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:delete-sweep-outline\" :size=\"14\" />\r\n </template>\r\n 清空\r\n </NButton>\r\n </div>\r\n\r\n <!-- 消息列表 -->\r\n <NScrollbar\r\n class=\"notification-list__scroll\"\r\n :style=\"{ maxHeight: `${scrollHeight}px` }\"\r\n >\r\n <template v-if=\"loading && messages.length === 0\">\r\n <!-- 骨架屏 -->\r\n <div v-for=\"i in 4\" :key=\"i\" class=\"notification-list__skeleton\">\r\n <NSkeleton circle :width=\"36\" :height=\"36\" />\r\n <div class=\"notification-list__skeleton-text\">\r\n <NSkeleton text :width=\"'60%'\" :height=\"14\" />\r\n <NSkeleton text :width=\"'90%'\" :height=\"12\" />\r\n <NSkeleton text :width=\"'40%'\" :height=\"10\" />\r\n </div>\r\n </div>\r\n </template>\r\n\r\n <template v-else-if=\"messages.length > 0\">\r\n <div class=\"notification-list__items\">\r\n <NotificationItem\r\n v-for=\"msg in messages\"\r\n :key=\"msg.id\"\r\n :message=\"msg\"\r\n @click=\"$emit('itemClick', $event)\"\r\n @read=\"$emit('read', $event)\"\r\n @delete=\"$emit('delete', $event)\"\r\n />\r\n </div>\r\n\r\n <!-- 加载更多 -->\r\n <div\r\n v-if=\"hasMore\"\r\n class=\"notification-list__load-more\"\r\n @click=\"$emit('loadMore')\"\r\n >\r\n <NSpin v-if=\"loading\" :size=\"14\" />\r\n <span>{{ loading ? \"加载中...\" : \"加载更多\" }}</span>\r\n </div>\r\n </template>\r\n\r\n <!-- 空状态 -->\r\n <div v-else class=\"notification-list__empty\">\r\n <C_Icon\r\n name=\"mdi:bell-off-outline\"\r\n :size=\"48\"\r\n class=\"notification-list__empty-icon\"\r\n />\r\n <span class=\"notification-list__empty-text\">暂无消息</span>\r\n </div>\r\n </NScrollbar>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport type { NotificationMessage, NotificationCategory } from \"../types\";\r\nimport { CATEGORY_TABS } from \"../constants\";\r\nimport NotificationItem from \"./NotificationItem.vue\";\r\n\r\ninterface Props {\r\n /** 消息列表 */\r\n messages: NotificationMessage[];\r\n /** 当前分类 */\r\n activeCategory: NotificationCategory | \"all\";\r\n /** 各分类未读数 */\r\n unreadByCategory: Record<string, number>;\r\n /** 总未读数 */\r\n unreadCount: number;\r\n /** 是否加载中 */\r\n loading: boolean;\r\n /** 是否有更多 */\r\n hasMore: boolean;\r\n /** 滚动区域高度 */\r\n scrollHeight?: number;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n scrollHeight: 420,\r\n});\r\n\r\ndefineEmits<{\r\n switchCategory: [key: NotificationCategory | \"all\"];\r\n itemClick: [message: NotificationMessage];\r\n read: [id: string];\r\n delete: [id: string];\r\n markAllRead: [];\r\n clear: [];\r\n loadMore: [];\r\n}>();\r\n\r\n/** 当前分类下的未读数 */\r\nconst currentUnread = computed(() => {\r\n if (props.activeCategory === \"all\") return props.unreadCount;\r\n return props.unreadByCategory[props.activeCategory] ?? 0;\r\n});\r\n\r\n/** 获取 Tab 的未读数 */\r\nfunction getTabCount(key: NotificationCategory | \"all\"): number {\r\n if (key === \"all\") return props.unreadCount;\r\n return props.unreadByCategory[key] ?? 0;\r\n}\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.notification-list {\r\n &__tabs {\r\n display: flex;\r\n border-bottom: 1px solid var(--c-border);\r\n padding: 0 4px;\r\n }\r\n\r\n &__tab {\r\n position: relative;\r\n display: flex;\r\n align-items: center;\r\n gap: 4px;\r\n padding: 10px 12px;\r\n font-size: 13px;\r\n color: var(--c-text-2);\r\n cursor: pointer;\r\n transition: all var(--c-transition, 0.2s ease);\r\n user-select: none;\r\n\r\n &::after {\r\n content: \"\";\r\n position: absolute;\r\n bottom: 0;\r\n left: 12px;\r\n right: 12px;\r\n height: 2px;\r\n background: transparent;\r\n border-radius: 1px;\r\n transition: background var(--c-transition, 0.2s ease);\r\n }\r\n\r\n &:hover {\r\n color: var(--c-text-1);\r\n }\r\n\r\n &--active {\r\n color: var(--c-primary);\r\n font-weight: 500;\r\n\r\n &::after {\r\n background: var(--c-primary);\r\n }\r\n }\r\n }\r\n\r\n &__tab-count {\r\n min-width: 16px;\r\n height: 16px;\r\n padding: 0 4px;\r\n border-radius: 8px;\r\n background: #f5222d;\r\n color: #fff;\r\n font-size: 10px;\r\n font-weight: 600;\r\n line-height: 16px;\r\n text-align: center;\r\n }\r\n\r\n &__toolbar {\r\n display: flex;\r\n align-items: center;\r\n justify-content: flex-end;\r\n gap: 12px;\r\n padding: 6px 16px;\r\n border-bottom: 1px solid var(--c-border);\r\n }\r\n\r\n &__scroll {\r\n flex: 1;\r\n }\r\n\r\n &__skeleton {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 12px;\r\n padding: 14px 16px;\r\n }\r\n\r\n &__skeleton-text {\r\n flex: 1;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n }\r\n\r\n &__load-more {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 6px;\r\n padding: 12px;\r\n font-size: 12px;\r\n color: var(--c-text-2);\r\n cursor: pointer;\r\n transition: color var(--c-transition, 0.2s ease);\r\n\r\n &:hover {\r\n color: var(--c-primary);\r\n }\r\n }\r\n\r\n &__empty {\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 60px 0;\r\n gap: 12px;\r\n }\r\n\r\n &__empty-icon {\r\n color: var(--c-text-4);\r\n opacity: 0.5;\r\n }\r\n\r\n &__empty-text {\r\n font-size: 13px;\r\n color: var(--c-text-4);\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 消息详情\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"notification-detail\">\r\n <!-- 返回栏 -->\r\n <div class=\"notification-detail__back\">\r\n <NButton text size=\"small\" @click=\"$emit('back')\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:arrow-left\" :size=\"16\" />\r\n </template>\r\n 返回列表\r\n </NButton>\r\n </div>\r\n\r\n <!-- 消息头部 -->\r\n <div class=\"notification-detail__header\">\r\n <h3 class=\"notification-detail__title\">{{ message.title }}</h3>\r\n <div class=\"notification-detail__meta\">\r\n <NTag :type=\"categoryTag.color\" size=\"tiny\" :bordered=\"false\" round>\r\n <template #icon>\r\n <C_Icon :name=\"categoryTag.icon\" :size=\"12\" />\r\n </template>\r\n {{ categoryTag.label }}\r\n </NTag>\r\n <NTag\r\n v-if=\"message.priority === 'urgent' || message.priority === 'high'\"\r\n :type=\"priorityTag.type\"\r\n size=\"tiny\"\r\n :bordered=\"false\"\r\n round\r\n >\r\n {{ priorityTag.label }}\r\n </NTag>\r\n <span class=\"notification-detail__time\">{{ formattedTime }}</span>\r\n </div>\r\n <div v-if=\"message.sender\" class=\"notification-detail__sender\">\r\n <NAvatar\r\n v-if=\"message.sender.avatar\"\r\n :src=\"message.sender.avatar\"\r\n :size=\"20\"\r\n round\r\n />\r\n <span>{{ message.sender.name }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 消息内容 -->\r\n <NScrollbar\r\n class=\"notification-detail__content\"\r\n :style=\"{ maxHeight: `${scrollHeight}px` }\"\r\n >\r\n <div\r\n v-if=\"message.content\"\r\n class=\"notification-detail__body\"\r\n v-html=\"message.content\"\r\n />\r\n <div v-else class=\"notification-detail__body\">\r\n {{ message.summary }}\r\n </div>\r\n </NScrollbar>\r\n\r\n <!-- 操作区 -->\r\n <div v-if=\"message.actionUrl\" class=\"notification-detail__footer\">\r\n <NButton type=\"primary\" size=\"small\" @click=\"handleAction\">\r\n {{ message.actionText || \"查看详情\" }}\r\n <template #icon>\r\n <C_Icon name=\"mdi:arrow-right\" :size=\"16\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport type { NotificationMessage } from \"../types\";\r\nimport { CATEGORY_MAP, PRIORITY_MAP } from \"../constants\";\r\nimport { useNotificationFormat } from \"../composables/useNotificationFormat\";\r\n\r\ninterface Props {\r\n /** 消息数据 */\r\n message: NotificationMessage;\r\n /** 内容滚动区高度 */\r\n scrollHeight?: number;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n scrollHeight: 320,\r\n});\r\n\r\nconst emit = defineEmits<{\r\n back: [];\r\n action: [url: string];\r\n navigate: [url: string];\r\n}>();\r\nconst { formatRelativeTime } = useNotificationFormat();\r\n\r\n/** 分类标签 */\r\nconst categoryTag = computed(\r\n () => CATEGORY_MAP[props.message.category] ?? CATEGORY_MAP.system,\r\n);\r\n\r\n/** 优先级标签 */\r\nconst priorityTag = computed(\r\n () => PRIORITY_MAP[props.message.priority] ?? PRIORITY_MAP.normal,\r\n);\r\n\r\n/** 格式化时间 */\r\nconst formattedTime = computed(() =>\r\n formatRelativeTime(props.message.timestamp),\r\n);\r\n\r\n/** 处理操作按钮点击 */\r\nfunction handleAction() {\r\n const url = props.message.actionUrl;\r\n if (!url) return;\r\n\r\n emit(\"action\", url);\r\n\r\n if (url.startsWith(\"http\")) {\r\n window.open(url, \"_blank\");\r\n } else {\r\n emit(\"navigate\", url);\r\n }\r\n}\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.notification-detail {\r\n display: flex;\r\n flex-direction: column;\r\n height: 100%;\r\n\r\n &__back {\r\n padding: 8px 16px;\r\n border-bottom: 1px solid var(--c-border);\r\n }\r\n\r\n &__header {\r\n padding: 16px;\r\n border-bottom: 1px solid var(--c-border);\r\n }\r\n\r\n &__title {\r\n font-size: 15px;\r\n font-weight: 600;\r\n color: var(--c-text-1);\r\n line-height: 1.5;\r\n margin: 0 0 8px;\r\n }\r\n\r\n &__meta {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n flex-wrap: wrap;\r\n }\r\n\r\n &__time {\r\n font-size: 11px;\r\n color: var(--c-text-4);\r\n }\r\n\r\n &__sender {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n margin-top: 8px;\r\n font-size: 12px;\r\n color: var(--c-text-2);\r\n }\r\n\r\n &__content {\r\n flex: 1;\r\n min-height: 0;\r\n }\r\n\r\n &__body {\r\n padding: 16px;\r\n font-size: 13px;\r\n line-height: 1.7;\r\n color: var(--c-text-1);\r\n word-break: break-word;\r\n\r\n :deep(p) {\r\n margin: 0 0 8px;\r\n }\r\n\r\n :deep(ul),\r\n :deep(ol) {\r\n padding-left: 20px;\r\n margin: 8px 0;\r\n }\r\n\r\n :deep(li) {\r\n margin: 4px 0;\r\n }\r\n\r\n :deep(strong) {\r\n font-weight: 600;\r\n color: var(--c-primary);\r\n }\r\n }\r\n\r\n &__footer {\r\n padding: 12px 16px;\r\n border-top: 1px solid var(--c-border);\r\n display: flex;\r\n justify-content: flex-end;\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 消息详情\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"notification-detail\">\r\n <!-- 返回栏 -->\r\n <div class=\"notification-detail__back\">\r\n <NButton text size=\"small\" @click=\"$emit('back')\">\r\n <template #icon>\r\n <C_Icon name=\"mdi:arrow-left\" :size=\"16\" />\r\n </template>\r\n 返回列表\r\n </NButton>\r\n </div>\r\n\r\n <!-- 消息头部 -->\r\n <div class=\"notification-detail__header\">\r\n <h3 class=\"notification-detail__title\">{{ message.title }}</h3>\r\n <div class=\"notification-detail__meta\">\r\n <NTag :type=\"categoryTag.color\" size=\"tiny\" :bordered=\"false\" round>\r\n <template #icon>\r\n <C_Icon :name=\"categoryTag.icon\" :size=\"12\" />\r\n </template>\r\n {{ categoryTag.label }}\r\n </NTag>\r\n <NTag\r\n v-if=\"message.priority === 'urgent' || message.priority === 'high'\"\r\n :type=\"priorityTag.type\"\r\n size=\"tiny\"\r\n :bordered=\"false\"\r\n round\r\n >\r\n {{ priorityTag.label }}\r\n </NTag>\r\n <span class=\"notification-detail__time\">{{ formattedTime }}</span>\r\n </div>\r\n <div v-if=\"message.sender\" class=\"notification-detail__sender\">\r\n <NAvatar\r\n v-if=\"message.sender.avatar\"\r\n :src=\"message.sender.avatar\"\r\n :size=\"20\"\r\n round\r\n />\r\n <span>{{ message.sender.name }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- 消息内容 -->\r\n <NScrollbar\r\n class=\"notification-detail__content\"\r\n :style=\"{ maxHeight: `${scrollHeight}px` }\"\r\n >\r\n <div\r\n v-if=\"message.content\"\r\n class=\"notification-detail__body\"\r\n v-html=\"message.content\"\r\n />\r\n <div v-else class=\"notification-detail__body\">\r\n {{ message.summary }}\r\n </div>\r\n </NScrollbar>\r\n\r\n <!-- 操作区 -->\r\n <div v-if=\"message.actionUrl\" class=\"notification-detail__footer\">\r\n <NButton type=\"primary\" size=\"small\" @click=\"handleAction\">\r\n {{ message.actionText || \"查看详情\" }}\r\n <template #icon>\r\n <C_Icon name=\"mdi:arrow-right\" :size=\"16\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport type { NotificationMessage } from \"../types\";\r\nimport { CATEGORY_MAP, PRIORITY_MAP } from \"../constants\";\r\nimport { useNotificationFormat } from \"../composables/useNotificationFormat\";\r\n\r\ninterface Props {\r\n /** 消息数据 */\r\n message: NotificationMessage;\r\n /** 内容滚动区高度 */\r\n scrollHeight?: number;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n scrollHeight: 320,\r\n});\r\n\r\nconst emit = defineEmits<{\r\n back: [];\r\n action: [url: string];\r\n navigate: [url: string];\r\n}>();\r\nconst { formatRelativeTime } = useNotificationFormat();\r\n\r\n/** 分类标签 */\r\nconst categoryTag = computed(\r\n () => CATEGORY_MAP[props.message.category] ?? CATEGORY_MAP.system,\r\n);\r\n\r\n/** 优先级标签 */\r\nconst priorityTag = computed(\r\n () => PRIORITY_MAP[props.message.priority] ?? PRIORITY_MAP.normal,\r\n);\r\n\r\n/** 格式化时间 */\r\nconst formattedTime = computed(() =>\r\n formatRelativeTime(props.message.timestamp),\r\n);\r\n\r\n/** 处理操作按钮点击 */\r\nfunction handleAction() {\r\n const url = props.message.actionUrl;\r\n if (!url) return;\r\n\r\n emit(\"action\", url);\r\n\r\n if (url.startsWith(\"http\")) {\r\n window.open(url, \"_blank\");\r\n } else {\r\n emit(\"navigate\", url);\r\n }\r\n}\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.notification-detail {\r\n display: flex;\r\n flex-direction: column;\r\n height: 100%;\r\n\r\n &__back {\r\n padding: 8px 16px;\r\n border-bottom: 1px solid var(--c-border);\r\n }\r\n\r\n &__header {\r\n padding: 16px;\r\n border-bottom: 1px solid var(--c-border);\r\n }\r\n\r\n &__title {\r\n font-size: 15px;\r\n font-weight: 600;\r\n color: var(--c-text-1);\r\n line-height: 1.5;\r\n margin: 0 0 8px;\r\n }\r\n\r\n &__meta {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n flex-wrap: wrap;\r\n }\r\n\r\n &__time {\r\n font-size: 11px;\r\n color: var(--c-text-4);\r\n }\r\n\r\n &__sender {\r\n display: flex;\r\n align-items: center;\r\n gap: 6px;\r\n margin-top: 8px;\r\n font-size: 12px;\r\n color: var(--c-text-2);\r\n }\r\n\r\n &__content {\r\n flex: 1;\r\n min-height: 0;\r\n }\r\n\r\n &__body {\r\n padding: 16px;\r\n font-size: 13px;\r\n line-height: 1.7;\r\n color: var(--c-text-1);\r\n word-break: break-word;\r\n\r\n :deep(p) {\r\n margin: 0 0 8px;\r\n }\r\n\r\n :deep(ul),\r\n :deep(ol) {\r\n padding-left: 20px;\r\n margin: 8px 0;\r\n }\r\n\r\n :deep(li) {\r\n margin: 4px 0;\r\n }\r\n\r\n :deep(strong) {\r\n font-weight: 600;\r\n color: var(--c-primary);\r\n }\r\n }\r\n\r\n &__footer {\r\n padding: 12px 16px;\r\n border-top: 1px solid var(--c-border);\r\n display: flex;\r\n justify-content: flex-end;\r\n }\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 通知中心组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"c-notification-center\">\r\n <NPopover\r\n v-model:show=\"core.popoverVisible.value\"\r\n :width=\"420\"\r\n trigger=\"click\"\r\n placement=\"bottom-end\"\r\n :show-arrow=\"false\"\r\n content-class=\"c-notification-center__popover\"\r\n @after-leave=\"handlePopoverClose\"\r\n >\r\n <!-- 触发器:角标铃铛 -->\r\n <template #trigger>\r\n <NotificationBadge\r\n :count=\"core.unreadCount.value\"\r\n :max-count=\"props.maxBadgeCount\"\r\n :has-urgent=\"hasUrgentMessage\"\r\n @click=\"handleBadgeClick\"\r\n />\r\n </template>\r\n\r\n <!-- 面板内容 -->\r\n <div class=\"c-notification-center__panel\">\r\n <!-- 头部标题 -->\r\n <div class=\"c-notification-center__header\">\r\n <span class=\"c-notification-center__title\">消息中心</span>\r\n <div class=\"c-notification-center__header-extra\">\r\n <!-- WebSocket 连接状态 -->\r\n <NTooltip v-if=\"props.wsConfig\" trigger=\"hover\" placement=\"top\">\r\n <template #trigger>\r\n <span\r\n class=\"c-notification-center__ws-dot\"\r\n :class=\"`c-notification-center__ws-dot--${core.wsStatus.value}`\"\r\n />\r\n </template>\r\n {{ wsStatusText }}\r\n </NTooltip>\r\n </div>\r\n </div>\r\n\r\n <!-- 列表 / 详情切换 -->\r\n <Transition name=\"slide-fade\" mode=\"out-in\">\r\n <!-- 消息详情 -->\r\n <NotificationDetail\r\n v-if=\"core.selectedMessage.value\"\r\n :key=\"core.selectedMessage.value.id\"\r\n :message=\"core.selectedMessage.value\"\r\n @back=\"core.clearSelection\"\r\n @action=\"handleAction\"\r\n @navigate=\"(url) => emit('navigate', url)\"\r\n />\r\n\r\n <!-- 消息列表 -->\r\n <NotificationList\r\n v-else\r\n :messages=\"core.filteredMessages.value\"\r\n :active-category=\"core.activeCategory.value\"\r\n :unread-by-category=\"core.unreadByCategory.value\"\r\n :unread-count=\"core.unreadCount.value\"\r\n :loading=\"core.loading.value\"\r\n :has-more=\"core.hasMore.value\"\r\n @switch-category=\"core.switchCategory\"\r\n @item-click=\"handleItemClick\"\r\n @read=\"handleRead\"\r\n @delete=\"handleDelete\"\r\n @mark-all-read=\"handleMarkAllRead\"\r\n @clear=\"handleClear\"\r\n @load-more=\"core.loadMore\"\r\n />\r\n </Transition>\r\n </div>\r\n </NPopover>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, watch } from \"vue\";\r\nimport type {\r\n NotificationCenterProps,\r\n NotificationCenterExpose,\r\n NotificationMessage,\r\n NotificationCategory,\r\n} from \"./types\";\r\nimport { useNotificationCore } from \"./composables/useNotificationCore\";\r\nimport NotificationBadge from \"./components/NotificationBadge.vue\";\r\nimport NotificationList from \"./components/NotificationList.vue\";\r\nimport NotificationDetail from \"./components/NotificationDetail.vue\";\r\n\r\ndefineOptions({ name: \"C_NotificationCenter\" });\r\n\r\nconst props = withDefaults(defineProps<NotificationCenterProps>(), {\r\n desktopNotification: false,\r\n maxBadgeCount: 99,\r\n pollingInterval: 60_000,\r\n pageSize: 20,\r\n storageKey: \"notification_center\",\r\n});\r\n\r\nconst emit = defineEmits<{\r\n itemClick: [message: NotificationMessage];\r\n read: [ids: string[]];\r\n allRead: [category?: NotificationCategory];\r\n delete: [ids: string[]];\r\n unreadChange: [count: number];\r\n wsStatusChange: [status: string];\r\n newMessage: [message: NotificationMessage];\r\n navigate: [url: string];\r\n}>();\r\n\r\nconst core = useNotificationCore(props);\r\n\r\n/* ─── 计算属性 ───────────────────────────────── */\r\n\r\n/** 是否有紧急未读消息 */\r\nconst hasUrgentMessage = computed(() =>\r\n core.messages.value.some(\r\n (m) => m.status === \"unread\" && m.priority === \"urgent\",\r\n ),\r\n);\r\n\r\n/** WebSocket 状态文本 */\r\nconst wsStatusText = computed(() => {\r\n const map: Record<string, string> = {\r\n connected: \"已连接\",\r\n connecting: \"连接中...\",\r\n disconnected: \"未连接\",\r\n reconnecting: \"重连中...\",\r\n };\r\n return map[core.wsStatus.value] ?? \"未知\";\r\n});\r\n\r\n/* ─── 监听未读数变化 ─────────────────────────── */\r\n\r\nwatch(\r\n () => core.unreadCount.value,\r\n (count) => {\r\n emit(\"unreadChange\", count);\r\n },\r\n);\r\n\r\n/* ─── 事件处理 ───────────────────────────────── */\r\n\r\n/** 角标点击 */\r\nfunction handleBadgeClick() {\r\n core.popoverVisible.value = !core.popoverVisible.value;\r\n}\r\n\r\n/** 点击消息 */\r\nfunction handleItemClick(message: NotificationMessage) {\r\n core.selectMessage(message);\r\n emit(\"itemClick\", message);\r\n}\r\n\r\n/** 标记已读 */\r\nfunction handleRead(id: string) {\r\n core.markAsRead([id]);\r\n emit(\"read\", [id]);\r\n}\r\n\r\n/** 删除消息 */\r\nfunction handleDelete(id: string) {\r\n core.deleteMessages([id]);\r\n emit(\"delete\", [id]);\r\n}\r\n\r\n/** 全部已读 */\r\nfunction handleMarkAllRead() {\r\n const category =\r\n core.activeCategory.value === \"all\" ? undefined : core.activeCategory.value;\r\n core.markAllAsRead(category);\r\n emit(\"allRead\", category);\r\n}\r\n\r\n/** 清空 */\r\nfunction handleClear() {\r\n const category =\r\n core.activeCategory.value === \"all\" ? undefined : core.activeCategory.value;\r\n core.clearMessages(category);\r\n}\r\n\r\n/** 操作链接跳转 */\r\nfunction handleAction() {\r\n core.popoverVisible.value = false;\r\n}\r\n\r\n/** Popover 关闭后重置详情 */\r\nfunction handlePopoverClose() {\r\n core.clearSelection();\r\n}\r\n\r\n/* ─── Expose ──────────────────────────────────── */\r\n\r\ndefineExpose<NotificationCenterExpose>({\r\n refresh: () => core.fetchMessages(true),\r\n connectWS: () => {\r\n if (props.wsConfig) core.connectWS(props.wsConfig);\r\n },\r\n disconnectWS: core.disconnectWS,\r\n getUnreadCount: () => core.unreadCount.value,\r\n getMessages: () => core.messages.value,\r\n markRead: (ids: string[]) => core.markAsRead(ids),\r\n markAllAsRead: (category?: NotificationCategory) =>\r\n core.markAllAsRead(category),\r\n});\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.c-notification-center {\r\n display: inline-flex;\r\n align-items: center;\r\n\r\n &__panel {\r\n display: flex;\r\n flex-direction: column;\r\n max-height: 560px;\r\n }\r\n\r\n &__header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 14px 16px 10px;\r\n border-bottom: 1px solid var(--c-border);\r\n }\r\n\r\n &__title {\r\n font-size: 15px;\r\n font-weight: 600;\r\n color: var(--c-text-1);\r\n }\r\n\r\n &__header-extra {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n }\r\n\r\n &__ws-dot {\r\n width: 8px;\r\n height: 8px;\r\n border-radius: 50%;\r\n transition: background-color var(--c-transition, 0.2s ease);\r\n\r\n &--connected {\r\n background: #52c41a;\r\n box-shadow: 0 0 4px rgba(82, 196, 26, 0.5);\r\n }\r\n\r\n &--connecting,\r\n &--reconnecting {\r\n background: #faad14;\r\n animation: status-blink 1s ease-in-out infinite;\r\n }\r\n\r\n &--disconnected {\r\n background: var(--c-text-4);\r\n }\r\n }\r\n}\r\n\r\n@keyframes status-blink {\r\n 0%,\r\n 100% {\r\n opacity: 1;\r\n }\r\n\r\n 50% {\r\n opacity: 0.3;\r\n }\r\n}\r\n\r\n/* ─── 切换过渡 ───────────────────────────────── */\r\n\r\n.slide-fade-enter-active {\r\n transition: all 0.25s ease-out;\r\n}\r\n\r\n.slide-fade-leave-active {\r\n transition: all 0.2s ease-in;\r\n}\r\n\r\n.slide-fade-enter-from {\r\n opacity: 0;\r\n transform: translateY(8px);\r\n}\r\n\r\n.slide-fade-leave-to {\r\n opacity: 0;\r\n transform: translateY(-8px);\r\n}\r\n</style>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2026-02-27\r\n * @Description: 通知中心组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\r\n-->\r\n\r\n<template>\r\n <div class=\"c-notification-center\">\r\n <NPopover\r\n v-model:show=\"core.popoverVisible.value\"\r\n :width=\"420\"\r\n trigger=\"click\"\r\n placement=\"bottom-end\"\r\n :show-arrow=\"false\"\r\n content-class=\"c-notification-center__popover\"\r\n @after-leave=\"handlePopoverClose\"\r\n >\r\n <!-- 触发器:角标铃铛 -->\r\n <template #trigger>\r\n <NotificationBadge\r\n :count=\"core.unreadCount.value\"\r\n :max-count=\"props.maxBadgeCount\"\r\n :has-urgent=\"hasUrgentMessage\"\r\n @click=\"handleBadgeClick\"\r\n />\r\n </template>\r\n\r\n <!-- 面板内容 -->\r\n <div class=\"c-notification-center__panel\">\r\n <!-- 头部标题 -->\r\n <div class=\"c-notification-center__header\">\r\n <span class=\"c-notification-center__title\">消息中心</span>\r\n <div class=\"c-notification-center__header-extra\">\r\n <!-- WebSocket 连接状态 -->\r\n <NTooltip v-if=\"props.wsConfig\" trigger=\"hover\" placement=\"top\">\r\n <template #trigger>\r\n <span\r\n class=\"c-notification-center__ws-dot\"\r\n :class=\"`c-notification-center__ws-dot--${core.wsStatus.value}`\"\r\n />\r\n </template>\r\n {{ wsStatusText }}\r\n </NTooltip>\r\n </div>\r\n </div>\r\n\r\n <!-- 列表 / 详情切换 -->\r\n <Transition name=\"slide-fade\" mode=\"out-in\">\r\n <!-- 消息详情 -->\r\n <NotificationDetail\r\n v-if=\"core.selectedMessage.value\"\r\n :key=\"core.selectedMessage.value.id\"\r\n :message=\"core.selectedMessage.value\"\r\n @back=\"core.clearSelection\"\r\n @action=\"handleAction\"\r\n @navigate=\"(url) => emit('navigate', url)\"\r\n />\r\n\r\n <!-- 消息列表 -->\r\n <NotificationList\r\n v-else\r\n :messages=\"core.filteredMessages.value\"\r\n :active-category=\"core.activeCategory.value\"\r\n :unread-by-category=\"core.unreadByCategory.value\"\r\n :unread-count=\"core.unreadCount.value\"\r\n :loading=\"core.loading.value\"\r\n :has-more=\"core.hasMore.value\"\r\n @switch-category=\"core.switchCategory\"\r\n @item-click=\"handleItemClick\"\r\n @read=\"handleRead\"\r\n @delete=\"handleDelete\"\r\n @mark-all-read=\"handleMarkAllRead\"\r\n @clear=\"handleClear\"\r\n @load-more=\"core.loadMore\"\r\n />\r\n </Transition>\r\n </div>\r\n </NPopover>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, watch } from \"vue\";\r\nimport type {\r\n NotificationCenterProps,\r\n NotificationCenterExpose,\r\n NotificationMessage,\r\n NotificationCategory,\r\n} from \"./types\";\r\nimport { useNotificationCore } from \"./composables/useNotificationCore\";\r\nimport NotificationBadge from \"./components/NotificationBadge.vue\";\r\nimport NotificationList from \"./components/NotificationList.vue\";\r\nimport NotificationDetail from \"./components/NotificationDetail.vue\";\r\n\r\ndefineOptions({ name: \"C_NotificationCenter\" });\r\n\r\nconst props = withDefaults(defineProps<NotificationCenterProps>(), {\r\n desktopNotification: false,\r\n maxBadgeCount: 99,\r\n pollingInterval: 60_000,\r\n pageSize: 20,\r\n storageKey: \"notification_center\",\r\n});\r\n\r\nconst emit = defineEmits<{\r\n itemClick: [message: NotificationMessage];\r\n read: [ids: string[]];\r\n allRead: [category?: NotificationCategory];\r\n delete: [ids: string[]];\r\n unreadChange: [count: number];\r\n wsStatusChange: [status: string];\r\n newMessage: [message: NotificationMessage];\r\n navigate: [url: string];\r\n}>();\r\n\r\nconst core = useNotificationCore(props);\r\n\r\n/* ─── 计算属性 ───────────────────────────────── */\r\n\r\n/** 是否有紧急未读消息 */\r\nconst hasUrgentMessage = computed(() =>\r\n core.messages.value.some(\r\n (m) => m.status === \"unread\" && m.priority === \"urgent\",\r\n ),\r\n);\r\n\r\n/** WebSocket 状态文本 */\r\nconst wsStatusText = computed(() => {\r\n const map: Record<string, string> = {\r\n connected: \"已连接\",\r\n connecting: \"连接中...\",\r\n disconnected: \"未连接\",\r\n reconnecting: \"重连中...\",\r\n };\r\n return map[core.wsStatus.value] ?? \"未知\";\r\n});\r\n\r\n/* ─── 监听未读数变化 ─────────────────────────── */\r\n\r\nwatch(\r\n () => core.unreadCount.value,\r\n (count) => {\r\n emit(\"unreadChange\", count);\r\n },\r\n);\r\n\r\n/* ─── 事件处理 ───────────────────────────────── */\r\n\r\n/** 角标点击 */\r\nfunction handleBadgeClick() {\r\n core.popoverVisible.value = !core.popoverVisible.value;\r\n}\r\n\r\n/** 点击消息 */\r\nfunction handleItemClick(message: NotificationMessage) {\r\n core.selectMessage(message);\r\n emit(\"itemClick\", message);\r\n}\r\n\r\n/** 标记已读 */\r\nfunction handleRead(id: string) {\r\n core.markAsRead([id]);\r\n emit(\"read\", [id]);\r\n}\r\n\r\n/** 删除消息 */\r\nfunction handleDelete(id: string) {\r\n core.deleteMessages([id]);\r\n emit(\"delete\", [id]);\r\n}\r\n\r\n/** 全部已读 */\r\nfunction handleMarkAllRead() {\r\n const category =\r\n core.activeCategory.value === \"all\" ? undefined : core.activeCategory.value;\r\n core.markAllAsRead(category);\r\n emit(\"allRead\", category);\r\n}\r\n\r\n/** 清空 */\r\nfunction handleClear() {\r\n const category =\r\n core.activeCategory.value === \"all\" ? undefined : core.activeCategory.value;\r\n core.clearMessages(category);\r\n}\r\n\r\n/** 操作链接跳转 */\r\nfunction handleAction() {\r\n core.popoverVisible.value = false;\r\n}\r\n\r\n/** Popover 关闭后重置详情 */\r\nfunction handlePopoverClose() {\r\n core.clearSelection();\r\n}\r\n\r\n/* ─── Expose ──────────────────────────────────── */\r\n\r\ndefineExpose<NotificationCenterExpose>({\r\n refresh: () => core.fetchMessages(true),\r\n connectWS: () => {\r\n if (props.wsConfig) core.connectWS(props.wsConfig);\r\n },\r\n disconnectWS: core.disconnectWS,\r\n getUnreadCount: () => core.unreadCount.value,\r\n getMessages: () => core.messages.value,\r\n markRead: (ids: string[]) => core.markAsRead(ids),\r\n markAllAsRead: (category?: NotificationCategory) =>\r\n core.markAllAsRead(category),\r\n});\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.c-notification-center {\r\n display: inline-flex;\r\n align-items: center;\r\n\r\n &__panel {\r\n display: flex;\r\n flex-direction: column;\r\n max-height: 560px;\r\n }\r\n\r\n &__header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n padding: 14px 16px 10px;\r\n border-bottom: 1px solid var(--c-border);\r\n }\r\n\r\n &__title {\r\n font-size: 15px;\r\n font-weight: 600;\r\n color: var(--c-text-1);\r\n }\r\n\r\n &__header-extra {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n }\r\n\r\n &__ws-dot {\r\n width: 8px;\r\n height: 8px;\r\n border-radius: 50%;\r\n transition: background-color var(--c-transition, 0.2s ease);\r\n\r\n &--connected {\r\n background: #52c41a;\r\n box-shadow: 0 0 4px rgba(82, 196, 26, 0.5);\r\n }\r\n\r\n &--connecting,\r\n &--reconnecting {\r\n background: #faad14;\r\n animation: status-blink 1s ease-in-out infinite;\r\n }\r\n\r\n &--disconnected {\r\n background: var(--c-text-4);\r\n }\r\n }\r\n}\r\n\r\n@keyframes status-blink {\r\n 0%,\r\n 100% {\r\n opacity: 1;\r\n }\r\n\r\n 50% {\r\n opacity: 0.3;\r\n }\r\n}\r\n\r\n/* ─── 切换过渡 ───────────────────────────────── */\r\n\r\n.slide-fade-enter-active {\r\n transition: all 0.25s ease-out;\r\n}\r\n\r\n.slide-fade-leave-active {\r\n transition: all 0.2s ease-in;\r\n}\r\n\r\n.slide-fade-enter-from {\r\n opacity: 0;\r\n transform: translateY(8px);\r\n}\r\n\r\n.slide-fade-leave-to {\r\n opacity: 0;\r\n transform: translateY(-8px);\r\n}\r\n</style>\r\n"],"mappings":"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"}