@robot-admin/naive-ui-components 0.10.2 → 0.11.0
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.
- package/CHANGELOG.md +111 -94
- package/LICENSE +21 -21
- package/README.md +463 -315
- package/README_EN.md +404 -0
- package/SECURITY.md +23 -0
- package/dist/C_ActionBar.cjs +1 -1
- package/dist/C_ActionBar.css +46 -48
- package/dist/C_ActionBar.d.cts +1 -1
- package/dist/C_ActionBar.js +1 -1
- package/dist/C_ActionBar2.cjs +1 -0
- package/dist/C_ActionBar2.js +1 -2
- package/dist/C_AntV.cjs +1 -1
- package/dist/C_AntV.css +3 -3
- package/dist/C_AntV.d.cts +1 -1
- package/dist/C_AntV.js +1 -1
- package/dist/C_AntV2.cjs +4 -0
- package/dist/C_AntV2.js +3 -4
- package/dist/C_AudioPlayer.cjs +1 -1
- package/dist/C_AudioPlayer.css +15 -7
- package/dist/C_AudioPlayer.d.cts +1 -1
- package/dist/C_AudioPlayer.js +1 -1
- package/dist/C_AudioPlayer2.cjs +1 -0
- package/dist/C_AudioPlayer2.js +1 -2
- package/dist/C_AvatarGroup.cjs +1 -1
- package/dist/C_AvatarGroup.css +15 -7
- package/dist/C_AvatarGroup.d.cts +1 -1
- package/dist/C_AvatarGroup2.cjs +1 -0
- package/dist/C_AvatarGroup2.js +1 -2
- package/dist/C_Barcode.cjs +1 -1
- package/dist/C_Barcode.css +0 -2
- package/dist/C_Barcode.d.cts +1 -1
- package/dist/C_Barcode2.cjs +1 -0
- package/dist/C_Barcode2.js +1 -2
- package/dist/C_Breadcrumb.cjs +1 -1
- package/dist/C_Breadcrumb.css +0 -2
- package/dist/C_Breadcrumb.d.cts +2 -2
- package/dist/C_Breadcrumb.d.ts +1 -1
- package/dist/C_Breadcrumb.js +1 -1
- package/dist/C_Breadcrumb2.cjs +1 -0
- package/dist/C_Breadcrumb2.js +1 -2
- package/dist/C_Captcha.cjs +1 -1
- package/dist/C_Captcha.css +46 -21
- package/dist/C_Captcha.d.cts +3 -2
- package/dist/C_Captcha.d.ts +3 -2
- package/dist/C_Captcha.js +1 -1
- package/dist/C_Captcha2.cjs +1 -0
- package/dist/C_Captcha2.js +1 -2
- package/dist/C_Cascade.cjs +1 -1
- package/dist/C_Cascade.css +0 -2
- package/dist/C_Cascade.d.cts +1 -1
- package/dist/C_Cascade.d.ts +1 -1
- package/dist/C_Cascade2.cjs +1 -0
- package/dist/C_Cascade2.js +1 -2
- package/dist/C_Chat.cjs +1 -1
- package/dist/C_Chat.css +0 -2
- package/dist/C_Chat.d.cts +1 -1
- package/dist/C_Chat.d.ts +1 -1
- package/dist/C_Chat.js +1 -1
- package/dist/C_Chat2.cjs +1 -0
- package/dist/C_Chat2.js +1 -2
- package/dist/C_City.cjs +1 -1
- package/dist/C_City.css +28 -30
- package/dist/C_City.d.cts +1 -1
- package/dist/C_City.d.ts +1 -1
- package/dist/C_City2.cjs +1 -0
- package/dist/C_City2.js +1 -2
- package/dist/C_Code.cjs +1 -1
- package/dist/C_Code.css +241 -4
- package/dist/C_Code.d.cts +1 -1
- package/dist/C_Code.d.ts +1 -1
- package/dist/C_Code.js +1 -1
- package/dist/C_Code2.cjs +1 -0
- package/dist/C_Code2.js +1 -2
- package/dist/C_CollapsePanel.cjs +1 -1
- package/dist/C_CollapsePanel.css +12 -6
- package/dist/C_CollapsePanel.d.cts +1 -1
- package/dist/C_CollapsePanel.js +1 -1
- package/dist/C_CollapsePanel2.cjs +1 -0
- package/dist/C_CollapsePanel2.js +1 -2
- package/dist/C_ContextMenu.cjs +1 -1
- package/dist/C_ContextMenu.css +16 -18
- package/dist/C_ContextMenu.d.cts +1 -1
- package/dist/C_ContextMenu.d.ts +1 -1
- package/dist/C_ContextMenu.js +1 -1
- package/dist/C_ContextMenu2.cjs +1 -0
- package/dist/C_ContextMenu2.js +1 -2
- package/dist/C_Cron.cjs +1 -1
- package/dist/C_Cron.css +18 -8
- package/dist/C_Cron.d.cts +1 -1
- package/dist/C_Cron.js +1 -1
- package/dist/C_Cron2.cjs +1 -0
- package/dist/C_Cron2.js +1 -2
- package/dist/C_Date.cjs +1 -1
- package/dist/C_Date.css +1 -0
- package/dist/C_Date.d.cts +2 -2
- package/dist/C_Date.d.ts +2 -2
- package/dist/C_Date2.cjs +1 -0
- package/dist/C_Date2.js +1 -2
- package/dist/C_Draggable.cjs +1 -1
- package/dist/C_Draggable.css +9 -5
- package/dist/C_Draggable.d.cts +1 -1
- package/dist/C_Draggable2.cjs +1 -0
- package/dist/C_Draggable2.js +1 -2
- package/dist/C_Editor.cjs +1 -1
- package/dist/C_Editor.css +80 -67
- package/dist/C_Editor.d.cts +1 -1
- package/dist/C_Editor.d.ts +1 -1
- package/dist/C_Editor2.cjs +1 -0
- package/dist/C_Editor2.js +1 -2
- package/dist/C_FilePreview.cjs +1 -1
- package/dist/C_FilePreview.css +0 -2
- package/dist/C_FilePreview.d.cts +1 -1
- package/dist/C_FilePreview.js +1 -1
- package/dist/C_FilePreview2.cjs +1 -0
- package/dist/C_FilePreview2.js +1 -2
- package/dist/C_Form.base.css +980 -0
- package/dist/C_Form.cjs +1 -1
- package/dist/C_Form.css +256 -155
- package/dist/C_Form.d.cts +2 -2
- package/dist/C_Form.d.ts +2 -2
- package/dist/C_Form.js +1 -1
- package/dist/C_Form2.cjs +1 -0
- package/dist/C_Form2.js +1 -2
- package/dist/C_FormSearch.cjs +1 -1
- package/dist/C_FormSearch.css +47 -47
- package/dist/C_FormSearch.d.cts +2 -2
- package/dist/C_FormSearch.d.ts +2 -2
- package/dist/C_FormSearch2.cjs +1 -0
- package/dist/C_FormSearch2.js +1 -2
- package/dist/C_FormulaEditor.cjs +1 -1
- package/dist/C_FormulaEditor.css +64 -38
- package/dist/C_FormulaEditor.d.cts +1 -1
- package/dist/C_FormulaEditor.js +1 -1
- package/dist/C_FormulaEditor2.cjs +2 -0
- package/dist/C_FormulaEditor2.js +2 -2
- package/dist/C_FullCalendar.cjs +1 -1
- package/dist/C_FullCalendar.css +0 -2
- package/dist/C_FullCalendar.d.cts +1 -1
- package/dist/C_FullCalendar2.cjs +1 -0
- package/dist/C_FullCalendar2.js +1 -2
- package/dist/C_GlobalSearch.cjs +1 -1
- package/dist/C_GlobalSearch.css +111 -113
- package/dist/C_GlobalSearch.d.cts +1 -1
- package/dist/C_GlobalSearch2.cjs +1 -0
- package/dist/C_GlobalSearch2.js +1 -2
- package/dist/C_Guide.cjs +1 -1
- package/dist/C_Guide.css +1 -0
- package/dist/C_Guide.d.cts +1 -1
- package/dist/C_Guide.d.ts +1 -1
- package/dist/C_Guide.js +1 -1
- package/dist/C_Guide2.cjs +1 -0
- package/dist/C_Guide2.js +1 -2
- package/dist/C_Icon.cjs +1 -1
- package/dist/C_Icon.css +58 -0
- package/dist/C_Icon.d.cts +1 -1
- package/dist/C_Icon.d.ts +1 -1
- package/dist/C_Icon2.cjs +1 -0
- package/dist/C_Icon2.js +1 -2
- package/dist/C_ImageCropper.cjs +1 -1
- package/dist/C_ImageCropper.css +0 -2
- package/dist/C_ImageCropper.d.cts +1 -1
- package/dist/C_ImageCropper.js +1 -1
- package/dist/C_ImageCropper2.cjs +1 -0
- package/dist/C_ImageCropper2.js +1 -2
- package/dist/C_Language.cjs +1 -1
- package/dist/C_Language.css +1 -0
- package/dist/C_Language.d.cts +1 -1
- package/dist/C_Language.d.ts +1 -1
- package/dist/C_Language.js +1 -1
- package/dist/C_Language2.cjs +1 -0
- package/dist/C_Language2.js +1 -2
- package/dist/C_Login.cjs +1 -1
- package/dist/C_Login.css +92 -76
- package/dist/C_Login.d.cts +1 -1
- package/dist/C_Login.d.ts +1 -1
- package/dist/C_Login.js +1 -1
- package/dist/C_Login2.cjs +1 -0
- package/dist/C_Login2.js +1 -2
- package/dist/C_Map.cjs +1 -1
- package/dist/C_Map.css +665 -5
- package/dist/C_Map.d.cts +1 -1
- package/dist/C_Map2.cjs +1 -0
- package/dist/C_Map2.js +1 -2
- package/dist/C_Markdown.cjs +1 -1
- package/dist/C_Markdown.css +940 -35
- package/dist/C_Markdown.d.cts +1 -1
- package/dist/C_Markdown.d.ts +1 -1
- package/dist/C_Markdown2.cjs +1 -0
- package/dist/C_Markdown2.js +1 -2
- package/dist/C_Menu.cjs +1 -1
- package/dist/C_Menu.css +1 -0
- package/dist/C_Menu.d.cts +3 -3
- package/dist/C_Menu.d.ts +3 -3
- package/dist/C_Menu2.cjs +1 -0
- package/dist/C_Menu2.js +1 -2
- package/dist/C_NotificationCenter.cjs +1 -1
- package/dist/C_NotificationCenter.css +95 -47
- package/dist/C_NotificationCenter.d.cts +1 -1
- package/dist/C_NotificationCenter.js +1 -1
- package/dist/C_NotificationCenter2.cjs +1 -0
- package/dist/C_NotificationCenter2.js +1 -2
- package/dist/C_OrgChart.cjs +1 -1
- package/dist/C_OrgChart.css +6 -4
- package/dist/C_OrgChart.d.cts +1 -1
- package/dist/C_OrgChart.d.ts +1 -1
- package/dist/C_OrgChart2.cjs +1 -0
- package/dist/C_OrgChart2.js +1 -2
- package/dist/C_Progress.cjs +1 -1
- package/dist/C_Progress.css +1 -0
- package/dist/C_Progress.d.cts +1 -1
- package/dist/C_Progress.d.ts +1 -1
- package/dist/C_Progress2.cjs +1 -0
- package/dist/C_Progress2.js +1 -2
- package/dist/C_QRCode.cjs +1 -1
- package/dist/C_QRCode.css +8 -10
- package/dist/C_QRCode.d.cts +1 -1
- package/dist/C_QRCode2.cjs +1 -0
- package/dist/C_QRCode2.js +1 -2
- package/dist/C_Signature.cjs +1 -1
- package/dist/C_Signature.css +0 -2
- package/dist/C_Signature.d.cts +1 -1
- package/dist/C_Signature.js +1 -1
- package/dist/C_Signature2.cjs +1 -0
- package/dist/C_Signature2.js +1 -2
- package/dist/C_Skeleton.cjs +1 -1
- package/dist/C_Skeleton.css +0 -2
- package/dist/C_Skeleton.d.cts +1 -1
- package/dist/C_Skeleton.d.ts +1 -1
- package/dist/C_Skeleton2.cjs +1 -0
- package/dist/C_Skeleton2.js +1 -2
- package/dist/C_SplitPane.cjs +1 -1
- package/dist/C_SplitPane.css +27 -11
- package/dist/C_SplitPane.d.cts +1 -1
- package/dist/C_SplitPane.js +1 -1
- package/dist/C_SplitPane2.cjs +1 -0
- package/dist/C_SplitPane2.js +1 -2
- package/dist/C_Steps.cjs +1 -1
- package/dist/C_Steps.css +37 -39
- package/dist/C_Steps.d.cts +1 -1
- package/dist/C_Steps.d.ts +1 -1
- package/dist/C_Steps2.cjs +1 -0
- package/dist/C_Steps2.js +1 -2
- package/dist/C_Table.base.css +1588 -0
- package/dist/C_Table.cjs +1 -1
- package/dist/C_Table.css +1202 -112
- package/dist/C_Table.d.cts +2 -2
- package/dist/C_Table.d.ts +11 -2
- package/dist/C_Table.js +1 -1
- package/dist/C_TagsView.cjs +1 -1
- package/dist/C_TagsView.css +44 -40
- package/dist/C_TagsView.d.cts +2 -2
- package/dist/C_TagsView.d.ts +2 -2
- package/dist/C_TagsView.js +1 -1
- package/dist/C_TagsView2.cjs +1 -0
- package/dist/C_TagsView2.js +1 -2
- package/dist/C_Theme.cjs +1 -1
- package/dist/C_Theme.css +1 -0
- package/dist/C_Theme.d.cts +1 -1
- package/dist/C_Theme.d.ts +1 -1
- package/dist/C_Theme.js +1 -1
- package/dist/C_Theme2.cjs +1 -0
- package/dist/C_Theme2.js +1 -2
- package/dist/C_Time.cjs +1 -1
- package/dist/C_Time.css +0 -2
- package/dist/C_Time.d.cts +2 -2
- package/dist/C_Time.d.ts +2 -2
- package/dist/C_Time2.cjs +1 -0
- package/dist/C_Time2.js +1 -2
- package/dist/C_Timeline.cjs +1 -1
- package/dist/C_Timeline.css +44 -46
- package/dist/C_Timeline.d.cts +1 -1
- package/dist/C_Timeline.js +1 -1
- package/dist/C_Timeline2.cjs +1 -0
- package/dist/C_Timeline2.js +1 -2
- package/dist/C_Transfer.cjs +1 -1
- package/dist/C_Transfer.css +21 -9
- package/dist/C_Transfer.d.cts +1 -1
- package/dist/C_Transfer.js +1 -1
- package/dist/C_Transfer2.cjs +1 -0
- package/dist/C_Transfer2.js +1 -2
- package/dist/C_Tree.cjs +1 -1
- package/dist/C_Tree.css +0 -2
- package/dist/C_Tree.d.cts +1 -1
- package/dist/C_Tree.js +1 -1
- package/dist/C_Tree2.cjs +1 -0
- package/dist/C_Tree2.js +1 -2
- package/dist/C_Upload.cjs +1 -1
- package/dist/C_Upload.css +17 -17
- package/dist/C_Upload.d.cts +1 -1
- package/dist/C_Upload.js +1 -1
- package/dist/C_Upload2.cjs +1 -0
- package/dist/C_Upload2.js +1 -25
- package/dist/C_VideoPlayer.cjs +1 -1
- package/dist/C_VideoPlayer.css +35 -24
- package/dist/C_VideoPlayer.d.cts +2 -2
- package/dist/C_VideoPlayer.d.ts +2 -2
- package/dist/C_VideoPlayer.js +1 -1
- package/dist/C_VideoPlayer2.cjs +3 -0
- package/dist/C_VideoPlayer2.js +3 -4
- package/dist/C_VtableGantt.cjs +1 -1
- package/dist/C_VtableGantt.css +15 -7
- package/dist/C_VtableGantt.d.cts +1 -1
- package/dist/C_VtableGantt.d.ts +1 -1
- package/dist/C_VtableGantt.js +1 -1
- package/dist/C_VtableGantt2.cjs +1 -0
- package/dist/C_VtableGantt2.js +1 -2
- package/dist/C_WaterFall.cjs +1 -1
- package/dist/C_WaterFall.css +15 -17
- package/dist/C_WaterFall.d.cts +1 -1
- package/dist/C_WaterFall2.cjs +1 -0
- package/dist/C_WaterFall2.js +1 -2
- package/dist/C_WorkFlow.cjs +1 -1
- package/dist/C_WorkFlow.css +257 -197
- package/dist/C_WorkFlow.d.cts +1 -1
- package/dist/C_WorkFlow.js +1 -1
- package/dist/C_WorkFlow2.cjs +1 -0
- package/dist/C_WorkFlow2.js +1 -2
- package/dist/city.cjs +1 -0
- package/dist/city.js +1 -2
- package/dist/config.cjs +1 -0
- package/dist/config.d.cts +33 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.js +1 -0
- package/dist/constants.d.cts +272 -0
- package/dist/constants.d.ts +2 -3
- package/dist/constants2.d.cts +177 -0
- package/dist/constants2.d.ts +3 -4
- package/dist/constants3.d.cts +479 -0
- package/dist/constants3.d.ts +16 -12
- package/dist/constants4.d.cts +420 -0
- package/dist/constants4.d.ts +27 -37
- package/dist/constants5.d.cts +491 -0
- package/dist/constants5.d.ts +20 -4
- package/dist/data.cjs +1 -0
- package/dist/data.d.cts +67 -0
- package/dist/data.d.ts +12 -12
- package/dist/data.js +1 -0
- package/dist/export-helper.cjs +1 -0
- package/dist/helpers.cjs +1 -0
- package/dist/helpers.js +1 -0
- package/dist/html.cjs +1 -0
- package/dist/html.js +1 -0
- package/dist/images/marker-icon-2x.png +0 -0
- package/dist/images/marker-icon.png +0 -0
- package/dist/images/marker-shadow.png +0 -0
- package/dist/index.cjs +1 -2
- package/dist/index.d.cts +60 -99
- package/dist/index.d.ts +75 -164
- package/dist/index.js +1 -2
- package/dist/index.vue.d.cts +79 -0
- package/dist/index.vue.d.ts +3 -4
- package/dist/index10.vue.d.cts +47 -0
- package/dist/index10.vue.d.ts +80 -40
- package/dist/index11.vue.d.cts +88 -0
- package/dist/index11.vue.d.ts +68 -78
- package/dist/index12.vue.d.cts +79 -0
- package/dist/index12.vue.d.ts +49 -45
- package/dist/index13.vue.d.cts +64 -0
- package/dist/index13.vue.d.ts +99 -34
- package/dist/index14.vue.d.cts +106 -0
- package/dist/index14.vue.d.ts +82 -99
- package/dist/index15.vue.d.cts +91 -0
- package/dist/index15.vue.d.ts +25 -83
- package/dist/index16.vue.d.cts +34 -0
- package/dist/index16.vue.d.ts +113 -24
- package/dist/index17.vue.d.cts +124 -0
- package/dist/index17.vue.d.ts +76 -110
- package/dist/index18.vue.d.cts +83 -0
- package/dist/index18.vue.d.ts +67 -73
- package/dist/index19.vue.d.cts +101 -0
- package/dist/index19.vue.d.ts +93 -59
- package/dist/index2.vue.d.cts +82 -0
- package/dist/index2.vue.d.ts +7 -8
- package/dist/index20.vue.d.cts +54 -0
- package/dist/index20.vue.d.ts +40 -88
- package/dist/index21.vue.d.cts +103 -0
- package/dist/index21.vue.d.ts +89 -41
- package/dist/index22.vue.d.cts +45 -0
- package/dist/index22.vue.d.ts +37 -96
- package/dist/index23.vue.d.cts +19 -0
- package/dist/index23.vue.d.ts +8 -35
- package/dist/{index28.vue.d.ts → index24.vue.d.cts} +4 -5
- package/dist/index24.vue.d.ts +650 -86
- package/dist/index25.vue.d.cts +96 -0
- package/dist/index25.vue.d.ts +86 -10
- package/dist/index26.vue.d.cts +111 -0
- package/dist/index26.vue.d.ts +4 -5
- package/dist/index27.vue.d.cts +80 -0
- package/dist/index27.vue.d.ts +2 -3
- package/dist/index3.vue.d.cts +87 -0
- package/dist/index3.vue.d.ts +4 -5
- package/dist/index4.vue.d.cts +37 -0
- package/dist/index4.vue.d.ts +6 -7
- package/dist/index5.vue.d.cts +133 -0
- package/dist/index5.vue.d.ts +2 -3
- package/dist/index6.vue.d.cts +29 -0
- package/dist/index6.vue.d.ts +25 -38
- package/dist/index7.vue.d.cts +32 -0
- package/dist/index7.vue.d.ts +117 -23
- package/dist/index8.vue.d.cts +127 -0
- package/dist/index8.vue.d.ts +17 -111
- package/dist/index9.vue.d.cts +34 -0
- package/dist/index9.vue.d.ts +38 -26
- package/dist/public.d.ts +166 -0
- package/dist/resolver.cjs +1 -2
- package/dist/resolver.d.cts +6 -4
- package/dist/resolver.d.ts +6 -4
- package/dist/resolver.js +1 -2
- package/dist/storage.cjs +1 -0
- package/dist/storage.js +1 -2
- package/dist/style.css +9864 -7702
- package/dist/types.d.cts +107 -0
- package/dist/types.d.ts +52 -102
- package/dist/types2.d.cts +58 -0
- package/dist/useCalendarEvents.d.cts +150 -0
- package/dist/useCalendarEvents.d.ts +3 -4
- package/dist/useCollapsePanel.d.cts +133 -0
- package/dist/useCollapsePanel.d.ts +2 -3
- package/dist/useCropperCore.d.cts +104 -0
- package/dist/useCropperCore.d.ts +11 -9
- package/dist/useDraggableLayout.d.cts +196 -0
- package/dist/useDraggableLayout.d.ts +17 -15
- package/dist/useDynamicFormState.d.cts +749 -0
- package/dist/useDynamicFormState.d.ts +234 -112
- package/dist/useEdgeInteraction.d.cts +324 -0
- package/dist/useEdgeInteraction.d.ts +6 -7
- package/dist/useFullscreen.d.cts +165 -0
- package/dist/useFullscreen.d.ts +1 -2
- package/dist/useGlobalSearch.d.cts +110 -0
- package/dist/useGlobalSearch.d.ts +17 -9
- package/dist/useInfiniteScroll.d.cts +177 -0
- package/dist/useInfiniteScroll.d.ts +13 -5
- package/dist/useQRCode.d.cts +89 -0
- package/dist/useQRCode.d.ts +5 -3
- package/dist/useSearchState.d.cts +871 -0
- package/dist/useSearchState.d.ts +830 -30
- package/dist/useSignatureHistory.d.cts +197 -0
- package/dist/useSignatureHistory.d.ts +10 -5
- package/dist/useSplitResize.d.cts +157 -0
- package/dist/useSplitResize.d.ts +3 -4
- package/dist/useTableQuery.cjs +3 -0
- package/dist/{useModalEdit.d.ts → useTableQuery.d.cts} +624 -211
- package/dist/useTableQuery.d.ts +1721 -0
- package/dist/useTableQuery.js +3 -0
- package/dist/useTimeSelection.d.cts +116 -0
- package/dist/useTimeSelection.d.ts +20 -9
- package/dist/useTreeOperations.d.cts +185 -0
- package/dist/useTreeOperations.d.ts +13 -11
- package/dist/useWorkflowValidation.d.cts +1051 -0
- package/dist/useWorkflowValidation.d.ts +62 -63
- package/dist/verification.d.cts +58 -0
- package/dist/verification.d.ts +58 -0
- package/package.json +102 -69
- package/dist/C_ActionBar-BAcfOfRw.css.map +0 -1
- package/dist/C_ActionBar-DG2HW7t5.css.map +0 -1
- package/dist/C_ActionBar.js.map +0 -1
- package/dist/C_ActionBar2.js.map +0 -1
- package/dist/C_AntV-D_jTK8oZ.css.map +0 -1
- package/dist/C_AntV-DanTAq22.css.map +0 -1
- package/dist/C_AntV.js.map +0 -1
- package/dist/C_AntV2.js.map +0 -1
- package/dist/C_AudioPlayer-BnYIJXPD.css.map +0 -1
- package/dist/C_AudioPlayer-Cj4lUdZo.css.map +0 -1
- package/dist/C_AudioPlayer.js.map +0 -1
- package/dist/C_AudioPlayer2.js.map +0 -1
- package/dist/C_AvatarGroup-B9d-GJ9c.css.map +0 -1
- package/dist/C_AvatarGroup-D3PwsKME.css.map +0 -1
- package/dist/C_AvatarGroup.js.map +0 -1
- package/dist/C_AvatarGroup2.js.map +0 -1
- package/dist/C_Barcode-CAU2mFcx.css.map +0 -1
- package/dist/C_Barcode-D2oJaEg3.css.map +0 -1
- package/dist/C_Barcode.js.map +0 -1
- package/dist/C_Barcode2.js.map +0 -1
- package/dist/C_Breadcrumb-BiH0d9AI.css.map +0 -1
- package/dist/C_Breadcrumb-DOcLt9uX.css.map +0 -1
- package/dist/C_Breadcrumb.js.map +0 -1
- package/dist/C_Breadcrumb2.js.map +0 -1
- package/dist/C_Captcha-B3yrtG7t.css.map +0 -1
- package/dist/C_Captcha-D4hDdWDu.css.map +0 -1
- package/dist/C_Captcha.js.map +0 -1
- package/dist/C_Captcha2.js.map +0 -1
- package/dist/C_Cascade-CmCrLVft.css.map +0 -1
- package/dist/C_Cascade-U6A5AJJb.css.map +0 -1
- package/dist/C_Cascade.js.map +0 -1
- package/dist/C_Cascade2.js.map +0 -1
- package/dist/C_Chat-DeUv6uuI.css.map +0 -1
- package/dist/C_Chat-DwatUQqu.css.map +0 -1
- package/dist/C_Chat.js.map +0 -1
- package/dist/C_Chat2.js.map +0 -1
- package/dist/C_City-DdkX6u64.css.map +0 -1
- package/dist/C_City-iP-dIvui.css.map +0 -1
- package/dist/C_City.js.map +0 -1
- package/dist/C_City2.js.map +0 -1
- package/dist/C_Code-BWmJMNS7.css.map +0 -1
- package/dist/C_Code-k14vjgVl.css.map +0 -1
- package/dist/C_Code.js.map +0 -1
- package/dist/C_Code2.js.map +0 -1
- package/dist/C_CollapsePanel-BSKcWSmY.css.map +0 -1
- package/dist/C_CollapsePanel-DRvRq8tB.css.map +0 -1
- package/dist/C_CollapsePanel.js.map +0 -1
- package/dist/C_CollapsePanel2.js.map +0 -1
- package/dist/C_ContextMenu-C9iuxG2n.css.map +0 -1
- package/dist/C_ContextMenu-CD0iaSju.css.map +0 -1
- package/dist/C_ContextMenu.js.map +0 -1
- package/dist/C_ContextMenu2.js.map +0 -1
- package/dist/C_Cron-BIQ2By9v.css.map +0 -1
- package/dist/C_Cron-BNSAGD6A.css.map +0 -1
- package/dist/C_Cron.js.map +0 -1
- package/dist/C_Cron2.js.map +0 -1
- package/dist/C_Date.js.map +0 -1
- package/dist/C_Date2.js.map +0 -1
- package/dist/C_Draggable-BDAbk4s5.css.map +0 -1
- package/dist/C_Draggable-UhfDLXop.css.map +0 -1
- package/dist/C_Draggable.js.map +0 -1
- package/dist/C_Draggable2.js.map +0 -1
- package/dist/C_Editor-CZOaChru.css.map +0 -1
- package/dist/C_Editor-kJAQq5z6.css.map +0 -1
- package/dist/C_Editor.js.map +0 -1
- package/dist/C_Editor2.js.map +0 -1
- package/dist/C_FilePreview-Bs_omgJS.css.map +0 -1
- package/dist/C_FilePreview-DbwoyF2j.css.map +0 -1
- package/dist/C_FilePreview.js.map +0 -1
- package/dist/C_FilePreview2.js.map +0 -1
- package/dist/C_Form-B_2ql62V.css.map +0 -1
- package/dist/C_Form-CX-7xsz3.css.map +0 -1
- package/dist/C_Form.js.map +0 -1
- package/dist/C_Form2.js.map +0 -1
- package/dist/C_FormSearch-C3F8peAY.css.map +0 -1
- package/dist/C_FormSearch-qW56bJPo.css.map +0 -1
- package/dist/C_FormSearch.js.map +0 -1
- package/dist/C_FormSearch2.js.map +0 -1
- package/dist/C_FormulaEditor-ChOvDbsZ.css.map +0 -1
- package/dist/C_FormulaEditor-mfiIUDY4.css.map +0 -1
- package/dist/C_FormulaEditor.js.map +0 -1
- package/dist/C_FormulaEditor2.js.map +0 -1
- package/dist/C_FullCalendar-C2nsqrgS.css.map +0 -1
- package/dist/C_FullCalendar-DdFtXVhy.css.map +0 -1
- package/dist/C_FullCalendar.js.map +0 -1
- package/dist/C_FullCalendar2.js.map +0 -1
- package/dist/C_GlobalSearch-Dq4Wo01K.css.map +0 -1
- package/dist/C_GlobalSearch-RB7J43gu.css.map +0 -1
- package/dist/C_GlobalSearch.js.map +0 -1
- package/dist/C_GlobalSearch2.js.map +0 -1
- package/dist/C_Guide.js.map +0 -1
- package/dist/C_Guide2.js.map +0 -1
- package/dist/C_Icon.js.map +0 -1
- package/dist/C_Icon2.js.map +0 -1
- package/dist/C_ImageCropper-CdTX2nxU.css.map +0 -1
- package/dist/C_ImageCropper-ilOqPklJ.css.map +0 -1
- package/dist/C_ImageCropper.js.map +0 -1
- package/dist/C_ImageCropper2.js.map +0 -1
- package/dist/C_Language.js.map +0 -1
- package/dist/C_Language2.js.map +0 -1
- package/dist/C_Login-BrQBs_m4.css.map +0 -1
- package/dist/C_Login-xMI1_ojE.css.map +0 -1
- package/dist/C_Login.js.map +0 -1
- package/dist/C_Login2.js.map +0 -1
- package/dist/C_Map-CWMMN58_.css.map +0 -1
- package/dist/C_Map-xSR2H-9o.css.map +0 -1
- package/dist/C_Map.js.map +0 -1
- package/dist/C_Map2.js.map +0 -1
- package/dist/C_Markdown-C7lTVJ1S.css.map +0 -1
- package/dist/C_Markdown-CZDm_baQ.css.map +0 -1
- package/dist/C_Markdown.js.map +0 -1
- package/dist/C_Markdown2.js.map +0 -1
- package/dist/C_Menu.js.map +0 -1
- package/dist/C_Menu2.js.map +0 -1
- package/dist/C_NotificationCenter-D7GGGWg7.css.map +0 -1
- package/dist/C_NotificationCenter-SSmeHA45.css.map +0 -1
- package/dist/C_NotificationCenter.js.map +0 -1
- package/dist/C_NotificationCenter2.js.map +0 -1
- package/dist/C_OrgChart-BXEp4i4w.css.map +0 -1
- package/dist/C_OrgChart-DqBufOv5.css.map +0 -1
- package/dist/C_OrgChart.js.map +0 -1
- package/dist/C_OrgChart2.js.map +0 -1
- package/dist/C_Progress.js.map +0 -1
- package/dist/C_Progress2.js.map +0 -1
- package/dist/C_QRCode-B_1lHUdx.css.map +0 -1
- package/dist/C_QRCode-CuQRYu0s.css.map +0 -1
- package/dist/C_QRCode.js.map +0 -1
- package/dist/C_QRCode2.js.map +0 -1
- package/dist/C_Signature-Bp-RBpAl.css.map +0 -1
- package/dist/C_Signature-Cb2VW1ok.css.map +0 -1
- package/dist/C_Signature.js.map +0 -1
- package/dist/C_Signature2.js.map +0 -1
- package/dist/C_Skeleton-BJoFYwrF.css.map +0 -1
- package/dist/C_Skeleton-Djsjw8XY.css.map +0 -1
- package/dist/C_Skeleton.js.map +0 -1
- package/dist/C_Skeleton2.js.map +0 -1
- package/dist/C_SplitPane-CgGWYsRE.css.map +0 -1
- package/dist/C_SplitPane-DGf6WEeR.css.map +0 -1
- package/dist/C_SplitPane.js.map +0 -1
- package/dist/C_SplitPane2.js.map +0 -1
- package/dist/C_Steps-27Exh7PM.css.map +0 -1
- package/dist/C_Steps-DU9ZsFrs.css.map +0 -1
- package/dist/C_Steps.js.map +0 -1
- package/dist/C_Steps2.js.map +0 -1
- package/dist/C_Table--kbQaaBv.css.map +0 -1
- package/dist/C_Table-D631kdXC.css.map +0 -1
- package/dist/C_Table.js.map +0 -1
- package/dist/C_Table2.js +0 -4
- package/dist/C_Table2.js.map +0 -1
- package/dist/C_TagsView-DtGtYhBH.css.map +0 -1
- package/dist/C_TagsView-s1dnSJAV.css.map +0 -1
- package/dist/C_TagsView.js.map +0 -1
- package/dist/C_TagsView2.js.map +0 -1
- package/dist/C_Theme.js.map +0 -1
- package/dist/C_Theme2.js.map +0 -1
- package/dist/C_Time-BQ9PGy8B.css.map +0 -1
- package/dist/C_Time-CO73krC6.css.map +0 -1
- package/dist/C_Time.js.map +0 -1
- package/dist/C_Time2.js.map +0 -1
- package/dist/C_Timeline-DdESmqwL.css.map +0 -1
- package/dist/C_Timeline-irQcO-b7.css.map +0 -1
- package/dist/C_Timeline.js.map +0 -1
- package/dist/C_Timeline2.js.map +0 -1
- package/dist/C_Transfer-11y3k-WD.css.map +0 -1
- package/dist/C_Transfer-EzdHzZNv.css.map +0 -1
- package/dist/C_Transfer.js.map +0 -1
- package/dist/C_Transfer2.js.map +0 -1
- package/dist/C_Tree-CsXgGfhn.css.map +0 -1
- package/dist/C_Tree-P7eRpnOa.css.map +0 -1
- package/dist/C_Tree.js.map +0 -1
- package/dist/C_Tree2.js.map +0 -1
- package/dist/C_Upload-CHjiVVh7.css.map +0 -1
- package/dist/C_Upload-DE3OLsZD.css.map +0 -1
- package/dist/C_Upload.js.map +0 -1
- package/dist/C_Upload2.js.map +0 -1
- package/dist/C_VideoPlayer-CnaNOFtW.css.map +0 -1
- package/dist/C_VideoPlayer-cnP88zNV.css.map +0 -1
- package/dist/C_VideoPlayer.js.map +0 -1
- package/dist/C_VideoPlayer2.js.map +0 -1
- package/dist/C_VtableGantt-CZlJgvpC.css.map +0 -1
- package/dist/C_VtableGantt-DKfQkql0.css.map +0 -1
- package/dist/C_VtableGantt.js.map +0 -1
- package/dist/C_VtableGantt2.js.map +0 -1
- package/dist/C_WaterFall-BJ_39ext.css.map +0 -1
- package/dist/C_WaterFall-cYc1vntE.css.map +0 -1
- package/dist/C_WaterFall.js.map +0 -1
- package/dist/C_WaterFall2.js.map +0 -1
- package/dist/C_WorkFlow-BFzRGGhJ.css.map +0 -1
- package/dist/C_WorkFlow-DSyCOvRp.css.map +0 -1
- package/dist/C_WorkFlow.js.map +0 -1
- package/dist/C_WorkFlow2.js.map +0 -1
- package/dist/city.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index2.d.ts +0 -120
- package/dist/resolver.cjs.map +0 -1
- package/dist/resolver.js.map +0 -1
- package/dist/storage.js.map +0 -1
- /package/dist/{chunk.js → chunk.cjs} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_NotificationCenter2.js","names":["$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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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_OrgChart-BXEp4i4w.css","names":[],"sources":["../src/components/C_OrgChart/index.vue?vue&type=style&index=0&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled *//* C_OrgChart 组织架构图组件样式 */\n.c-orgchart {\n --c-orgchart-line-color: #d1d5db;\n --c-orgchart-line-width: 2px;\n --c-orgchart-node-spacing: 24px;\n --c-orgchart-level-spacing: 48px;\n --c-orgchart-node-bg: var(--card-color, #ffffff);\n --c-orgchart-node-border: var(--border-color, #e5e7eb);\n --c-orgchart-node-shadow: rgba(0, 0, 0, 0.06);\n --c-orgchart-node-hover-shadow: rgba(0, 0, 0, 0.12);\n --c-orgchart-label-color: var(--text-color-1, #262626);\n --c-orgchart-subtitle-color: var(--text-color-3, #999999);\n --c-orgchart-toggle-bg: var(--card-color, #ffffff);\n --c-orgchart-toggle-color: var(--text-color-2, #666666);\n --c-orgchart-avatar-size: 36px;\n --c-orgchart-controls-bg: var(--card-color, #ffffff);\n --c-orgchart-controls-shadow: rgba(0, 0, 0, 0.1);\n position: relative;\n width: 100%;\n height: 100%;\n min-height: 300px;\n overflow: hidden;\n cursor: grab;\n user-select: none;\n}\n.c-orgchart:active {\n cursor: grabbing;\n}\n.c-orgchart__viewport {\n display: inline-flex;\n justify-content: center;\n width: 100%;\n padding: 32px;\n transition: transform 0.15s ease;\n}\n\n/* ─── 节点包裹器(垂直布局) ─── */\n.c-orgchart--vertical .c-orgchart__node-wrapper {\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n.c-orgchart--vertical .c-orgchart__children {\n display: flex;\n justify-content: center;\n padding-top: var(--c-orgchart-level-spacing);\n gap: var(--c-orgchart-node-spacing);\n position: relative;\n /* 垂直连线:父→子 */\n /* 横连线:兄弟节点 */\n /* 兄弟之间的水平线 */\n}\n.c-orgchart--vertical .c-orgchart__children::before {\n content: \"\";\n position: absolute;\n top: 0;\n left: 50%;\n width: var(--c-orgchart-line-width);\n height: calc(var(--c-orgchart-level-spacing) / 2);\n background: var(--c-orgchart-line-color);\n transform: translateX(-50%);\n}\n.c-orgchart--vertical .c-orgchart__children > .c-orgchart__node-wrapper {\n position: relative;\n}\n.c-orgchart--vertical .c-orgchart__children > .c-orgchart__node-wrapper::before {\n content: \"\";\n position: absolute;\n top: calc(var(--c-orgchart-level-spacing) / -2);\n left: 50%;\n width: var(--c-orgchart-line-width);\n height: calc(var(--c-orgchart-level-spacing) / 2);\n background: var(--c-orgchart-line-color);\n transform: translateX(-50%);\n}\n.c-orgchart--vertical .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child)::after {\n content: \"\";\n position: absolute;\n top: calc(var(--c-orgchart-level-spacing) / -2);\n height: var(--c-orgchart-line-width);\n background: var(--c-orgchart-line-color);\n}\n.c-orgchart--vertical .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child):first-child::after {\n left: 50%;\n right: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n}\n.c-orgchart--vertical .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child):last-child::after {\n left: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n right: 50%;\n}\n.c-orgchart--vertical .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child):not(:first-child):not(:last-child)::after {\n left: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n right: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n}\n\n/* ─── 水平布局 ─── */\n.c-orgchart--horizontal .c-orgchart__node-wrapper {\n display: flex;\n align-items: center;\n}\n.c-orgchart--horizontal .c-orgchart__children {\n display: flex;\n flex-direction: column;\n justify-content: center;\n padding-left: var(--c-orgchart-level-spacing);\n gap: var(--c-orgchart-node-spacing);\n position: relative;\n}\n.c-orgchart--horizontal .c-orgchart__children::before {\n content: \"\";\n position: absolute;\n left: 0;\n top: 50%;\n height: var(--c-orgchart-line-width);\n width: calc(var(--c-orgchart-level-spacing) / 2);\n background: var(--c-orgchart-line-color);\n transform: translateY(-50%);\n}\n.c-orgchart--horizontal .c-orgchart__children > .c-orgchart__node-wrapper {\n position: relative;\n}\n.c-orgchart--horizontal .c-orgchart__children > .c-orgchart__node-wrapper::before {\n content: \"\";\n position: absolute;\n left: calc(var(--c-orgchart-level-spacing) / -2);\n top: 50%;\n width: calc(var(--c-orgchart-level-spacing) / 2);\n height: var(--c-orgchart-line-width);\n background: var(--c-orgchart-line-color);\n transform: translateY(-50%);\n}\n.c-orgchart--horizontal .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child)::after {\n content: \"\";\n position: absolute;\n left: calc(var(--c-orgchart-level-spacing) / -2);\n width: var(--c-orgchart-line-width);\n background: var(--c-orgchart-line-color);\n}\n.c-orgchart--horizontal .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child):first-child::after {\n top: 50%;\n bottom: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n}\n.c-orgchart--horizontal .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child):last-child::after {\n top: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n bottom: 50%;\n}\n.c-orgchart--horizontal .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child):not(:first-child):not(:last-child)::after {\n top: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n bottom: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n}\n\n/* ─── 圆角连线 ─── */\n.c-orgchart--line-rounded .c-orgchart__children::before,\n.c-orgchart--line-rounded .c-orgchart__node-wrapper::before,\n.c-orgchart--line-rounded .c-orgchart__node-wrapper::after {\n border-radius: 4px;\n}\n\n/* ─── 节点卡片 ─── */\n.c-orgchart__node {\n position: relative;\n background: var(--c-orgchart-node-bg);\n border: 1px solid var(--c-orgchart-node-border);\n border-radius: 8px;\n padding: 12px 16px;\n cursor: pointer;\n box-shadow: 0 2px 8px var(--c-orgchart-node-shadow);\n transition: box-shadow 0.2s ease, transform 0.2s ease;\n white-space: nowrap;\n}\n.c-orgchart__node:hover {\n box-shadow: 0 4px 16px var(--c-orgchart-node-hover-shadow);\n transform: translateY(-2px);\n}\n.c-orgchart__node--leaf {\n opacity: 0.9;\n}\n\n/* ─── 默认节点布局 ─── */\n.c-orgchart__node-default {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.c-orgchart__avatar {\n width: var(--c-orgchart-avatar-size);\n height: var(--c-orgchart-avatar-size);\n border-radius: 50%;\n overflow: hidden;\n flex-shrink: 0;\n}\n.c-orgchart__avatar img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.c-orgchart__info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.c-orgchart__label {\n font-size: 14px;\n font-weight: 600;\n color: var(--c-orgchart-label-color);\n}\n.c-orgchart__subtitle {\n font-size: 12px;\n color: var(--c-orgchart-subtitle-color);\n}\n\n/* ─── 折叠按钮 ─── */\n.c-orgchart__toggle {\n position: absolute;\n bottom: -12px;\n left: 50%;\n transform: translateX(-50%);\n width: 24px;\n height: 24px;\n border-radius: 50%;\n border: 1px solid var(--c-orgchart-node-border);\n background: var(--c-orgchart-toggle-bg);\n color: var(--c-orgchart-toggle-color);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1;\n transition: background 0.2s ease;\n}\n.c-orgchart__toggle:hover {\n background: color-mix(in srgb, var(--c-orgchart-node-border) 20%, var(--c-orgchart-toggle-bg));\n}\n.c-orgchart--horizontal .c-orgchart__toggle {\n bottom: auto;\n left: auto;\n right: -12px;\n top: 50%;\n transform: translateY(-50%);\n}\n\n/* ─── 缩放控制 ─── */\n.c-orgchart__controls {\n position: absolute;\n bottom: 16px;\n right: 16px;\n display: flex;\n align-items: center;\n gap: 4px;\n background: var(--c-orgchart-controls-bg);\n border-radius: 8px;\n padding: 4px;\n box-shadow: 0 2px 8px var(--c-orgchart-controls-shadow);\n z-index: 10;\n}\n.c-orgchart__control-btn {\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--c-orgchart-label-color);\n font-size: 16px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s ease;\n}\n.c-orgchart__control-btn:hover {\n background: color-mix(in srgb, var(--c-orgchart-node-border) 30%, transparent);\n}\n.c-orgchart__zoom-level {\n font-size: 12px;\n color: var(--c-orgchart-subtitle-color);\n min-width: 40px;\n text-align: center;\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_OrgChart-DqBufOv5.css","names":[],"sources":["../src/components/C_OrgChart/index.vue?vue&type=style&index=0&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled *//* C_OrgChart 组织架构图组件样式 */\n.c-orgchart {\n --c-orgchart-line-color: #d1d5db;\n --c-orgchart-line-width: 2px;\n --c-orgchart-node-spacing: 24px;\n --c-orgchart-level-spacing: 48px;\n --c-orgchart-node-bg: var(--card-color, #ffffff);\n --c-orgchart-node-border: var(--border-color, #e5e7eb);\n --c-orgchart-node-shadow: rgba(0, 0, 0, 0.06);\n --c-orgchart-node-hover-shadow: rgba(0, 0, 0, 0.12);\n --c-orgchart-label-color: var(--text-color-1, #262626);\n --c-orgchart-subtitle-color: var(--text-color-3, #999999);\n --c-orgchart-toggle-bg: var(--card-color, #ffffff);\n --c-orgchart-toggle-color: var(--text-color-2, #666666);\n --c-orgchart-avatar-size: 36px;\n --c-orgchart-controls-bg: var(--card-color, #ffffff);\n --c-orgchart-controls-shadow: rgba(0, 0, 0, 0.1);\n position: relative;\n width: 100%;\n height: 100%;\n min-height: 300px;\n overflow: hidden;\n cursor: grab;\n user-select: none;\n}\n.c-orgchart:active {\n cursor: grabbing;\n}\n.c-orgchart__viewport {\n display: inline-flex;\n justify-content: center;\n width: 100%;\n padding: 32px;\n transition: transform 0.15s ease;\n}\n\n/* ─── 节点包裹器(垂直布局) ─── */\n.c-orgchart--vertical .c-orgchart__node-wrapper {\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n.c-orgchart--vertical .c-orgchart__children {\n display: flex;\n justify-content: center;\n padding-top: var(--c-orgchart-level-spacing);\n gap: var(--c-orgchart-node-spacing);\n position: relative;\n /* 垂直连线:父→子 */\n /* 横连线:兄弟节点 */\n /* 兄弟之间的水平线 */\n}\n.c-orgchart--vertical .c-orgchart__children::before {\n content: \"\";\n position: absolute;\n top: 0;\n left: 50%;\n width: var(--c-orgchart-line-width);\n height: calc(var(--c-orgchart-level-spacing) / 2);\n background: var(--c-orgchart-line-color);\n transform: translateX(-50%);\n}\n.c-orgchart--vertical .c-orgchart__children > .c-orgchart__node-wrapper {\n position: relative;\n}\n.c-orgchart--vertical .c-orgchart__children > .c-orgchart__node-wrapper::before {\n content: \"\";\n position: absolute;\n top: calc(var(--c-orgchart-level-spacing) / -2);\n left: 50%;\n width: var(--c-orgchart-line-width);\n height: calc(var(--c-orgchart-level-spacing) / 2);\n background: var(--c-orgchart-line-color);\n transform: translateX(-50%);\n}\n.c-orgchart--vertical .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child)::after {\n content: \"\";\n position: absolute;\n top: calc(var(--c-orgchart-level-spacing) / -2);\n height: var(--c-orgchart-line-width);\n background: var(--c-orgchart-line-color);\n}\n.c-orgchart--vertical .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child):first-child::after {\n left: 50%;\n right: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n}\n.c-orgchart--vertical .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child):last-child::after {\n left: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n right: 50%;\n}\n.c-orgchart--vertical .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child):not(:first-child):not(:last-child)::after {\n left: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n right: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n}\n\n/* ─── 水平布局 ─── */\n.c-orgchart--horizontal .c-orgchart__node-wrapper {\n display: flex;\n align-items: center;\n}\n.c-orgchart--horizontal .c-orgchart__children {\n display: flex;\n flex-direction: column;\n justify-content: center;\n padding-left: var(--c-orgchart-level-spacing);\n gap: var(--c-orgchart-node-spacing);\n position: relative;\n}\n.c-orgchart--horizontal .c-orgchart__children::before {\n content: \"\";\n position: absolute;\n left: 0;\n top: 50%;\n height: var(--c-orgchart-line-width);\n width: calc(var(--c-orgchart-level-spacing) / 2);\n background: var(--c-orgchart-line-color);\n transform: translateY(-50%);\n}\n.c-orgchart--horizontal .c-orgchart__children > .c-orgchart__node-wrapper {\n position: relative;\n}\n.c-orgchart--horizontal .c-orgchart__children > .c-orgchart__node-wrapper::before {\n content: \"\";\n position: absolute;\n left: calc(var(--c-orgchart-level-spacing) / -2);\n top: 50%;\n width: calc(var(--c-orgchart-level-spacing) / 2);\n height: var(--c-orgchart-line-width);\n background: var(--c-orgchart-line-color);\n transform: translateY(-50%);\n}\n.c-orgchart--horizontal .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child)::after {\n content: \"\";\n position: absolute;\n left: calc(var(--c-orgchart-level-spacing) / -2);\n width: var(--c-orgchart-line-width);\n background: var(--c-orgchart-line-color);\n}\n.c-orgchart--horizontal .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child):first-child::after {\n top: 50%;\n bottom: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n}\n.c-orgchart--horizontal .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child):last-child::after {\n top: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n bottom: 50%;\n}\n.c-orgchart--horizontal .c-orgchart__children > .c-orgchart__node-wrapper:not(:only-child):not(:first-child):not(:last-child)::after {\n top: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n bottom: calc(-1 * var(--c-orgchart-node-spacing) / 2);\n}\n\n/* ─── 圆角连线 ─── */\n.c-orgchart--line-rounded .c-orgchart__children::before,\n.c-orgchart--line-rounded .c-orgchart__node-wrapper::before,\n.c-orgchart--line-rounded .c-orgchart__node-wrapper::after {\n border-radius: 4px;\n}\n\n/* ─── 节点卡片 ─── */\n.c-orgchart__node {\n position: relative;\n background: var(--c-orgchart-node-bg);\n border: 1px solid var(--c-orgchart-node-border);\n border-radius: 8px;\n padding: 12px 16px;\n cursor: pointer;\n box-shadow: 0 2px 8px var(--c-orgchart-node-shadow);\n transition: box-shadow 0.2s ease, transform 0.2s ease;\n white-space: nowrap;\n}\n.c-orgchart__node:hover {\n box-shadow: 0 4px 16px var(--c-orgchart-node-hover-shadow);\n transform: translateY(-2px);\n}\n.c-orgchart__node--leaf {\n opacity: 0.9;\n}\n\n/* ─── 默认节点布局 ─── */\n.c-orgchart__node-default {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.c-orgchart__avatar {\n width: var(--c-orgchart-avatar-size);\n height: var(--c-orgchart-avatar-size);\n border-radius: 50%;\n overflow: hidden;\n flex-shrink: 0;\n}\n.c-orgchart__avatar img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.c-orgchart__info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n.c-orgchart__label {\n font-size: 14px;\n font-weight: 600;\n color: var(--c-orgchart-label-color);\n}\n.c-orgchart__subtitle {\n font-size: 12px;\n color: var(--c-orgchart-subtitle-color);\n}\n\n/* ─── 折叠按钮 ─── */\n.c-orgchart__toggle {\n position: absolute;\n bottom: -12px;\n left: 50%;\n transform: translateX(-50%);\n width: 24px;\n height: 24px;\n border-radius: 50%;\n border: 1px solid var(--c-orgchart-node-border);\n background: var(--c-orgchart-toggle-bg);\n color: var(--c-orgchart-toggle-color);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1;\n transition: background 0.2s ease;\n}\n.c-orgchart__toggle:hover {\n background: color-mix(in srgb, var(--c-orgchart-node-border) 20%, var(--c-orgchart-toggle-bg));\n}\n.c-orgchart--horizontal .c-orgchart__toggle {\n bottom: auto;\n left: auto;\n right: -12px;\n top: 50%;\n transform: translateY(-50%);\n}\n\n/* ─── 缩放控制 ─── */\n.c-orgchart__controls {\n position: absolute;\n bottom: 16px;\n right: 16px;\n display: flex;\n align-items: center;\n gap: 4px;\n background: var(--c-orgchart-controls-bg);\n border-radius: 8px;\n padding: 4px;\n box-shadow: 0 2px 8px var(--c-orgchart-controls-shadow);\n z-index: 10;\n}\n.c-orgchart__control-btn {\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--c-orgchart-label-color);\n font-size: 16px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s ease;\n}\n.c-orgchart__control-btn:hover {\n background: color-mix(in srgb, var(--c-orgchart-node-border) 30%, transparent);\n}\n.c-orgchart__zoom-level {\n font-size: 12px;\n color: var(--c-orgchart-subtitle-color);\n min-width: 40px;\n text-align: center;\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
package/dist/C_OrgChart.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_OrgChart.js","names":["node","$emit","collapsible","$slots"],"sources":["../src/components/C_OrgChart/OrgNode.vue","../src/components/C_OrgChart/OrgNode.vue","../src/components/C_OrgChart/index.vue","../src/components/C_OrgChart/index.vue"],"sourcesContent":["<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2026-03-06\n * @Description: C_OrgChart 递归节点组件\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div class=\"c-orgchart__node-wrapper\">\n <!-- 节点卡片 -->\n <div\n class=\"c-orgchart__node\"\n :class=\"[node.className, { 'c-orgchart__node--leaf': isLeaf }]\"\n @click.stop=\"$emit('node-click', node)\"\n >\n <slot\n name=\"node\"\n :node=\"node\"\n :is-leaf=\"isLeaf\"\n :is-expanded=\"isExpanded\"\n >\n <!-- 默认节点渲染 -->\n <div class=\"c-orgchart__node-default\">\n <div\n v-if=\"node.avatar\"\n class=\"c-orgchart__avatar\"\n >\n <img\n :src=\"node.avatar\"\n :alt=\"node.label\"\n />\n </div>\n <div class=\"c-orgchart__info\">\n <div class=\"c-orgchart__label\">{{ node.label }}</div>\n <div\n v-if=\"node.subtitle\"\n class=\"c-orgchart__subtitle\"\n >\n {{ node.subtitle }}\n </div>\n </div>\n </div>\n </slot>\n\n <!-- 折叠/展开按钮 -->\n <button\n v-if=\"collapsible && !isLeaf\"\n class=\"c-orgchart__toggle\"\n @click.stop=\"toggleExpand\"\n >\n {{ isExpanded ? '−' : '+' }}\n </button>\n </div>\n\n <!-- 子节点容器 -->\n <div\n v-if=\"hasChildren && isExpanded\"\n class=\"c-orgchart__children\"\n >\n <OrgNode\n v-for=\"child in node.children\"\n :key=\"child.id\"\n :node=\"child\"\n :collapsible=\"collapsible\"\n @node-click=\"$emit('node-click', $event)\"\n @node-expand=\"$emit('node-expand', $event)\"\n @node-collapse=\"$emit('node-collapse', $event)\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed } from 'vue'\n import type { OrgChartNode } from './types'\n\n defineOptions({ name: 'OrgNode' })\n\n const props = defineProps<{\n node: OrgChartNode\n collapsible?: boolean\n }>()\n\n defineEmits<{\n 'node-click': [node: OrgChartNode]\n 'node-expand': [node: OrgChartNode]\n 'node-collapse': [node: OrgChartNode]\n }>()\n\n const isExpanded = ref(!props.node.collapsed)\n\n const hasChildren = computed(\n () => props.node.children && props.node.children.length > 0\n )\n\n const isLeaf = computed(() => !hasChildren.value)\n\n const toggleExpand = () => {\n isExpanded.value = !isExpanded.value\n }\n</script>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2026-03-06\n * @Description: C_OrgChart 递归节点组件\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div class=\"c-orgchart__node-wrapper\">\n <!-- 节点卡片 -->\n <div\n class=\"c-orgchart__node\"\n :class=\"[node.className, { 'c-orgchart__node--leaf': isLeaf }]\"\n @click.stop=\"$emit('node-click', node)\"\n >\n <slot\n name=\"node\"\n :node=\"node\"\n :is-leaf=\"isLeaf\"\n :is-expanded=\"isExpanded\"\n >\n <!-- 默认节点渲染 -->\n <div class=\"c-orgchart__node-default\">\n <div\n v-if=\"node.avatar\"\n class=\"c-orgchart__avatar\"\n >\n <img\n :src=\"node.avatar\"\n :alt=\"node.label\"\n />\n </div>\n <div class=\"c-orgchart__info\">\n <div class=\"c-orgchart__label\">{{ node.label }}</div>\n <div\n v-if=\"node.subtitle\"\n class=\"c-orgchart__subtitle\"\n >\n {{ node.subtitle }}\n </div>\n </div>\n </div>\n </slot>\n\n <!-- 折叠/展开按钮 -->\n <button\n v-if=\"collapsible && !isLeaf\"\n class=\"c-orgchart__toggle\"\n @click.stop=\"toggleExpand\"\n >\n {{ isExpanded ? '−' : '+' }}\n </button>\n </div>\n\n <!-- 子节点容器 -->\n <div\n v-if=\"hasChildren && isExpanded\"\n class=\"c-orgchart__children\"\n >\n <OrgNode\n v-for=\"child in node.children\"\n :key=\"child.id\"\n :node=\"child\"\n :collapsible=\"collapsible\"\n @node-click=\"$emit('node-click', $event)\"\n @node-expand=\"$emit('node-expand', $event)\"\n @node-collapse=\"$emit('node-collapse', $event)\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed } from 'vue'\n import type { OrgChartNode } from './types'\n\n defineOptions({ name: 'OrgNode' })\n\n const props = defineProps<{\n node: OrgChartNode\n collapsible?: boolean\n }>()\n\n defineEmits<{\n 'node-click': [node: OrgChartNode]\n 'node-expand': [node: OrgChartNode]\n 'node-collapse': [node: OrgChartNode]\n }>()\n\n const isExpanded = ref(!props.node.collapsed)\n\n const hasChildren = computed(\n () => props.node.children && props.node.children.length > 0\n )\n\n const isLeaf = computed(() => !hasChildren.value)\n\n const toggleExpand = () => {\n isExpanded.value = !isExpanded.value\n }\n</script>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2026-03-06\n * @Description: C_OrgChart 组织架构图组件\n * 纯 CSS + Vue 实现的树形组织架构图,支持缩放/平移/折叠\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div\n ref=\"containerRef\"\n class=\"c-orgchart\"\n :class=\"[\n `c-orgchart--${props.direction}`,\n `c-orgchart--line-${props.lineStyle}`,\n ]\"\n :style=\"cssVars\"\n @wheel.prevent=\"handleWheel\"\n @mousedown=\"handlePanStart\"\n @mousemove=\"handlePanMove\"\n @mouseup=\"handlePanEnd\"\n @mouseleave=\"handlePanEnd\"\n >\n <div\n class=\"c-orgchart__viewport\"\n :style=\"viewportStyle\"\n >\n <OrgNode\n :node=\"props.data\"\n :collapsible=\"props.collapsible\"\n @node-click=\"handleNodeClick\"\n @node-expand=\"handleNodeExpand\"\n @node-collapse=\"handleNodeCollapse\"\n >\n <template\n v-if=\"$slots.node\"\n #node=\"slotProps\"\n >\n <slot\n name=\"node\"\n v-bind=\"slotProps\"\n />\n </template>\n </OrgNode>\n </div>\n\n <!-- 缩放控制 -->\n <div\n v-if=\"props.zoomable\"\n class=\"c-orgchart__controls\"\n >\n <button\n class=\"c-orgchart__control-btn\"\n title=\"放大\"\n @click=\"zoomIn\"\n >\n +\n </button>\n <span class=\"c-orgchart__zoom-level\">{{ zoomPercent }}%</span>\n <button\n class=\"c-orgchart__control-btn\"\n title=\"缩小\"\n @click=\"zoomOut\"\n >\n −\n </button>\n <button\n class=\"c-orgchart__control-btn\"\n title=\"重置\"\n @click=\"resetView\"\n >\n ⟳\n </button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed } from 'vue'\n import OrgNode from './OrgNode.vue'\n import type { OrgChartProps, OrgChartNode } from './types'\n\n defineOptions({ name: 'C_OrgChart' })\n\n const props = withDefaults(defineProps<OrgChartProps>(), {\n direction: 'vertical',\n lineStyle: 'rounded',\n lineColor: '#d1d5db',\n lineWidth: 2,\n nodeSpacing: 24,\n levelSpacing: 48,\n zoomable: true,\n pannable: true,\n initialZoom: 1,\n minZoom: 0.3,\n maxZoom: 2,\n collapsible: true,\n })\n\n const emit = defineEmits<{\n 'node-click': [node: OrgChartNode]\n 'node-expand': [node: OrgChartNode]\n 'node-collapse': [node: OrgChartNode]\n }>()\n\n const containerRef = ref<HTMLElement | null>(null)\n\n // ─── 缩放 & 平移 ───\n const zoom = ref(props.initialZoom)\n const panX = ref(0)\n const panY = ref(0)\n const isPanning = ref(false)\n const panStartX = ref(0)\n const panStartY = ref(0)\n\n const zoomPercent = computed(() => Math.round(zoom.value * 100))\n\n const cssVars = computed(() => ({\n '--c-orgchart-line-color': props.lineColor,\n '--c-orgchart-line-width': `${props.lineWidth}px`,\n '--c-orgchart-node-spacing': `${props.nodeSpacing}px`,\n '--c-orgchart-level-spacing': `${props.levelSpacing}px`,\n }))\n\n const viewportStyle = computed(() => ({\n transform: `translate(${panX.value}px, ${panY.value}px) scale(${zoom.value})`,\n transformOrigin: 'center top',\n }))\n\n /**\n * * @description: 鼠标滚轮缩放\n */\n const handleWheel = (e: WheelEvent) => {\n if (!props.zoomable) return\n const delta = e.deltaY > 0 ? -0.1 : 0.1\n const newZoom = Math.max(\n props.minZoom,\n Math.min(props.maxZoom, zoom.value + delta)\n )\n zoom.value = Math.round(newZoom * 100) / 100\n }\n\n const handlePanStart = (e: MouseEvent) => {\n if (!props.pannable) return\n isPanning.value = true\n panStartX.value = e.clientX - panX.value\n panStartY.value = e.clientY - panY.value\n }\n\n const handlePanMove = (e: MouseEvent) => {\n if (!isPanning.value) return\n panX.value = e.clientX - panStartX.value\n panY.value = e.clientY - panStartY.value\n }\n\n const handlePanEnd = () => {\n isPanning.value = false\n }\n\n const zoomIn = () => {\n zoom.value = Math.min(props.maxZoom, zoom.value + 0.1)\n }\n\n const zoomOut = () => {\n zoom.value = Math.max(props.minZoom, zoom.value - 0.1)\n }\n\n const resetView = () => {\n zoom.value = props.initialZoom\n panX.value = 0\n panY.value = 0\n }\n\n // ─── 事件桥接 ───\n const handleNodeClick = (node: OrgChartNode) => emit('node-click', node)\n const handleNodeExpand = (node: OrgChartNode) => emit('node-expand', node)\n const handleNodeCollapse = (node: OrgChartNode) => emit('node-collapse', node)\n\n defineExpose({\n zoomIn,\n zoomOut,\n resetView,\n zoom,\n })\n</script>\n\n<style lang=\"scss\">\n @use './index.scss';\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2026-03-06\n * @Description: C_OrgChart 组织架构图组件\n * 纯 CSS + Vue 实现的树形组织架构图,支持缩放/平移/折叠\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div\n ref=\"containerRef\"\n class=\"c-orgchart\"\n :class=\"[\n `c-orgchart--${props.direction}`,\n `c-orgchart--line-${props.lineStyle}`,\n ]\"\n :style=\"cssVars\"\n @wheel.prevent=\"handleWheel\"\n @mousedown=\"handlePanStart\"\n @mousemove=\"handlePanMove\"\n @mouseup=\"handlePanEnd\"\n @mouseleave=\"handlePanEnd\"\n >\n <div\n class=\"c-orgchart__viewport\"\n :style=\"viewportStyle\"\n >\n <OrgNode\n :node=\"props.data\"\n :collapsible=\"props.collapsible\"\n @node-click=\"handleNodeClick\"\n @node-expand=\"handleNodeExpand\"\n @node-collapse=\"handleNodeCollapse\"\n >\n <template\n v-if=\"$slots.node\"\n #node=\"slotProps\"\n >\n <slot\n name=\"node\"\n v-bind=\"slotProps\"\n />\n </template>\n </OrgNode>\n </div>\n\n <!-- 缩放控制 -->\n <div\n v-if=\"props.zoomable\"\n class=\"c-orgchart__controls\"\n >\n <button\n class=\"c-orgchart__control-btn\"\n title=\"放大\"\n @click=\"zoomIn\"\n >\n +\n </button>\n <span class=\"c-orgchart__zoom-level\">{{ zoomPercent }}%</span>\n <button\n class=\"c-orgchart__control-btn\"\n title=\"缩小\"\n @click=\"zoomOut\"\n >\n −\n </button>\n <button\n class=\"c-orgchart__control-btn\"\n title=\"重置\"\n @click=\"resetView\"\n >\n ⟳\n </button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed } from 'vue'\n import OrgNode from './OrgNode.vue'\n import type { OrgChartProps, OrgChartNode } from './types'\n\n defineOptions({ name: 'C_OrgChart' })\n\n const props = withDefaults(defineProps<OrgChartProps>(), {\n direction: 'vertical',\n lineStyle: 'rounded',\n lineColor: '#d1d5db',\n lineWidth: 2,\n nodeSpacing: 24,\n levelSpacing: 48,\n zoomable: true,\n pannable: true,\n initialZoom: 1,\n minZoom: 0.3,\n maxZoom: 2,\n collapsible: true,\n })\n\n const emit = defineEmits<{\n 'node-click': [node: OrgChartNode]\n 'node-expand': [node: OrgChartNode]\n 'node-collapse': [node: OrgChartNode]\n }>()\n\n const containerRef = ref<HTMLElement | null>(null)\n\n // ─── 缩放 & 平移 ───\n const zoom = ref(props.initialZoom)\n const panX = ref(0)\n const panY = ref(0)\n const isPanning = ref(false)\n const panStartX = ref(0)\n const panStartY = ref(0)\n\n const zoomPercent = computed(() => Math.round(zoom.value * 100))\n\n const cssVars = computed(() => ({\n '--c-orgchart-line-color': props.lineColor,\n '--c-orgchart-line-width': `${props.lineWidth}px`,\n '--c-orgchart-node-spacing': `${props.nodeSpacing}px`,\n '--c-orgchart-level-spacing': `${props.levelSpacing}px`,\n }))\n\n const viewportStyle = computed(() => ({\n transform: `translate(${panX.value}px, ${panY.value}px) scale(${zoom.value})`,\n transformOrigin: 'center top',\n }))\n\n /**\n * * @description: 鼠标滚轮缩放\n */\n const handleWheel = (e: WheelEvent) => {\n if (!props.zoomable) return\n const delta = e.deltaY > 0 ? -0.1 : 0.1\n const newZoom = Math.max(\n props.minZoom,\n Math.min(props.maxZoom, zoom.value + delta)\n )\n zoom.value = Math.round(newZoom * 100) / 100\n }\n\n const handlePanStart = (e: MouseEvent) => {\n if (!props.pannable) return\n isPanning.value = true\n panStartX.value = e.clientX - panX.value\n panStartY.value = e.clientY - panY.value\n }\n\n const handlePanMove = (e: MouseEvent) => {\n if (!isPanning.value) return\n panX.value = e.clientX - panStartX.value\n panY.value = e.clientY - panStartY.value\n }\n\n const handlePanEnd = () => {\n isPanning.value = false\n }\n\n const zoomIn = () => {\n zoom.value = Math.min(props.maxZoom, zoom.value + 0.1)\n }\n\n const zoomOut = () => {\n zoom.value = Math.max(props.minZoom, zoom.value - 0.1)\n }\n\n const resetView = () => {\n zoom.value = props.initialZoom\n panX.value = 0\n panY.value = 0\n }\n\n // ─── 事件桥接 ───\n const handleNodeClick = (node: OrgChartNode) => emit('node-click', node)\n const handleNodeExpand = (node: OrgChartNode) => emit('node-expand', node)\n const handleNodeCollapse = (node: OrgChartNode) => emit('node-collapse', node)\n\n defineExpose({\n zoomIn,\n zoomOut,\n resetView,\n zoom,\n })\n</script>\n\n<style lang=\"scss\">\n @use './index.scss';\n</style>\n"],"mappings":"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"}
|
package/dist/C_OrgChart2.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_OrgChart2.js","names":["node","$emit","collapsible","$slots"],"sources":["../src/components/C_OrgChart/OrgNode.vue","../src/components/C_OrgChart/OrgNode.vue","../src/components/C_OrgChart/index.vue","../src/components/C_OrgChart/index.vue"],"sourcesContent":["<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2026-03-06\n * @Description: C_OrgChart 递归节点组件\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div class=\"c-orgchart__node-wrapper\">\n <!-- 节点卡片 -->\n <div\n class=\"c-orgchart__node\"\n :class=\"[node.className, { 'c-orgchart__node--leaf': isLeaf }]\"\n @click.stop=\"$emit('node-click', node)\"\n >\n <slot\n name=\"node\"\n :node=\"node\"\n :is-leaf=\"isLeaf\"\n :is-expanded=\"isExpanded\"\n >\n <!-- 默认节点渲染 -->\n <div class=\"c-orgchart__node-default\">\n <div\n v-if=\"node.avatar\"\n class=\"c-orgchart__avatar\"\n >\n <img\n :src=\"node.avatar\"\n :alt=\"node.label\"\n />\n </div>\n <div class=\"c-orgchart__info\">\n <div class=\"c-orgchart__label\">{{ node.label }}</div>\n <div\n v-if=\"node.subtitle\"\n class=\"c-orgchart__subtitle\"\n >\n {{ node.subtitle }}\n </div>\n </div>\n </div>\n </slot>\n\n <!-- 折叠/展开按钮 -->\n <button\n v-if=\"collapsible && !isLeaf\"\n class=\"c-orgchart__toggle\"\n @click.stop=\"toggleExpand\"\n >\n {{ isExpanded ? '−' : '+' }}\n </button>\n </div>\n\n <!-- 子节点容器 -->\n <div\n v-if=\"hasChildren && isExpanded\"\n class=\"c-orgchart__children\"\n >\n <OrgNode\n v-for=\"child in node.children\"\n :key=\"child.id\"\n :node=\"child\"\n :collapsible=\"collapsible\"\n @node-click=\"$emit('node-click', $event)\"\n @node-expand=\"$emit('node-expand', $event)\"\n @node-collapse=\"$emit('node-collapse', $event)\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed } from 'vue'\n import type { OrgChartNode } from './types'\n\n defineOptions({ name: 'OrgNode' })\n\n const props = defineProps<{\n node: OrgChartNode\n collapsible?: boolean\n }>()\n\n defineEmits<{\n 'node-click': [node: OrgChartNode]\n 'node-expand': [node: OrgChartNode]\n 'node-collapse': [node: OrgChartNode]\n }>()\n\n const isExpanded = ref(!props.node.collapsed)\n\n const hasChildren = computed(\n () => props.node.children && props.node.children.length > 0\n )\n\n const isLeaf = computed(() => !hasChildren.value)\n\n const toggleExpand = () => {\n isExpanded.value = !isExpanded.value\n }\n</script>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2026-03-06\n * @Description: C_OrgChart 递归节点组件\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div class=\"c-orgchart__node-wrapper\">\n <!-- 节点卡片 -->\n <div\n class=\"c-orgchart__node\"\n :class=\"[node.className, { 'c-orgchart__node--leaf': isLeaf }]\"\n @click.stop=\"$emit('node-click', node)\"\n >\n <slot\n name=\"node\"\n :node=\"node\"\n :is-leaf=\"isLeaf\"\n :is-expanded=\"isExpanded\"\n >\n <!-- 默认节点渲染 -->\n <div class=\"c-orgchart__node-default\">\n <div\n v-if=\"node.avatar\"\n class=\"c-orgchart__avatar\"\n >\n <img\n :src=\"node.avatar\"\n :alt=\"node.label\"\n />\n </div>\n <div class=\"c-orgchart__info\">\n <div class=\"c-orgchart__label\">{{ node.label }}</div>\n <div\n v-if=\"node.subtitle\"\n class=\"c-orgchart__subtitle\"\n >\n {{ node.subtitle }}\n </div>\n </div>\n </div>\n </slot>\n\n <!-- 折叠/展开按钮 -->\n <button\n v-if=\"collapsible && !isLeaf\"\n class=\"c-orgchart__toggle\"\n @click.stop=\"toggleExpand\"\n >\n {{ isExpanded ? '−' : '+' }}\n </button>\n </div>\n\n <!-- 子节点容器 -->\n <div\n v-if=\"hasChildren && isExpanded\"\n class=\"c-orgchart__children\"\n >\n <OrgNode\n v-for=\"child in node.children\"\n :key=\"child.id\"\n :node=\"child\"\n :collapsible=\"collapsible\"\n @node-click=\"$emit('node-click', $event)\"\n @node-expand=\"$emit('node-expand', $event)\"\n @node-collapse=\"$emit('node-collapse', $event)\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed } from 'vue'\n import type { OrgChartNode } from './types'\n\n defineOptions({ name: 'OrgNode' })\n\n const props = defineProps<{\n node: OrgChartNode\n collapsible?: boolean\n }>()\n\n defineEmits<{\n 'node-click': [node: OrgChartNode]\n 'node-expand': [node: OrgChartNode]\n 'node-collapse': [node: OrgChartNode]\n }>()\n\n const isExpanded = ref(!props.node.collapsed)\n\n const hasChildren = computed(\n () => props.node.children && props.node.children.length > 0\n )\n\n const isLeaf = computed(() => !hasChildren.value)\n\n const toggleExpand = () => {\n isExpanded.value = !isExpanded.value\n }\n</script>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2026-03-06\n * @Description: C_OrgChart 组织架构图组件\n * 纯 CSS + Vue 实现的树形组织架构图,支持缩放/平移/折叠\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div\n ref=\"containerRef\"\n class=\"c-orgchart\"\n :class=\"[\n `c-orgchart--${props.direction}`,\n `c-orgchart--line-${props.lineStyle}`,\n ]\"\n :style=\"cssVars\"\n @wheel.prevent=\"handleWheel\"\n @mousedown=\"handlePanStart\"\n @mousemove=\"handlePanMove\"\n @mouseup=\"handlePanEnd\"\n @mouseleave=\"handlePanEnd\"\n >\n <div\n class=\"c-orgchart__viewport\"\n :style=\"viewportStyle\"\n >\n <OrgNode\n :node=\"props.data\"\n :collapsible=\"props.collapsible\"\n @node-click=\"handleNodeClick\"\n @node-expand=\"handleNodeExpand\"\n @node-collapse=\"handleNodeCollapse\"\n >\n <template\n v-if=\"$slots.node\"\n #node=\"slotProps\"\n >\n <slot\n name=\"node\"\n v-bind=\"slotProps\"\n />\n </template>\n </OrgNode>\n </div>\n\n <!-- 缩放控制 -->\n <div\n v-if=\"props.zoomable\"\n class=\"c-orgchart__controls\"\n >\n <button\n class=\"c-orgchart__control-btn\"\n title=\"放大\"\n @click=\"zoomIn\"\n >\n +\n </button>\n <span class=\"c-orgchart__zoom-level\">{{ zoomPercent }}%</span>\n <button\n class=\"c-orgchart__control-btn\"\n title=\"缩小\"\n @click=\"zoomOut\"\n >\n −\n </button>\n <button\n class=\"c-orgchart__control-btn\"\n title=\"重置\"\n @click=\"resetView\"\n >\n ⟳\n </button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed } from 'vue'\n import OrgNode from './OrgNode.vue'\n import type { OrgChartProps, OrgChartNode } from './types'\n\n defineOptions({ name: 'C_OrgChart' })\n\n const props = withDefaults(defineProps<OrgChartProps>(), {\n direction: 'vertical',\n lineStyle: 'rounded',\n lineColor: '#d1d5db',\n lineWidth: 2,\n nodeSpacing: 24,\n levelSpacing: 48,\n zoomable: true,\n pannable: true,\n initialZoom: 1,\n minZoom: 0.3,\n maxZoom: 2,\n collapsible: true,\n })\n\n const emit = defineEmits<{\n 'node-click': [node: OrgChartNode]\n 'node-expand': [node: OrgChartNode]\n 'node-collapse': [node: OrgChartNode]\n }>()\n\n const containerRef = ref<HTMLElement | null>(null)\n\n // ─── 缩放 & 平移 ───\n const zoom = ref(props.initialZoom)\n const panX = ref(0)\n const panY = ref(0)\n const isPanning = ref(false)\n const panStartX = ref(0)\n const panStartY = ref(0)\n\n const zoomPercent = computed(() => Math.round(zoom.value * 100))\n\n const cssVars = computed(() => ({\n '--c-orgchart-line-color': props.lineColor,\n '--c-orgchart-line-width': `${props.lineWidth}px`,\n '--c-orgchart-node-spacing': `${props.nodeSpacing}px`,\n '--c-orgchart-level-spacing': `${props.levelSpacing}px`,\n }))\n\n const viewportStyle = computed(() => ({\n transform: `translate(${panX.value}px, ${panY.value}px) scale(${zoom.value})`,\n transformOrigin: 'center top',\n }))\n\n /**\n * * @description: 鼠标滚轮缩放\n */\n const handleWheel = (e: WheelEvent) => {\n if (!props.zoomable) return\n const delta = e.deltaY > 0 ? -0.1 : 0.1\n const newZoom = Math.max(\n props.minZoom,\n Math.min(props.maxZoom, zoom.value + delta)\n )\n zoom.value = Math.round(newZoom * 100) / 100\n }\n\n const handlePanStart = (e: MouseEvent) => {\n if (!props.pannable) return\n isPanning.value = true\n panStartX.value = e.clientX - panX.value\n panStartY.value = e.clientY - panY.value\n }\n\n const handlePanMove = (e: MouseEvent) => {\n if (!isPanning.value) return\n panX.value = e.clientX - panStartX.value\n panY.value = e.clientY - panStartY.value\n }\n\n const handlePanEnd = () => {\n isPanning.value = false\n }\n\n const zoomIn = () => {\n zoom.value = Math.min(props.maxZoom, zoom.value + 0.1)\n }\n\n const zoomOut = () => {\n zoom.value = Math.max(props.minZoom, zoom.value - 0.1)\n }\n\n const resetView = () => {\n zoom.value = props.initialZoom\n panX.value = 0\n panY.value = 0\n }\n\n // ─── 事件桥接 ───\n const handleNodeClick = (node: OrgChartNode) => emit('node-click', node)\n const handleNodeExpand = (node: OrgChartNode) => emit('node-expand', node)\n const handleNodeCollapse = (node: OrgChartNode) => emit('node-collapse', node)\n\n defineExpose({\n zoomIn,\n zoomOut,\n resetView,\n zoom,\n })\n</script>\n\n<style lang=\"scss\">\n @use './index.scss';\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2026-03-06\n * @Description: C_OrgChart 组织架构图组件\n * 纯 CSS + Vue 实现的树形组织架构图,支持缩放/平移/折叠\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <div\n ref=\"containerRef\"\n class=\"c-orgchart\"\n :class=\"[\n `c-orgchart--${props.direction}`,\n `c-orgchart--line-${props.lineStyle}`,\n ]\"\n :style=\"cssVars\"\n @wheel.prevent=\"handleWheel\"\n @mousedown=\"handlePanStart\"\n @mousemove=\"handlePanMove\"\n @mouseup=\"handlePanEnd\"\n @mouseleave=\"handlePanEnd\"\n >\n <div\n class=\"c-orgchart__viewport\"\n :style=\"viewportStyle\"\n >\n <OrgNode\n :node=\"props.data\"\n :collapsible=\"props.collapsible\"\n @node-click=\"handleNodeClick\"\n @node-expand=\"handleNodeExpand\"\n @node-collapse=\"handleNodeCollapse\"\n >\n <template\n v-if=\"$slots.node\"\n #node=\"slotProps\"\n >\n <slot\n name=\"node\"\n v-bind=\"slotProps\"\n />\n </template>\n </OrgNode>\n </div>\n\n <!-- 缩放控制 -->\n <div\n v-if=\"props.zoomable\"\n class=\"c-orgchart__controls\"\n >\n <button\n class=\"c-orgchart__control-btn\"\n title=\"放大\"\n @click=\"zoomIn\"\n >\n +\n </button>\n <span class=\"c-orgchart__zoom-level\">{{ zoomPercent }}%</span>\n <button\n class=\"c-orgchart__control-btn\"\n title=\"缩小\"\n @click=\"zoomOut\"\n >\n −\n </button>\n <button\n class=\"c-orgchart__control-btn\"\n title=\"重置\"\n @click=\"resetView\"\n >\n ⟳\n </button>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref, computed } from 'vue'\n import OrgNode from './OrgNode.vue'\n import type { OrgChartProps, OrgChartNode } from './types'\n\n defineOptions({ name: 'C_OrgChart' })\n\n const props = withDefaults(defineProps<OrgChartProps>(), {\n direction: 'vertical',\n lineStyle: 'rounded',\n lineColor: '#d1d5db',\n lineWidth: 2,\n nodeSpacing: 24,\n levelSpacing: 48,\n zoomable: true,\n pannable: true,\n initialZoom: 1,\n minZoom: 0.3,\n maxZoom: 2,\n collapsible: true,\n })\n\n const emit = defineEmits<{\n 'node-click': [node: OrgChartNode]\n 'node-expand': [node: OrgChartNode]\n 'node-collapse': [node: OrgChartNode]\n }>()\n\n const containerRef = ref<HTMLElement | null>(null)\n\n // ─── 缩放 & 平移 ───\n const zoom = ref(props.initialZoom)\n const panX = ref(0)\n const panY = ref(0)\n const isPanning = ref(false)\n const panStartX = ref(0)\n const panStartY = ref(0)\n\n const zoomPercent = computed(() => Math.round(zoom.value * 100))\n\n const cssVars = computed(() => ({\n '--c-orgchart-line-color': props.lineColor,\n '--c-orgchart-line-width': `${props.lineWidth}px`,\n '--c-orgchart-node-spacing': `${props.nodeSpacing}px`,\n '--c-orgchart-level-spacing': `${props.levelSpacing}px`,\n }))\n\n const viewportStyle = computed(() => ({\n transform: `translate(${panX.value}px, ${panY.value}px) scale(${zoom.value})`,\n transformOrigin: 'center top',\n }))\n\n /**\n * * @description: 鼠标滚轮缩放\n */\n const handleWheel = (e: WheelEvent) => {\n if (!props.zoomable) return\n const delta = e.deltaY > 0 ? -0.1 : 0.1\n const newZoom = Math.max(\n props.minZoom,\n Math.min(props.maxZoom, zoom.value + delta)\n )\n zoom.value = Math.round(newZoom * 100) / 100\n }\n\n const handlePanStart = (e: MouseEvent) => {\n if (!props.pannable) return\n isPanning.value = true\n panStartX.value = e.clientX - panX.value\n panStartY.value = e.clientY - panY.value\n }\n\n const handlePanMove = (e: MouseEvent) => {\n if (!isPanning.value) return\n panX.value = e.clientX - panStartX.value\n panY.value = e.clientY - panStartY.value\n }\n\n const handlePanEnd = () => {\n isPanning.value = false\n }\n\n const zoomIn = () => {\n zoom.value = Math.min(props.maxZoom, zoom.value + 0.1)\n }\n\n const zoomOut = () => {\n zoom.value = Math.max(props.minZoom, zoom.value - 0.1)\n }\n\n const resetView = () => {\n zoom.value = props.initialZoom\n panX.value = 0\n panY.value = 0\n }\n\n // ─── 事件桥接 ───\n const handleNodeClick = (node: OrgChartNode) => emit('node-click', node)\n const handleNodeExpand = (node: OrgChartNode) => emit('node-expand', node)\n const handleNodeCollapse = (node: OrgChartNode) => emit('node-collapse', node)\n\n defineExpose({\n zoomIn,\n zoomOut,\n resetView,\n zoom,\n })\n</script>\n\n<style lang=\"scss\">\n @use './index.scss';\n</style>\n"],"mappings":"8yBA6EE,IAAM,EAAQ,EAWR,EAAa,EAAI,CAAC,EAAM,KAAK,UAAS,CAEtC,EAAc,MACZ,EAAM,KAAK,UAAY,EAAM,KAAK,SAAS,OAAS,EAC5D,CAEM,EAAS,MAAe,CAAC,EAAY,MAAK,CAE1C,MAAqB,CACzB,EAAW,MAAQ,CAAC,EAAW,sDC1FjC,EA6DM,MA7DN,EA6DM,CA5DJ,EAAA,SAAa,CACb,EA0CM,MAAA,CAzCJ,MAAK,EAAA,CAAC,mBAAkB,CACfA,EAAAA,KAAK,UAAS,CAAA,yBAA8B,EAAA,MAAM,CAAA,CAAA,CAAA,CAC1D,QAAK,AAAA,EAAA,KAAA,EAAA,GAAOC,EAAAA,MAAK,aAAeD,EAAAA,KAAI,CAAA,CAAA,OAAA,CAAA,GAErC,EA2BO,EAAA,OAAA,OAAA,CAzBJ,KAAMA,EAAAA,KACN,OAAS,EAAA,MACT,WAAa,EAAA,WAuBT,CArBL,EAAA,WAAe,CACf,EAmBM,MAnBN,EAmBM,CAjBIA,EAAAA,KAAK,QAAA,GAAA,CADb,EAQM,MARN,EAQM,CAJJ,EAGE,MAAA,CAFC,IAAKA,EAAAA,KAAK,OACV,IAAKA,EAAAA,KAAK,iCAGf,EAQM,MARN,EAQM,CAPJ,EAAqD,MAArD,EAAqD,EAAnBA,EAAAA,KAAK,MAAK,CAAA,EAAA,CAEpCA,EAAAA,KAAK,UAAA,GAAA,CADb,EAKM,MALN,EAKM,EADDA,EAAAA,KAAK,SAAQ,CAAA,EAAA,EAAA,EAAA,OAAA,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAMxB,EAAA,YAAgB,CAERE,EAAAA,aAAW,CAAK,EAAA,OAAA,GAAA,CADxB,EAMS,SAAA,OAJP,MAAM,qBACL,QAAK,EAAO,EAAY,CAAA,OAAA,CAAA,IAEtB,EAAA,MAAU,IAAA,IAAA,CAAA,EAAA,EAAA,EAAA,OAAA,GAAA,KAIjB,EAAA,UAAc,CAEN,EAAA,OAAe,EAAA,OAAA,GAAA,CADvB,EAaM,MAbN,EAaM,EAAA,EAAA,GAAA,CATJ,EAQE,EAAA,KAAA,EAPgBF,EAAAA,KAAK,SAAd,QADT,EAQE,EAAA,CANC,IAAK,EAAM,GACX,KAAM,EACN,YAAaE,EAAAA,YACb,YAAU,AAAA,EAAA,KAAA,GAAED,EAAAA,MAAK,aAAe,EAAM,CACtC,aAAW,AAAA,EAAA,KAAA,GAAEA,EAAAA,MAAK,cAAgB,EAAM,CACxC,eAAa,AAAA,EAAA,KAAA,GAAEA,EAAAA,MAAK,gBAAkB,EAAM,8lBCkBnD,IAAM,EAAQ,EAeR,EAAO,EAMP,EAAe,EAAwB,KAAI,CAG3C,EAAO,EAAI,EAAM,YAAW,CAC5B,EAAO,EAAI,EAAC,CACZ,EAAO,EAAI,EAAC,CACZ,EAAY,EAAI,GAAK,CACrB,EAAY,EAAI,EAAC,CACjB,EAAY,EAAI,EAAC,CAEjB,EAAc,MAAe,KAAK,MAAM,EAAK,MAAQ,IAAI,CAAA,CAEzD,EAAU,OAAgB,CAC9B,0BAA2B,EAAM,UACjC,0BAA2B,GAAG,EAAM,UAAU,IAC9C,4BAA6B,GAAG,EAAM,YAAY,IAClD,6BAA8B,GAAG,EAAM,aAAa,IACrD,EAAC,CAEI,EAAgB,OAAgB,CACpC,UAAW,aAAa,EAAK,MAAM,MAAM,EAAK,MAAM,YAAY,EAAK,MAAM,GAC3E,gBAAiB,aAClB,EAAC,CAKI,EAAe,GAAkB,CACrC,GAAI,CAAC,EAAM,SAAU,OACrB,IAAM,EAAQ,EAAE,OAAS,EAAI,IAAO,GAC9B,EAAU,KAAK,IACnB,EAAM,QACN,KAAK,IAAI,EAAM,QAAS,EAAK,MAAQ,EAAK,CAC5C,CACA,EAAK,MAAQ,KAAK,MAAM,EAAU,IAAI,CAAG,KAGrC,EAAkB,GAAkB,CACnC,EAAM,WACX,EAAU,MAAQ,GAClB,EAAU,MAAQ,EAAE,QAAU,EAAK,MACnC,EAAU,MAAQ,EAAE,QAAU,EAAK,QAG/B,EAAiB,GAAkB,CAClC,EAAU,QACf,EAAK,MAAQ,EAAE,QAAU,EAAU,MACnC,EAAK,MAAQ,EAAE,QAAU,EAAU,QAG/B,MAAqB,CACzB,EAAU,MAAQ,IAGd,MAAe,CACnB,EAAK,MAAQ,KAAK,IAAI,EAAM,QAAS,EAAK,MAAQ,GAAG,EAGjD,MAAgB,CACpB,EAAK,MAAQ,KAAK,IAAI,EAAM,QAAS,EAAK,MAAQ,GAAG,EAGjD,MAAkB,CACtB,EAAK,MAAQ,EAAM,YACnB,EAAK,MAAQ,EACb,EAAK,MAAQ,GAIT,EAAmB,GAAuB,EAAK,aAAc,EAAI,CACjE,EAAoB,GAAuB,EAAK,cAAe,EAAI,CACnE,EAAsB,GAAuB,EAAK,gBAAiB,EAAI,QAE7E,EAAa,CACX,SACA,UACA,YACA,OACD,CAAA,aC9KD,EAiEM,MAAA,SAhEA,eAAJ,IAAI,EACJ,MAAK,EAAA,CAAC,aAAY,CAAA,eACa,EAAM,YAAA,oBAAuC,EAAM,YAAA,CAAA,CAAA,CAIjF,MAAK,EAAE,EAAA,MAAO,CACd,QAAK,EAAU,EAAW,CAAA,UAAA,CAAA,CAC1B,YAAW,EACX,YAAW,EACX,UAAS,EACT,aAAY,IAEb,EAqBM,MAAA,CApBJ,MAAM,uBACL,MAAK,EAAE,EAAA,MAAA,GAER,EAgBU,EAAA,CAfP,KAAM,EAAM,KACZ,YAAa,EAAM,YACnB,YAAY,EACZ,aAAa,EACb,eAAe,YAGRE,EAAAA,OAAO,KAAA,MACZ,YAAM,GAAS,CAEhB,EAGE,EAAA,OAAA,OAAA,EAAA,EADQ,EAAS,CAAA,CAAA,CAAA,CAAA,oDAMzB,EAAA,SAAa,CAEL,EAAM,UAAA,GAAA,CADd,EA0BM,MA1BN,EA0BM,CAtBJ,EAMS,SAAA,CALP,MAAM,0BACN,MAAM,KACL,QAAO,GACT,MAED,CACA,EAA8D,OAA9D,EAA8D,EAAtB,EAAA,MAAW,CAAG,IAAC,EAAA,CACvD,EAMS,SAAA,CALP,MAAM,0BACN,MAAM,KACL,QAAO,GACT,MAED,CACA,EAMS,SAAA,CALP,MAAM,0BACN,MAAM,KACL,QAAO,GACT,MAAA"}
|
package/dist/C_Progress.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_Progress.js","names":["isAnimation","showIndicator"],"sources":["../src/components/C_Progress/index.vue","../src/components/C_Progress/index.vue"],"sourcesContent":["<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 进度条组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NProgress\r\n v-bind=\"progressProps\"\r\n :percentage=\"processedPercentage\"\r\n :processing=\"isAnimation\"\r\n >\r\n <template v-if=\"showIndicator && hasIndicatorSlot\" #default>\r\n <slot name=\"indicator\" />\r\n </template>\r\n </NProgress>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { ref, computed, watch, useSlots, onMounted } from \"vue\";\r\nimport { NProgress } from \"naive-ui\";\r\nimport type { CSSProperties } from \"vue\";\r\n\r\ndefineOptions({ name: \"C_Progress\" });\r\n\r\ntype CSS = CSSProperties | string;\r\n\r\ninterface Props {\r\n percentage: number | number[];\r\n isAnimation?: boolean;\r\n time?: number;\r\n type?: \"line\" | \"circle\" | \"multiple-circle\" | \"dashboard\";\r\n borderRadius?: number | string;\r\n circleGap?: number;\r\n color?: string | string[] | { stops: string[] } | Array<{ stops: string[] }>;\r\n fillBorderRadius?: number | string;\r\n gapDegree?: number;\r\n gapOffsetDegree?: number;\r\n height?: number;\r\n indicatorPlacement?: \"inside\" | \"outside\";\r\n indicatorTextColor?: string;\r\n offsetDegree?: number;\r\n railColor?: string | string[];\r\n railStyle?: string | CSS | Array<string | CSS>;\r\n showIndicator?: boolean;\r\n status?: \"default\" | \"success\" | \"error\" | \"warning\" | \"info\";\r\n strokeWidth?: number;\r\n unit?: string;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n percentage: 0,\r\n isAnimation: false,\r\n time: 3000,\r\n indicatorPlacement: \"outside\",\r\n showIndicator: true,\r\n status: \"default\",\r\n strokeWidth: 7,\r\n unit: \"%\",\r\n});\r\n\r\nconst slots = useSlots();\r\nconst p = ref<number | number[]>(\r\n Array.isArray(props.percentage) ? [...props.percentage] : 0,\r\n);\r\n\r\nconst hasIndicatorSlot = computed(() => !!slots.indicator);\r\n\r\nconst processedPercentage = computed(() => {\r\n return props.type === \"multiple-circle\"\r\n ? Array.isArray(p.value)\r\n ? p.value\r\n : [p.value]\r\n : Array.isArray(p.value)\r\n ? p.value[0]\r\n : p.value;\r\n});\r\n\r\nconst progressProps = computed(() => ({\r\n type: props.type,\r\n borderRadius: props.borderRadius,\r\n circleGap: props.circleGap,\r\n color: props.color,\r\n fillBorderRadius: props.fillBorderRadius,\r\n gapDegree: props.gapDegree,\r\n gapOffsetDegree: props.gapOffsetDegree,\r\n height: props.height,\r\n indicatorPlacement: props.indicatorPlacement,\r\n indicatorTextColor: props.indicatorTextColor,\r\n offsetDegree: props.offsetDegree,\r\n railColor: props.railColor,\r\n railStyle: props.railStyle,\r\n status: props.status,\r\n strokeWidth: props.strokeWidth,\r\n unit: props.unit,\r\n}));\r\n\r\nwatch(\r\n () => props.percentage,\r\n (newVal) => {\r\n if (!props.isAnimation) {\r\n p.value = newVal;\r\n } else if (props.type === \"multiple-circle\" && Array.isArray(newVal)) {\r\n p.value = [...newVal];\r\n }\r\n },\r\n { immediate: true, deep: true },\r\n);\r\n\r\nonMounted(() => {\r\n if (props.isAnimation && props.type !== \"multiple-circle\") {\r\n const targetValue = Array.isArray(props.percentage)\r\n ? props.percentage[0]\r\n : props.percentage;\r\n\r\n if (targetValue > 0) {\r\n const startTime = performance.now();\r\n\r\n const animate = (now: number) => {\r\n const elapsed = now - startTime;\r\n const progress = Math.min(elapsed / props.time, 1);\r\n p.value = Math.round(progress * targetValue);\r\n\r\n if (progress < 1) {\r\n requestAnimationFrame(animate);\r\n }\r\n };\r\n\r\n requestAnimationFrame(animate);\r\n }\r\n }\r\n});\r\n</script>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 进度条组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NProgress\r\n v-bind=\"progressProps\"\r\n :percentage=\"processedPercentage\"\r\n :processing=\"isAnimation\"\r\n >\r\n <template v-if=\"showIndicator && hasIndicatorSlot\" #default>\r\n <slot name=\"indicator\" />\r\n </template>\r\n </NProgress>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { ref, computed, watch, useSlots, onMounted } from \"vue\";\r\nimport { NProgress } from \"naive-ui\";\r\nimport type { CSSProperties } from \"vue\";\r\n\r\ndefineOptions({ name: \"C_Progress\" });\r\n\r\ntype CSS = CSSProperties | string;\r\n\r\ninterface Props {\r\n percentage: number | number[];\r\n isAnimation?: boolean;\r\n time?: number;\r\n type?: \"line\" | \"circle\" | \"multiple-circle\" | \"dashboard\";\r\n borderRadius?: number | string;\r\n circleGap?: number;\r\n color?: string | string[] | { stops: string[] } | Array<{ stops: string[] }>;\r\n fillBorderRadius?: number | string;\r\n gapDegree?: number;\r\n gapOffsetDegree?: number;\r\n height?: number;\r\n indicatorPlacement?: \"inside\" | \"outside\";\r\n indicatorTextColor?: string;\r\n offsetDegree?: number;\r\n railColor?: string | string[];\r\n railStyle?: string | CSS | Array<string | CSS>;\r\n showIndicator?: boolean;\r\n status?: \"default\" | \"success\" | \"error\" | \"warning\" | \"info\";\r\n strokeWidth?: number;\r\n unit?: string;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n percentage: 0,\r\n isAnimation: false,\r\n time: 3000,\r\n indicatorPlacement: \"outside\",\r\n showIndicator: true,\r\n status: \"default\",\r\n strokeWidth: 7,\r\n unit: \"%\",\r\n});\r\n\r\nconst slots = useSlots();\r\nconst p = ref<number | number[]>(\r\n Array.isArray(props.percentage) ? [...props.percentage] : 0,\r\n);\r\n\r\nconst hasIndicatorSlot = computed(() => !!slots.indicator);\r\n\r\nconst processedPercentage = computed(() => {\r\n return props.type === \"multiple-circle\"\r\n ? Array.isArray(p.value)\r\n ? p.value\r\n : [p.value]\r\n : Array.isArray(p.value)\r\n ? p.value[0]\r\n : p.value;\r\n});\r\n\r\nconst progressProps = computed(() => ({\r\n type: props.type,\r\n borderRadius: props.borderRadius,\r\n circleGap: props.circleGap,\r\n color: props.color,\r\n fillBorderRadius: props.fillBorderRadius,\r\n gapDegree: props.gapDegree,\r\n gapOffsetDegree: props.gapOffsetDegree,\r\n height: props.height,\r\n indicatorPlacement: props.indicatorPlacement,\r\n indicatorTextColor: props.indicatorTextColor,\r\n offsetDegree: props.offsetDegree,\r\n railColor: props.railColor,\r\n railStyle: props.railStyle,\r\n status: props.status,\r\n strokeWidth: props.strokeWidth,\r\n unit: props.unit,\r\n}));\r\n\r\nwatch(\r\n () => props.percentage,\r\n (newVal) => {\r\n if (!props.isAnimation) {\r\n p.value = newVal;\r\n } else if (props.type === \"multiple-circle\" && Array.isArray(newVal)) {\r\n p.value = [...newVal];\r\n }\r\n },\r\n { immediate: true, deep: true },\r\n);\r\n\r\nonMounted(() => {\r\n if (props.isAnimation && props.type !== \"multiple-circle\") {\r\n const targetValue = Array.isArray(props.percentage)\r\n ? props.percentage[0]\r\n : props.percentage;\r\n\r\n if (targetValue > 0) {\r\n const startTime = performance.now();\r\n\r\n const animate = (now: number) => {\r\n const elapsed = now - startTime;\r\n const progress = Math.min(elapsed / props.time, 1);\r\n p.value = Math.round(progress * targetValue);\r\n\r\n if (progress < 1) {\r\n requestAnimationFrame(animate);\r\n }\r\n };\r\n\r\n requestAnimationFrame(animate);\r\n }\r\n }\r\n});\r\n</script>\r\n"],"mappings":"miBAmDA,IAAM,EAAQ,EAWR,GAAA,EAAA,EAAA,WAAkB,CAClB,GAAA,EAAA,EAAA,KACJ,MAAM,QAAQ,EAAM,WAAW,CAAG,CAAC,GAAG,EAAM,WAAW,CAAG,EAC3D,CAEK,GAAA,EAAA,EAAA,cAAkC,CAAC,CAAC,EAAM,UAAU,CAEpD,GAAA,EAAA,EAAA,cACG,EAAM,OAAS,kBAClB,MAAM,QAAQ,EAAE,MAAM,CACpB,EAAE,MACF,CAAC,EAAE,MAAM,CACX,MAAM,QAAQ,EAAE,MAAM,CACpB,EAAE,MAAM,GACR,EAAE,MACR,CAEI,GAAA,EAAA,EAAA,eAAgC,CACpC,KAAM,EAAM,KACZ,aAAc,EAAM,aACpB,UAAW,EAAM,UACjB,MAAO,EAAM,MACb,iBAAkB,EAAM,iBACxB,UAAW,EAAM,UACjB,gBAAiB,EAAM,gBACvB,OAAQ,EAAM,OACd,mBAAoB,EAAM,mBAC1B,mBAAoB,EAAM,mBAC1B,aAAc,EAAM,aACpB,UAAW,EAAM,UACjB,UAAW,EAAM,UACjB,OAAQ,EAAM,OACd,YAAa,EAAM,YACnB,KAAM,EAAM,KACb,EAAE,QAEH,EAAA,EAAA,WACQ,EAAM,WACX,GAAW,CACL,EAAM,YAEA,EAAM,OAAS,mBAAqB,MAAM,QAAQ,EAAO,GAClE,EAAE,MAAQ,CAAC,GAAG,EAAO,EAFrB,EAAE,MAAQ,GAKd,CAAE,UAAW,GAAM,KAAM,GAAM,CAChC,EAED,EAAA,EAAA,eAAgB,CACd,GAAI,EAAM,aAAe,EAAM,OAAS,kBAAmB,CACzD,IAAM,EAAc,MAAM,QAAQ,EAAM,WAAW,CAC/C,EAAM,WAAW,GACjB,EAAM,WAEV,GAAI,EAAc,EAAG,CACnB,IAAM,EAAY,YAAY,KAAK,CAE7B,EAAW,GAAgB,CAC/B,IAAM,EAAU,EAAM,EAChB,EAAW,KAAK,IAAI,EAAU,EAAM,KAAM,EAAE,CAClD,EAAE,MAAQ,KAAK,MAAM,EAAW,EAAY,CAExC,EAAW,GACb,sBAAsB,EAAQ,EAIlC,sBAAsB,EAAQ,IAGlC,yDCpHY,EAAA,UAAA,EAAA,EAAA,EAAA,YAPF,EAOE,MAPW,CACpB,WAAY,EAAA,MACZ,WAAYA,EAAAA,uCAEGC,EAAAA,eAAiB,EAAA,MAAA,MAAmB,+BACzB,EAAA,EAAA,EAAA,YAAA,EAAA,OAAA,YAAA,CAAA,CAAA"}
|
package/dist/C_Progress2.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_Progress2.js","names":["isAnimation","showIndicator"],"sources":["../src/components/C_Progress/index.vue","../src/components/C_Progress/index.vue"],"sourcesContent":["<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 进度条组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NProgress\r\n v-bind=\"progressProps\"\r\n :percentage=\"processedPercentage\"\r\n :processing=\"isAnimation\"\r\n >\r\n <template v-if=\"showIndicator && hasIndicatorSlot\" #default>\r\n <slot name=\"indicator\" />\r\n </template>\r\n </NProgress>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { ref, computed, watch, useSlots, onMounted } from \"vue\";\r\nimport { NProgress } from \"naive-ui\";\r\nimport type { CSSProperties } from \"vue\";\r\n\r\ndefineOptions({ name: \"C_Progress\" });\r\n\r\ntype CSS = CSSProperties | string;\r\n\r\ninterface Props {\r\n percentage: number | number[];\r\n isAnimation?: boolean;\r\n time?: number;\r\n type?: \"line\" | \"circle\" | \"multiple-circle\" | \"dashboard\";\r\n borderRadius?: number | string;\r\n circleGap?: number;\r\n color?: string | string[] | { stops: string[] } | Array<{ stops: string[] }>;\r\n fillBorderRadius?: number | string;\r\n gapDegree?: number;\r\n gapOffsetDegree?: number;\r\n height?: number;\r\n indicatorPlacement?: \"inside\" | \"outside\";\r\n indicatorTextColor?: string;\r\n offsetDegree?: number;\r\n railColor?: string | string[];\r\n railStyle?: string | CSS | Array<string | CSS>;\r\n showIndicator?: boolean;\r\n status?: \"default\" | \"success\" | \"error\" | \"warning\" | \"info\";\r\n strokeWidth?: number;\r\n unit?: string;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n percentage: 0,\r\n isAnimation: false,\r\n time: 3000,\r\n indicatorPlacement: \"outside\",\r\n showIndicator: true,\r\n status: \"default\",\r\n strokeWidth: 7,\r\n unit: \"%\",\r\n});\r\n\r\nconst slots = useSlots();\r\nconst p = ref<number | number[]>(\r\n Array.isArray(props.percentage) ? [...props.percentage] : 0,\r\n);\r\n\r\nconst hasIndicatorSlot = computed(() => !!slots.indicator);\r\n\r\nconst processedPercentage = computed(() => {\r\n return props.type === \"multiple-circle\"\r\n ? Array.isArray(p.value)\r\n ? p.value\r\n : [p.value]\r\n : Array.isArray(p.value)\r\n ? p.value[0]\r\n : p.value;\r\n});\r\n\r\nconst progressProps = computed(() => ({\r\n type: props.type,\r\n borderRadius: props.borderRadius,\r\n circleGap: props.circleGap,\r\n color: props.color,\r\n fillBorderRadius: props.fillBorderRadius,\r\n gapDegree: props.gapDegree,\r\n gapOffsetDegree: props.gapOffsetDegree,\r\n height: props.height,\r\n indicatorPlacement: props.indicatorPlacement,\r\n indicatorTextColor: props.indicatorTextColor,\r\n offsetDegree: props.offsetDegree,\r\n railColor: props.railColor,\r\n railStyle: props.railStyle,\r\n status: props.status,\r\n strokeWidth: props.strokeWidth,\r\n unit: props.unit,\r\n}));\r\n\r\nwatch(\r\n () => props.percentage,\r\n (newVal) => {\r\n if (!props.isAnimation) {\r\n p.value = newVal;\r\n } else if (props.type === \"multiple-circle\" && Array.isArray(newVal)) {\r\n p.value = [...newVal];\r\n }\r\n },\r\n { immediate: true, deep: true },\r\n);\r\n\r\nonMounted(() => {\r\n if (props.isAnimation && props.type !== \"multiple-circle\") {\r\n const targetValue = Array.isArray(props.percentage)\r\n ? props.percentage[0]\r\n : props.percentage;\r\n\r\n if (targetValue > 0) {\r\n const startTime = performance.now();\r\n\r\n const animate = (now: number) => {\r\n const elapsed = now - startTime;\r\n const progress = Math.min(elapsed / props.time, 1);\r\n p.value = Math.round(progress * targetValue);\r\n\r\n if (progress < 1) {\r\n requestAnimationFrame(animate);\r\n }\r\n };\r\n\r\n requestAnimationFrame(animate);\r\n }\r\n }\r\n});\r\n</script>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-06-01\r\n * @Description: 进度条组件\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <NProgress\r\n v-bind=\"progressProps\"\r\n :percentage=\"processedPercentage\"\r\n :processing=\"isAnimation\"\r\n >\r\n <template v-if=\"showIndicator && hasIndicatorSlot\" #default>\r\n <slot name=\"indicator\" />\r\n </template>\r\n </NProgress>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\nimport { ref, computed, watch, useSlots, onMounted } from \"vue\";\r\nimport { NProgress } from \"naive-ui\";\r\nimport type { CSSProperties } from \"vue\";\r\n\r\ndefineOptions({ name: \"C_Progress\" });\r\n\r\ntype CSS = CSSProperties | string;\r\n\r\ninterface Props {\r\n percentage: number | number[];\r\n isAnimation?: boolean;\r\n time?: number;\r\n type?: \"line\" | \"circle\" | \"multiple-circle\" | \"dashboard\";\r\n borderRadius?: number | string;\r\n circleGap?: number;\r\n color?: string | string[] | { stops: string[] } | Array<{ stops: string[] }>;\r\n fillBorderRadius?: number | string;\r\n gapDegree?: number;\r\n gapOffsetDegree?: number;\r\n height?: number;\r\n indicatorPlacement?: \"inside\" | \"outside\";\r\n indicatorTextColor?: string;\r\n offsetDegree?: number;\r\n railColor?: string | string[];\r\n railStyle?: string | CSS | Array<string | CSS>;\r\n showIndicator?: boolean;\r\n status?: \"default\" | \"success\" | \"error\" | \"warning\" | \"info\";\r\n strokeWidth?: number;\r\n unit?: string;\r\n}\r\n\r\nconst props = withDefaults(defineProps<Props>(), {\r\n percentage: 0,\r\n isAnimation: false,\r\n time: 3000,\r\n indicatorPlacement: \"outside\",\r\n showIndicator: true,\r\n status: \"default\",\r\n strokeWidth: 7,\r\n unit: \"%\",\r\n});\r\n\r\nconst slots = useSlots();\r\nconst p = ref<number | number[]>(\r\n Array.isArray(props.percentage) ? [...props.percentage] : 0,\r\n);\r\n\r\nconst hasIndicatorSlot = computed(() => !!slots.indicator);\r\n\r\nconst processedPercentage = computed(() => {\r\n return props.type === \"multiple-circle\"\r\n ? Array.isArray(p.value)\r\n ? p.value\r\n : [p.value]\r\n : Array.isArray(p.value)\r\n ? p.value[0]\r\n : p.value;\r\n});\r\n\r\nconst progressProps = computed(() => ({\r\n type: props.type,\r\n borderRadius: props.borderRadius,\r\n circleGap: props.circleGap,\r\n color: props.color,\r\n fillBorderRadius: props.fillBorderRadius,\r\n gapDegree: props.gapDegree,\r\n gapOffsetDegree: props.gapOffsetDegree,\r\n height: props.height,\r\n indicatorPlacement: props.indicatorPlacement,\r\n indicatorTextColor: props.indicatorTextColor,\r\n offsetDegree: props.offsetDegree,\r\n railColor: props.railColor,\r\n railStyle: props.railStyle,\r\n status: props.status,\r\n strokeWidth: props.strokeWidth,\r\n unit: props.unit,\r\n}));\r\n\r\nwatch(\r\n () => props.percentage,\r\n (newVal) => {\r\n if (!props.isAnimation) {\r\n p.value = newVal;\r\n } else if (props.type === \"multiple-circle\" && Array.isArray(newVal)) {\r\n p.value = [...newVal];\r\n }\r\n },\r\n { immediate: true, deep: true },\r\n);\r\n\r\nonMounted(() => {\r\n if (props.isAnimation && props.type !== \"multiple-circle\") {\r\n const targetValue = Array.isArray(props.percentage)\r\n ? props.percentage[0]\r\n : props.percentage;\r\n\r\n if (targetValue > 0) {\r\n const startTime = performance.now();\r\n\r\n const animate = (now: number) => {\r\n const elapsed = now - startTime;\r\n const progress = Math.min(elapsed / props.time, 1);\r\n p.value = Math.round(progress * targetValue);\r\n\r\n if (progress < 1) {\r\n requestAnimationFrame(animate);\r\n }\r\n };\r\n\r\n requestAnimationFrame(animate);\r\n }\r\n }\r\n});\r\n</script>\r\n"],"mappings":"isBAmDA,IAAM,EAAQ,EAWR,EAAQ,GAAU,CAClB,EAAI,EACR,MAAM,QAAQ,EAAM,WAAW,CAAG,CAAC,GAAG,EAAM,WAAW,CAAG,EAC3D,CAEK,EAAmB,MAAe,CAAC,CAAC,EAAM,UAAU,CAEpD,EAAsB,MACnB,EAAM,OAAS,kBAClB,MAAM,QAAQ,EAAE,MAAM,CACpB,EAAE,MACF,CAAC,EAAE,MAAM,CACX,MAAM,QAAQ,EAAE,MAAM,CACpB,EAAE,MAAM,GACR,EAAE,MACR,CAEI,EAAgB,OAAgB,CACpC,KAAM,EAAM,KACZ,aAAc,EAAM,aACpB,UAAW,EAAM,UACjB,MAAO,EAAM,MACb,iBAAkB,EAAM,iBACxB,UAAW,EAAM,UACjB,gBAAiB,EAAM,gBACvB,OAAQ,EAAM,OACd,mBAAoB,EAAM,mBAC1B,mBAAoB,EAAM,mBAC1B,aAAc,EAAM,aACpB,UAAW,EAAM,UACjB,UAAW,EAAM,UACjB,OAAQ,EAAM,OACd,YAAa,EAAM,YACnB,KAAM,EAAM,KACb,EAAE,QAEH,MACQ,EAAM,WACX,GAAW,CACL,EAAM,YAEA,EAAM,OAAS,mBAAqB,MAAM,QAAQ,EAAO,GAClE,EAAE,MAAQ,CAAC,GAAG,EAAO,EAFrB,EAAE,MAAQ,GAKd,CAAE,UAAW,GAAM,KAAM,GAAM,CAChC,CAED,MAAgB,CACd,GAAI,EAAM,aAAe,EAAM,OAAS,kBAAmB,CACzD,IAAM,EAAc,MAAM,QAAQ,EAAM,WAAW,CAC/C,EAAM,WAAW,GACjB,EAAM,WAEV,GAAI,EAAc,EAAG,CACnB,IAAM,EAAY,YAAY,KAAK,CAE7B,EAAW,GAAgB,CAC/B,IAAM,EAAU,EAAM,EAChB,EAAW,KAAK,IAAI,EAAU,EAAM,KAAM,EAAE,CAClD,EAAE,MAAQ,KAAK,MAAM,EAAW,EAAY,CAExC,EAAW,GACb,sBAAsB,EAAQ,EAIlC,sBAAsB,EAAQ,IAGlC,aC5HA,EAQY,EAAA,EAAA,CARZ,EACU,EAOE,MAPW,CACpB,WAAY,EAAA,MACZ,WAAYA,EAAAA,uBAEGC,EAAAA,eAAiB,EAAA,MAAA,MAAmB,mBACzB,CAAzB,EAAyB,EAAA,OAAA,YAAA,CAAA,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_QRCode-B_1lHUdx.css","names":[],"sources":["../src/components/C_QRCode/index.vue?vue&type=style&index=0&scoped=40ee5142&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled *//* C_QRCode styles */\n.c-qrcode[data-v-40ee5142] {\n display: inline-flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n max-width: 100%;\n}\n.c-qrcode .qrcode-wrapper[data-v-40ee5142] {\n display: inline-block;\n padding: 8px;\n background: var(--c-bg-content, #fff);\n border-radius: 6px;\n transition: all 0.3s ease;\n overflow: hidden;\n max-width: 100%;\n line-height: 0;\n}\n.c-qrcode .qrcode-wrapper.with-border[data-v-40ee5142] {\n border: 1px dashed var(--c-border-default, #e0e0e6);\n}\n.c-qrcode .qrcode-wrapper[data-v-40ee5142]:hover {\n border-color: var(--primary-color, #409eff);\n box-shadow: var(--c-shadow-sm, 0 1px 4px rgba(0, 0, 0, 0.08));\n}\n.c-qrcode .qrcode-wrapper canvas[data-v-40ee5142] {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.c-qrcode .qrcode-wrapper .qrcode-svg[data-v-40ee5142] {\n display: flex;\n align-items: center;\n justify-content: center;\n max-width: 100%;\n}\n.c-qrcode .qrcode-wrapper .qrcode-svg[data-v-40ee5142] svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n.c-qrcode .qrcode-label[data-v-40ee5142] {\n font-size: 14px;\n color: var(--c-text-secondary, #999);\n text-align: center;\n font-weight: 500;\n word-break: break-all;\n max-width: 100%;\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_QRCode-CuQRYu0s.css","names":[],"sources":["../src/components/C_QRCode/index.vue?vue&type=style&index=0&scoped=40ee5142&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled *//* C_QRCode styles */\n.c-qrcode[data-v-40ee5142] {\n display: inline-flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n max-width: 100%;\n}\n.c-qrcode .qrcode-wrapper[data-v-40ee5142] {\n display: inline-block;\n padding: 8px;\n background: var(--c-bg-content, #fff);\n border-radius: 6px;\n transition: all 0.3s ease;\n overflow: hidden;\n max-width: 100%;\n line-height: 0;\n}\n.c-qrcode .qrcode-wrapper.with-border[data-v-40ee5142] {\n border: 1px dashed var(--c-border-default, #e0e0e6);\n}\n.c-qrcode .qrcode-wrapper[data-v-40ee5142]:hover {\n border-color: var(--primary-color, #409eff);\n box-shadow: var(--c-shadow-sm, 0 1px 4px rgba(0, 0, 0, 0.08));\n}\n.c-qrcode .qrcode-wrapper canvas[data-v-40ee5142] {\n display: block;\n max-width: 100%;\n height: auto;\n}\n.c-qrcode .qrcode-wrapper .qrcode-svg[data-v-40ee5142] {\n display: flex;\n align-items: center;\n justify-content: center;\n max-width: 100%;\n}\n.c-qrcode .qrcode-wrapper .qrcode-svg[data-v-40ee5142] svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n.c-qrcode .qrcode-label[data-v-40ee5142] {\n font-size: 14px;\n color: var(--c-text-secondary, #999);\n text-align: center;\n font-weight: 500;\n word-break: break-all;\n max-width: 100%;\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
package/dist/C_QRCode.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_QRCode.js","names":["QRCode","showBorder","mode","size","showLabel","label"],"sources":["../src/components/C_QRCode/composables/useQRCode.ts","../src/components/C_QRCode/index.vue","../src/components/C_QRCode/index.vue"],"sourcesContent":["import { ref, computed, watch } from \"vue\";\r\nimport QRCode from \"qrcode\";\r\nimport type { Ref } from \"vue\";\r\nimport type {\r\n ErrorCorrectionLevel,\r\n ExportType,\r\n LogoOptions,\r\n RenderMode,\r\n} from \"../types\";\r\n\r\ninterface UseQRCodeOptions {\r\n value: Ref<string>;\r\n size: Ref<number>;\r\n color: Ref<string>;\r\n bgColor: Ref<string>;\r\n errorCorrectionLevel: Ref<ErrorCorrectionLevel>;\r\n margin: Ref<number>;\r\n mode: Ref<RenderMode>;\r\n logo: Ref<LogoOptions | undefined>;\r\n}\r\n\r\nfunction drawLogo(\r\n canvas: HTMLCanvasElement,\r\n logo: LogoOptions,\r\n qrSize: number,\r\n): Promise<void> {\r\n return new Promise((resolve, reject) => {\r\n const ctx = canvas.getContext(\"2d\");\r\n if (!ctx) return reject(new Error(\"Canvas context 不可用\"));\r\n\r\n const img = new Image();\r\n img.crossOrigin = \"anonymous\";\r\n img.onload = () => {\r\n const ratio = logo.size ?? 0.2;\r\n const logoSize = Math.floor(qrSize * ratio);\r\n const padding = logo.padding ?? 4;\r\n const borderRadius = logo.borderRadius ?? 4;\r\n\r\n const x = (canvas.width - logoSize) / 2;\r\n const y = (canvas.height - logoSize) / 2;\r\n\r\n ctx.save();\r\n const bgX = x - padding;\r\n const bgY = y - padding;\r\n const bgSize = logoSize + padding * 2;\r\n const r = borderRadius + padding;\r\n\r\n ctx.beginPath();\r\n ctx.moveTo(bgX + r, bgY);\r\n ctx.arcTo(bgX + bgSize, bgY, bgX + bgSize, bgY + bgSize, r);\r\n ctx.arcTo(bgX + bgSize, bgY + bgSize, bgX, bgY + bgSize, r);\r\n ctx.arcTo(bgX, bgY + bgSize, bgX, bgY, r);\r\n ctx.arcTo(bgX, bgY, bgX + bgSize, bgY, r);\r\n ctx.closePath();\r\n ctx.fillStyle = logo.bgColor ?? \"#ffffff\";\r\n ctx.fill();\r\n\r\n ctx.beginPath();\r\n ctx.moveTo(x + borderRadius, y);\r\n ctx.arcTo(x + logoSize, y, x + logoSize, y + logoSize, borderRadius);\r\n ctx.arcTo(x + logoSize, y + logoSize, x, y + logoSize, borderRadius);\r\n ctx.arcTo(x, y + logoSize, x, y, borderRadius);\r\n ctx.arcTo(x, y, x + logoSize, y, borderRadius);\r\n ctx.closePath();\r\n ctx.clip();\r\n ctx.drawImage(img, x, y, logoSize, logoSize);\r\n ctx.restore();\r\n\r\n resolve();\r\n };\r\n img.onerror = () => reject(new Error(`Logo 加载失败: ${logo.src}`));\r\n img.src = logo.src;\r\n });\r\n}\r\n\r\nexport function useQRCode(\r\n canvasRef: Ref<HTMLCanvasElement | null>,\r\n options: UseQRCodeOptions,\r\n) {\r\n const svgHtml = ref(\"\");\r\n const error = ref<Error | null>(null);\r\n const loading = ref(false);\r\n\r\n const effectiveLevel = computed<ErrorCorrectionLevel>(() => {\r\n if (options.logo.value) {\r\n const level = options.errorCorrectionLevel.value;\r\n if (level === \"L\" || level === \"M\") return \"Q\";\r\n return level;\r\n }\r\n return options.errorCorrectionLevel.value;\r\n });\r\n\r\n const qrOptions = computed(() => ({\r\n width: options.size.value,\r\n margin: options.margin.value,\r\n errorCorrectionLevel: effectiveLevel.value,\r\n color: {\r\n dark: options.color.value,\r\n light: options.bgColor.value,\r\n },\r\n }));\r\n\r\n async function renderCanvas() {\r\n const canvas = canvasRef.value;\r\n if (!canvas || !options.value.value) return;\r\n await QRCode.toCanvas(canvas, options.value.value, qrOptions.value);\r\n if (options.logo.value) {\r\n await drawLogo(canvas, options.logo.value, options.size.value);\r\n }\r\n }\r\n\r\n async function renderSvg() {\r\n if (!options.value.value) {\r\n svgHtml.value = \"\";\r\n return;\r\n }\r\n const svgString = await QRCode.toString(options.value.value, {\r\n ...qrOptions.value,\r\n type: \"svg\",\r\n });\r\n svgHtml.value = svgString;\r\n }\r\n\r\n async function render() {\r\n if (!options.value.value) return;\r\n loading.value = true;\r\n error.value = null;\r\n try {\r\n if (options.mode.value === \"canvas\") {\r\n await renderCanvas();\r\n } else {\r\n await renderSvg();\r\n }\r\n } catch (e) {\r\n error.value = e instanceof Error ? e : new Error(String(e));\r\n console.error(\"二维码渲染失败:\", e);\r\n } finally {\r\n loading.value = false;\r\n }\r\n }\r\n\r\n async function toDataURL(\r\n type: ExportType = \"png\",\r\n quality = 0.92,\r\n ): Promise<string> {\r\n if (type === \"svg\") {\r\n const svgStr = await QRCode.toString(options.value.value, {\r\n ...qrOptions.value,\r\n type: \"svg\",\r\n });\r\n return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgStr)}`;\r\n }\r\n const tempCanvas = document.createElement(\"canvas\");\r\n await QRCode.toCanvas(tempCanvas, options.value.value, qrOptions.value);\r\n if (options.logo.value) {\r\n await drawLogo(tempCanvas, options.logo.value, options.size.value);\r\n }\r\n const mimeType = type === \"jpeg\" ? \"image/jpeg\" : \"image/png\";\r\n return tempCanvas.toDataURL(mimeType, quality);\r\n }\r\n\r\n async function download(filename = \"qrcode\", type: ExportType = \"png\") {\r\n const dataUrl = await toDataURL(type);\r\n const link = document.createElement(\"a\");\r\n link.download = `${filename}.${type}`;\r\n link.href = dataUrl;\r\n link.click();\r\n }\r\n\r\n watch(\r\n [\r\n options.value,\r\n options.size,\r\n options.color,\r\n options.bgColor,\r\n options.errorCorrectionLevel,\r\n options.margin,\r\n options.mode,\r\n options.logo,\r\n ],\r\n () => render(),\r\n { deep: true },\r\n );\r\n\r\n return { svgHtml, error, loading, render, toDataURL, download };\r\n}\r\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-16\n * @Description: 二维码组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-qrcode\">\n <div class=\"qrcode-wrapper\" :class=\"{ 'with-border': showBorder }\">\n <canvas v-show=\"mode === 'canvas'\" ref=\"canvasRef\" />\n <div\n v-if=\"mode === 'svg'\"\n class=\"qrcode-svg\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n v-html=\"svgHtml\"\n />\n </div>\n <div v-if=\"showLabel && label\" class=\"qrcode-label\">\n {{ label }}\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, toRef, watch, onMounted } from \"vue\";\nimport { useQRCode } from \"./composables/useQRCode\";\nimport type {\n ErrorCorrectionLevel,\n ExportType,\n LogoOptions,\n QRCodeExpose,\n RenderMode,\n} from \"./types\";\n\ndefineOptions({ name: \"C_QRCode\" });\n\ninterface Props {\n value: string;\n size?: number;\n color?: string;\n bgColor?: string;\n errorCorrectionLevel?: ErrorCorrectionLevel;\n margin?: number;\n mode?: RenderMode;\n logo?: LogoOptions;\n showBorder?: boolean;\n label?: string;\n showLabel?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 200,\n color: \"#000000\",\n bgColor: \"#FFFFFF\",\n errorCorrectionLevel: \"M\",\n margin: 2,\n mode: \"canvas\",\n logo: undefined,\n showBorder: true,\n label: \"\",\n showLabel: false,\n});\n\nconst emit = defineEmits<{\n error: [error: Error];\n}>();\n\nconst canvasRef = ref<HTMLCanvasElement | null>(null);\n\nconst { svgHtml, error, render, toDataURL, download } = useQRCode(canvasRef, {\n value: toRef(props, \"value\"),\n size: toRef(props, \"size\"),\n color: toRef(props, \"color\"),\n bgColor: toRef(props, \"bgColor\"),\n errorCorrectionLevel: toRef(props, \"errorCorrectionLevel\"),\n margin: toRef(props, \"margin\"),\n mode: toRef(props, \"mode\"),\n logo: toRef(props, \"logo\"),\n});\n\nwatch(error, (e) => {\n if (e) emit(\"error\", e);\n});\n\nonMounted(() => render());\n\ndefineExpose<QRCodeExpose>({\n toDataURL: (type?: ExportType, quality?: number) => toDataURL(type, quality),\n download: (filename?: string, type?: ExportType) => download(filename, type),\n refresh: () => render(),\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-16\n * @Description: 二维码组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-qrcode\">\n <div class=\"qrcode-wrapper\" :class=\"{ 'with-border': showBorder }\">\n <canvas v-show=\"mode === 'canvas'\" ref=\"canvasRef\" />\n <div\n v-if=\"mode === 'svg'\"\n class=\"qrcode-svg\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n v-html=\"svgHtml\"\n />\n </div>\n <div v-if=\"showLabel && label\" class=\"qrcode-label\">\n {{ label }}\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, toRef, watch, onMounted } from \"vue\";\nimport { useQRCode } from \"./composables/useQRCode\";\nimport type {\n ErrorCorrectionLevel,\n ExportType,\n LogoOptions,\n QRCodeExpose,\n RenderMode,\n} from \"./types\";\n\ndefineOptions({ name: \"C_QRCode\" });\n\ninterface Props {\n value: string;\n size?: number;\n color?: string;\n bgColor?: string;\n errorCorrectionLevel?: ErrorCorrectionLevel;\n margin?: number;\n mode?: RenderMode;\n logo?: LogoOptions;\n showBorder?: boolean;\n label?: string;\n showLabel?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 200,\n color: \"#000000\",\n bgColor: \"#FFFFFF\",\n errorCorrectionLevel: \"M\",\n margin: 2,\n mode: \"canvas\",\n logo: undefined,\n showBorder: true,\n label: \"\",\n showLabel: false,\n});\n\nconst emit = defineEmits<{\n error: [error: Error];\n}>();\n\nconst canvasRef = ref<HTMLCanvasElement | null>(null);\n\nconst { svgHtml, error, render, toDataURL, download } = useQRCode(canvasRef, {\n value: toRef(props, \"value\"),\n size: toRef(props, \"size\"),\n color: toRef(props, \"color\"),\n bgColor: toRef(props, \"bgColor\"),\n errorCorrectionLevel: toRef(props, \"errorCorrectionLevel\"),\n margin: toRef(props, \"margin\"),\n mode: toRef(props, \"mode\"),\n logo: toRef(props, \"logo\"),\n});\n\nwatch(error, (e) => {\n if (e) emit(\"error\", e);\n});\n\nonMounted(() => render());\n\ndefineExpose<QRCodeExpose>({\n toDataURL: (type?: ExportType, quality?: number) => toDataURL(type, quality),\n download: (filename?: string, type?: ExportType) => download(filename, type),\n refresh: () => render(),\n});\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n"],"mappings":"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"}
|