@robot-admin/naive-ui-components 0.10.3 → 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 -100
- package/LICENSE +21 -21
- package/README.md +463 -318
- 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 -2
- 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 -4
- package/dist/C_Menu.js +1 -1
- 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 -3
- 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 +6 -7
- package/dist/constants2.d.cts +177 -0
- package/dist/constants2.d.ts +5 -6
- package/dist/constants3.d.cts +479 -0
- package/dist/constants3.d.ts +17 -13
- package/dist/constants4.d.cts +420 -0
- package/dist/constants4.d.ts +30 -40
- package/dist/constants5.d.cts +491 -0
- package/dist/constants5.d.ts +22 -6
- package/dist/data.cjs +1 -0
- package/dist/data.d.cts +67 -0
- package/dist/data.d.ts +13 -13
- 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 +41 -83
- 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 +1 -2
- 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} +5 -6
- 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 +3 -4
- package/dist/index27.vue.d.cts +80 -0
- package/dist/index27.vue.d.ts +3 -4
- package/dist/index3.vue.d.cts +87 -0
- package/dist/index3.vue.d.ts +2 -3
- package/dist/index4.vue.d.cts +37 -0
- package/dist/index4.vue.d.ts +5 -6
- 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/{menuAdapter.d.ts → public.d.ts} +2 -3
- 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 +58 -0
- 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 +1 -2
- package/dist/useCropperCore.d.cts +104 -0
- package/dist/useCropperCore.d.ts +12 -10
- package/dist/useDraggableLayout.d.cts +196 -0
- package/dist/useDraggableLayout.d.ts +18 -16
- 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 +11 -3
- package/dist/useQRCode.d.cts +89 -0
- package/dist/useQRCode.d.ts +7 -5
- package/dist/useSearchState.d.cts +871 -0
- package/dist/useSearchState.d.ts +828 -28
- package/dist/useSignatureHistory.d.cts +197 -0
- package/dist/useSignatureHistory.d.ts +9 -4
- 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 +19 -8
- package/dist/useTreeOperations.d.cts +185 -0
- package/dist/useTreeOperations.d.ts +14 -12
- package/dist/useWorkflowValidation.d.cts +1051 -0
- package/dist/useWorkflowValidation.d.ts +66 -67
- package/dist/verification.d.cts +58 -0
- package/dist/verification.d.ts +58 -0
- package/package.json +101 -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-ldTy9Y34.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-SIzxuuZr.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-Bqqrl8nX.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-CF1SU7mc.css.map +0 -1
- package/dist/C_Chat-DeUv6uuI.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-Bn_uhJSo.css.map +0 -1
- package/dist/C_ContextMenu-C9iuxG2n.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-B3qYkLrN.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-ChZu346B.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-BNPbaut7.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-BN8vMRsY.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-4rk0Dzgn.css.map +0 -1
- package/dist/C_Timeline-DdESmqwL.css.map +0 -1
- package/dist/C_Timeline.js.map +0 -1
- package/dist/C_Timeline2.js.map +0 -1
- package/dist/C_Transfer-B-vjvhjI.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/_shared.cjs +0 -1
- package/dist/_shared.d.cts +0 -2
- package/dist/_shared.d.ts +0 -2
- package/dist/_shared.js +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/menuAdapter.js +0 -2
- package/dist/menuAdapter.js.map +0 -1
- 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_GlobalSearch.js","names":["placeholder","emptyText","resultGroupTitle"],"sources":["../src/components/C_GlobalSearch/composables/useGlobalSearch.ts","../src/components/C_GlobalSearch/index.vue","../src/components/C_GlobalSearch/index.vue"],"sourcesContent":["import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'\nimport type { SearchMenuItem, HistoryItem, GlobalSearchOptions } from '../types'\n\n/**\n * 全局搜索逻辑 — 管理菜单搜索、历史记录、键盘导航\n *\n * 已解耦:不依赖任何项目级 store / router,\n * 所有外部数据通过 options 注入。\n */\nexport function useGlobalSearch(options: GlobalSearchOptions) {\n const {\n menuItems: getMenuItems,\n isDark: getIsDark,\n onSelect,\n storageKey = 'robot-search-history',\n maxHistory = 10,\n maxDisplayHistory = 5,\n } = options\n\n // ==================== 响应式状态 ====================\n const showDialog = ref(false)\n const searchValue = ref('')\n const selectedIndex = ref(0)\n const searchInputRef = ref<HTMLInputElement>()\n const searchHistory = ref<HistoryItem[]>([])\n\n // ==================== 计算属性 ====================\n const isDark = computed(() => getIsDark?.() ?? false)\n\n const filteredMenuItems = computed(() => {\n if (!searchValue.value.trim()) return []\n const query = searchValue.value.toLowerCase()\n const items = getMenuItems()\n return items.filter(\n item =>\n item.label.toLowerCase().includes(query) ||\n formatMenuPath(item.key).toLowerCase().includes(query)\n )\n })\n\n const displayHistory = computed(() =>\n searchHistory.value.slice(0, maxDisplayHistory)\n )\n const hasResults = computed(() => filteredMenuItems.value.length > 0)\n const hasContent = computed(\n () => hasResults.value || displayHistory.value.length > 0\n )\n\n // ==================== 工具函数 ====================\n const getItemClasses = (index: number, isHistory: boolean) => [\n 'robot-result-item',\n isHistory && 'robot-history-item',\n { 'robot-result-item-selected': selectedIndex.value === index },\n ]\n\n const highlightMatch = (text: string, search: string): string => {\n if (!search) return text\n const regex = new RegExp(\n `(${search.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})`,\n 'gi'\n )\n return text.replace(regex, '<mark class=\"robot-highlight\">$1</mark>')\n }\n\n const formatMenuPath = (key: string): string =>\n key.replace(/^\\//, '').replace(/\\//g, ' > ') || '首页'\n\n // ==================== 搜索历史管理 ====================\n const addToHistory = (menuItem: SearchMenuItem) => {\n if (!menuItem.label.trim()) return\n const query = menuItem.label\n const filtered = searchHistory.value.filter(\n (item: HistoryItem) =>\n !(item.query === query && item.menuItem?.key === menuItem.key)\n )\n searchHistory.value = [\n { query, menuItem, timestamp: Date.now() },\n ...filtered,\n ].slice(0, maxHistory)\n localStorage.setItem(storageKey, JSON.stringify(searchHistory.value))\n }\n\n const loadHistory = () => {\n try {\n const saved = localStorage.getItem(storageKey)\n if (saved) {\n const parsed = JSON.parse(saved)\n searchHistory.value = parsed.map((item: any) =>\n typeof item === 'string'\n ? { query: item, menuItem: null, timestamp: 0 }\n : item\n )\n }\n } catch (error) {\n console.error('加载搜索历史失败:', error)\n }\n }\n\n const clearHistory = () => {\n searchHistory.value = []\n localStorage.removeItem(storageKey)\n }\n\n const removeHistoryItem = (index: number) => {\n searchHistory.value.splice(index, 1)\n localStorage.setItem(storageKey, JSON.stringify(searchHistory.value))\n if (selectedIndex.value >= searchHistory.value.length) {\n selectedIndex.value = Math.max(0, searchHistory.value.length - 1)\n }\n }\n\n // ==================== 对话框控制 ====================\n const openDialog = async () => {\n showDialog.value = true\n searchValue.value = ''\n selectedIndex.value = 0\n await nextTick()\n searchInputRef.value?.focus()\n }\n\n const closeDialog = () => {\n showDialog.value = false\n searchValue.value = ''\n selectedIndex.value = 0\n }\n\n // ==================== 选择项目 ====================\n const selectHistoryItem = (historyItem: HistoryItem) => {\n if (historyItem.menuItem) {\n const hasChildren = !!historyItem.menuItem.children?.length\n onSelect?.(historyItem.menuItem.key, hasChildren)\n closeDialog()\n } else {\n searchValue.value = historyItem.query\n selectedIndex.value = 0\n }\n }\n\n const selectItem = (item: SearchMenuItem) => {\n addToHistory(item)\n const hasChildren = !!item.children?.length\n onSelect?.(item.key, hasChildren)\n closeDialog()\n }\n\n // ==================== 键盘导航 ====================\n const handleEnter = () => {\n const isHistoryMode =\n !searchValue.value.trim() && displayHistory.value.length > 0\n const isSearchMode =\n searchValue.value.trim() && filteredMenuItems.value.length > 0\n\n if (isHistoryMode) {\n const historyItem = displayHistory.value[selectedIndex.value]\n if (historyItem) selectHistoryItem(historyItem)\n } else if (isSearchMode) {\n const item = filteredMenuItems.value[selectedIndex.value]\n if (item) selectItem(item)\n }\n }\n\n const focusNext = () => {\n const maxIndex = !searchValue.value.trim()\n ? displayHistory.value.length - 1\n : filteredMenuItems.value.length - 1\n selectedIndex.value = Math.min(selectedIndex.value + 1, maxIndex)\n }\n\n const focusPrev = () => {\n selectedIndex.value = Math.max(selectedIndex.value - 1, 0)\n }\n\n // ==================== 监听器和生命周期 ====================\n watch(searchValue, () => {\n selectedIndex.value = 0\n })\n\n onMounted(() => {\n loadHistory()\n const handleKeydown = (e: KeyboardEvent) => {\n if ((e.ctrlKey || e.metaKey) && e.key === 'k') {\n e.preventDefault()\n openDialog()\n }\n }\n document.addEventListener('keydown', handleKeydown)\n onUnmounted(() => document.removeEventListener('keydown', handleKeydown))\n })\n\n return {\n showDialog,\n searchValue,\n selectedIndex,\n searchInputRef,\n isDark,\n filteredMenuItems,\n displayHistory,\n hasResults,\n hasContent,\n getItemClasses,\n highlightMatch,\n formatMenuPath,\n clearHistory,\n removeHistoryItem,\n openDialog,\n closeDialog,\n selectHistoryItem,\n selectItem,\n handleEnter,\n focusNext,\n focusPrev,\n }\n}\n","<!--\n * @Description: 全局搜索组件 - 命令面板式搜索\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"robot-search-palette\">\n <!-- 搜索触发按钮 -->\n <div\n class=\"robot-search-trigger\"\n :class=\"{ 'trigger-dark': isDark }\"\n @click=\"openDialog\"\n >\n <div class=\"i-mdi:search robot-trigger-icon\" />\n <span class=\"robot-trigger-text\">搜索</span>\n <span class=\"robot-trigger-shortcut\">Ctrl K</span>\n </div>\n\n <!-- 搜索对话框 -->\n <Teleport to=\"body\">\n <Transition name=\"robot-dialog\">\n <div\n v-if=\"showDialog\"\n class=\"robot-dialog-overlay\"\n @click=\"closeDialog\"\n @keydown.esc=\"closeDialog\"\n >\n <div\n class=\"robot-dialog-container\"\n :class=\"{ 'dialog-dark': isDark }\"\n @click.stop\n >\n <!-- 搜索头部 -->\n <div class=\"robot-search-header\">\n <div class=\"i-mdi:magnify robot-search-icon\" />\n <input\n ref=\"searchInputRef\"\n v-model=\"searchValue\"\n type=\"text\"\n :placeholder=\"placeholder\"\n class=\"robot-search-input\"\n @keydown.esc=\"closeDialog\"\n @keydown.enter=\"handleEnter\"\n @keydown.arrow-down=\"focusNext\"\n @keydown.arrow-up=\"focusPrev\"\n />\n <button\n class=\"robot-close-btn\"\n @click=\"closeDialog\"\n >\n <div class=\"i-mdi:close\" />\n </button>\n </div>\n\n <!-- 搜索内容 -->\n <div class=\"robot-search-content\">\n <!-- 空状态 -->\n <div\n v-if=\"!hasContent\"\n class=\"robot-empty-state\"\n >\n <div class=\"i-mdi:magnify robot-empty-icon\" />\n <p>{{ emptyText }}</p>\n </div>\n\n <!-- 搜索历史 -->\n <template\n v-else-if=\"!searchValue.trim() && displayHistory.length > 0\"\n >\n <div class=\"robot-search-results\">\n <div class=\"robot-result-group\">\n <div class=\"robot-group-title\">\n <div\n class=\"i-mdi:clock-time-four-outline robot-group-icon\"\n />\n 最近搜索\n <span class=\"robot-group-count\">{{\n displayHistory.length\n }}</span>\n <button\n class=\"robot-clear-history\"\n @click=\"clearHistory\"\n title=\"清空历史\"\n >\n <div class=\"i-mdi:delete-outline\" />\n </button>\n </div>\n <div class=\"robot-group-items\">\n <div\n v-for=\"(historyItem, index) in displayHistory\"\n :key=\"`history-${index}`\"\n :class=\"getItemClasses(index, true)\"\n @click=\"selectHistoryItem(historyItem)\"\n @mouseenter=\"selectedIndex = index\"\n >\n <component\n v-if=\"historyItem.menuItem?.icon\"\n :is=\"historyItem.menuItem.icon\"\n class=\"robot-item-icon\"\n />\n <div\n v-else\n class=\"robot-item-icon robot-default-icon\"\n >\n <div class=\"i-mdi:file-document-outline\" />\n </div>\n <div class=\"robot-item-content\">\n <div class=\"robot-item-label\">\n {{ historyItem.query }}\n </div>\n <div class=\"robot-item-desc\">\n {{\n formatMenuPath(historyItem.menuItem?.key || '')\n }}\n </div>\n </div>\n <button\n class=\"robot-history-delete\"\n @click.stop=\"removeHistoryItem(index)\"\n title=\"删除此记录\"\n >\n <div class=\"i-mdi:close\" />\n </button>\n </div>\n </div>\n </div>\n </div>\n </template>\n\n <!-- 搜索结果 -->\n <template v-else-if=\"searchValue.trim()\">\n <div class=\"robot-search-results\">\n <!-- 无结果状态 -->\n <div\n v-if=\"!hasResults\"\n class=\"robot-empty-state\"\n >\n <div class=\"i-mdi:magnify robot-empty-icon\" />\n <p>未找到 \"{{ searchValue }}\" 相关内容</p>\n </div>\n\n <!-- 搜索结果列表 -->\n <div\n v-else\n class=\"robot-result-group\"\n >\n <div class=\"robot-group-title\">\n <div class=\"i-mdi:menu robot-group-icon\" />\n {{ resultGroupTitle }}\n <span class=\"robot-group-count\">{{\n filteredMenuItems.length\n }}</span>\n </div>\n <div class=\"robot-group-items\">\n <div\n v-for=\"(item, index) in filteredMenuItems\"\n :key=\"item.key\"\n :class=\"getItemClasses(index, false)\"\n @click=\"selectItem(item)\"\n @mouseenter=\"selectedIndex = index\"\n >\n <component\n v-if=\"item.icon\"\n :is=\"item.icon\"\n class=\"robot-item-icon\"\n />\n <div\n v-else\n class=\"robot-item-icon robot-default-icon\"\n >\n <div class=\"i-mdi:file-document-outline\" />\n </div>\n <div class=\"robot-item-content\">\n <div\n class=\"robot-item-label\"\n v-html=\"highlightMatch(item.label, searchValue)\"\n ></div>\n <div class=\"robot-item-desc\">\n {{ formatMenuPath(item.key) }}\n </div>\n </div>\n <div\n v-if=\"item.children?.length\"\n class=\"robot-item-badge\"\n >\n {{ item.children.length }}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </div>\n\n <!-- 搜索底部 -->\n <div class=\"robot-search-footer\">\n <div class=\"robot-footer-actions\">\n <div class=\"robot-action-item\">\n <kbd>↵</kbd><span>选择</span>\n </div>\n <div class=\"robot-action-item\">\n <kbd>↑↓</kbd><span>导航</span>\n </div>\n <div class=\"robot-action-item\">\n <kbd>esc</kbd><span>关闭</span>\n </div>\n </div>\n <div\n v-if=\"hasContent\"\n class=\"robot-footer-info\"\n >\n <span v-if=\"searchValue.trim() && hasResults\">\n 共 {{ filteredMenuItems.length }} 项结果\n </span>\n <span\n v-else-if=\"!searchValue.trim() && displayHistory.length > 0\"\n >\n {{ displayHistory.length }} 条历史记录\n </span>\n </div>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { useGlobalSearch } from './composables/useGlobalSearch'\n import type { GlobalSearchOptions } from './types'\n\n defineOptions({ name: 'C_GlobalSearch' })\n\n interface Props {\n /** useGlobalSearch 配置(必须传入 menuItems + onSelect) */\n options: GlobalSearchOptions\n /** 搜索框 placeholder */\n placeholder?: string\n /** 无内容提示文字 */\n emptyText?: string\n /** 结果分组标题 */\n resultGroupTitle?: string\n }\n\n const props = withDefaults(defineProps<Props>(), {\n placeholder: '搜索导航菜单...',\n emptyText: '输入关键词搜索菜单',\n resultGroupTitle: '菜单导航',\n })\n\n const {\n showDialog,\n searchValue,\n selectedIndex,\n searchInputRef,\n isDark,\n filteredMenuItems,\n displayHistory,\n hasResults,\n hasContent,\n getItemClasses,\n highlightMatch,\n formatMenuPath,\n clearHistory,\n removeHistoryItem,\n openDialog,\n closeDialog,\n selectHistoryItem,\n selectItem,\n handleEnter,\n focusNext,\n focusPrev,\n } = useGlobalSearch(props.options)\n\n defineExpose({ openDialog, closeDialog })\n</script>\n\n<style lang=\"scss\" scoped>\n @use './index.scss';\n</style>\n","<!--\n * @Description: 全局搜索组件 - 命令面板式搜索\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"robot-search-palette\">\n <!-- 搜索触发按钮 -->\n <div\n class=\"robot-search-trigger\"\n :class=\"{ 'trigger-dark': isDark }\"\n @click=\"openDialog\"\n >\n <div class=\"i-mdi:search robot-trigger-icon\" />\n <span class=\"robot-trigger-text\">搜索</span>\n <span class=\"robot-trigger-shortcut\">Ctrl K</span>\n </div>\n\n <!-- 搜索对话框 -->\n <Teleport to=\"body\">\n <Transition name=\"robot-dialog\">\n <div\n v-if=\"showDialog\"\n class=\"robot-dialog-overlay\"\n @click=\"closeDialog\"\n @keydown.esc=\"closeDialog\"\n >\n <div\n class=\"robot-dialog-container\"\n :class=\"{ 'dialog-dark': isDark }\"\n @click.stop\n >\n <!-- 搜索头部 -->\n <div class=\"robot-search-header\">\n <div class=\"i-mdi:magnify robot-search-icon\" />\n <input\n ref=\"searchInputRef\"\n v-model=\"searchValue\"\n type=\"text\"\n :placeholder=\"placeholder\"\n class=\"robot-search-input\"\n @keydown.esc=\"closeDialog\"\n @keydown.enter=\"handleEnter\"\n @keydown.arrow-down=\"focusNext\"\n @keydown.arrow-up=\"focusPrev\"\n />\n <button\n class=\"robot-close-btn\"\n @click=\"closeDialog\"\n >\n <div class=\"i-mdi:close\" />\n </button>\n </div>\n\n <!-- 搜索内容 -->\n <div class=\"robot-search-content\">\n <!-- 空状态 -->\n <div\n v-if=\"!hasContent\"\n class=\"robot-empty-state\"\n >\n <div class=\"i-mdi:magnify robot-empty-icon\" />\n <p>{{ emptyText }}</p>\n </div>\n\n <!-- 搜索历史 -->\n <template\n v-else-if=\"!searchValue.trim() && displayHistory.length > 0\"\n >\n <div class=\"robot-search-results\">\n <div class=\"robot-result-group\">\n <div class=\"robot-group-title\">\n <div\n class=\"i-mdi:clock-time-four-outline robot-group-icon\"\n />\n 最近搜索\n <span class=\"robot-group-count\">{{\n displayHistory.length\n }}</span>\n <button\n class=\"robot-clear-history\"\n @click=\"clearHistory\"\n title=\"清空历史\"\n >\n <div class=\"i-mdi:delete-outline\" />\n </button>\n </div>\n <div class=\"robot-group-items\">\n <div\n v-for=\"(historyItem, index) in displayHistory\"\n :key=\"`history-${index}`\"\n :class=\"getItemClasses(index, true)\"\n @click=\"selectHistoryItem(historyItem)\"\n @mouseenter=\"selectedIndex = index\"\n >\n <component\n v-if=\"historyItem.menuItem?.icon\"\n :is=\"historyItem.menuItem.icon\"\n class=\"robot-item-icon\"\n />\n <div\n v-else\n class=\"robot-item-icon robot-default-icon\"\n >\n <div class=\"i-mdi:file-document-outline\" />\n </div>\n <div class=\"robot-item-content\">\n <div class=\"robot-item-label\">\n {{ historyItem.query }}\n </div>\n <div class=\"robot-item-desc\">\n {{\n formatMenuPath(historyItem.menuItem?.key || '')\n }}\n </div>\n </div>\n <button\n class=\"robot-history-delete\"\n @click.stop=\"removeHistoryItem(index)\"\n title=\"删除此记录\"\n >\n <div class=\"i-mdi:close\" />\n </button>\n </div>\n </div>\n </div>\n </div>\n </template>\n\n <!-- 搜索结果 -->\n <template v-else-if=\"searchValue.trim()\">\n <div class=\"robot-search-results\">\n <!-- 无结果状态 -->\n <div\n v-if=\"!hasResults\"\n class=\"robot-empty-state\"\n >\n <div class=\"i-mdi:magnify robot-empty-icon\" />\n <p>未找到 \"{{ searchValue }}\" 相关内容</p>\n </div>\n\n <!-- 搜索结果列表 -->\n <div\n v-else\n class=\"robot-result-group\"\n >\n <div class=\"robot-group-title\">\n <div class=\"i-mdi:menu robot-group-icon\" />\n {{ resultGroupTitle }}\n <span class=\"robot-group-count\">{{\n filteredMenuItems.length\n }}</span>\n </div>\n <div class=\"robot-group-items\">\n <div\n v-for=\"(item, index) in filteredMenuItems\"\n :key=\"item.key\"\n :class=\"getItemClasses(index, false)\"\n @click=\"selectItem(item)\"\n @mouseenter=\"selectedIndex = index\"\n >\n <component\n v-if=\"item.icon\"\n :is=\"item.icon\"\n class=\"robot-item-icon\"\n />\n <div\n v-else\n class=\"robot-item-icon robot-default-icon\"\n >\n <div class=\"i-mdi:file-document-outline\" />\n </div>\n <div class=\"robot-item-content\">\n <div\n class=\"robot-item-label\"\n v-html=\"highlightMatch(item.label, searchValue)\"\n ></div>\n <div class=\"robot-item-desc\">\n {{ formatMenuPath(item.key) }}\n </div>\n </div>\n <div\n v-if=\"item.children?.length\"\n class=\"robot-item-badge\"\n >\n {{ item.children.length }}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </div>\n\n <!-- 搜索底部 -->\n <div class=\"robot-search-footer\">\n <div class=\"robot-footer-actions\">\n <div class=\"robot-action-item\">\n <kbd>↵</kbd><span>选择</span>\n </div>\n <div class=\"robot-action-item\">\n <kbd>↑↓</kbd><span>导航</span>\n </div>\n <div class=\"robot-action-item\">\n <kbd>esc</kbd><span>关闭</span>\n </div>\n </div>\n <div\n v-if=\"hasContent\"\n class=\"robot-footer-info\"\n >\n <span v-if=\"searchValue.trim() && hasResults\">\n 共 {{ filteredMenuItems.length }} 项结果\n </span>\n <span\n v-else-if=\"!searchValue.trim() && displayHistory.length > 0\"\n >\n {{ displayHistory.length }} 条历史记录\n </span>\n </div>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { useGlobalSearch } from './composables/useGlobalSearch'\n import type { GlobalSearchOptions } from './types'\n\n defineOptions({ name: 'C_GlobalSearch' })\n\n interface Props {\n /** useGlobalSearch 配置(必须传入 menuItems + onSelect) */\n options: GlobalSearchOptions\n /** 搜索框 placeholder */\n placeholder?: string\n /** 无内容提示文字 */\n emptyText?: string\n /** 结果分组标题 */\n resultGroupTitle?: string\n }\n\n const props = withDefaults(defineProps<Props>(), {\n placeholder: '搜索导航菜单...',\n emptyText: '输入关键词搜索菜单',\n resultGroupTitle: '菜单导航',\n })\n\n const {\n showDialog,\n searchValue,\n selectedIndex,\n searchInputRef,\n isDark,\n filteredMenuItems,\n displayHistory,\n hasResults,\n hasContent,\n getItemClasses,\n highlightMatch,\n formatMenuPath,\n clearHistory,\n removeHistoryItem,\n openDialog,\n closeDialog,\n selectHistoryItem,\n selectItem,\n handleEnter,\n focusNext,\n focusPrev,\n } = useGlobalSearch(props.options)\n\n defineExpose({ openDialog, closeDialog })\n</script>\n\n<style lang=\"scss\" scoped>\n @use './index.scss';\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_GlobalSearch2.js","names":["placeholder","emptyText","resultGroupTitle"],"sources":["../src/components/C_GlobalSearch/composables/useGlobalSearch.ts","../src/components/C_GlobalSearch/index.vue","../src/components/C_GlobalSearch/index.vue"],"sourcesContent":["import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'\nimport type { SearchMenuItem, HistoryItem, GlobalSearchOptions } from '../types'\n\n/**\n * 全局搜索逻辑 — 管理菜单搜索、历史记录、键盘导航\n *\n * 已解耦:不依赖任何项目级 store / router,\n * 所有外部数据通过 options 注入。\n */\nexport function useGlobalSearch(options: GlobalSearchOptions) {\n const {\n menuItems: getMenuItems,\n isDark: getIsDark,\n onSelect,\n storageKey = 'robot-search-history',\n maxHistory = 10,\n maxDisplayHistory = 5,\n } = options\n\n // ==================== 响应式状态 ====================\n const showDialog = ref(false)\n const searchValue = ref('')\n const selectedIndex = ref(0)\n const searchInputRef = ref<HTMLInputElement>()\n const searchHistory = ref<HistoryItem[]>([])\n\n // ==================== 计算属性 ====================\n const isDark = computed(() => getIsDark?.() ?? false)\n\n const filteredMenuItems = computed(() => {\n if (!searchValue.value.trim()) return []\n const query = searchValue.value.toLowerCase()\n const items = getMenuItems()\n return items.filter(\n item =>\n item.label.toLowerCase().includes(query) ||\n formatMenuPath(item.key).toLowerCase().includes(query)\n )\n })\n\n const displayHistory = computed(() =>\n searchHistory.value.slice(0, maxDisplayHistory)\n )\n const hasResults = computed(() => filteredMenuItems.value.length > 0)\n const hasContent = computed(\n () => hasResults.value || displayHistory.value.length > 0\n )\n\n // ==================== 工具函数 ====================\n const getItemClasses = (index: number, isHistory: boolean) => [\n 'robot-result-item',\n isHistory && 'robot-history-item',\n { 'robot-result-item-selected': selectedIndex.value === index },\n ]\n\n const highlightMatch = (text: string, search: string): string => {\n if (!search) return text\n const regex = new RegExp(\n `(${search.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')})`,\n 'gi'\n )\n return text.replace(regex, '<mark class=\"robot-highlight\">$1</mark>')\n }\n\n const formatMenuPath = (key: string): string =>\n key.replace(/^\\//, '').replace(/\\//g, ' > ') || '首页'\n\n // ==================== 搜索历史管理 ====================\n const addToHistory = (menuItem: SearchMenuItem) => {\n if (!menuItem.label.trim()) return\n const query = menuItem.label\n const filtered = searchHistory.value.filter(\n (item: HistoryItem) =>\n !(item.query === query && item.menuItem?.key === menuItem.key)\n )\n searchHistory.value = [\n { query, menuItem, timestamp: Date.now() },\n ...filtered,\n ].slice(0, maxHistory)\n localStorage.setItem(storageKey, JSON.stringify(searchHistory.value))\n }\n\n const loadHistory = () => {\n try {\n const saved = localStorage.getItem(storageKey)\n if (saved) {\n const parsed = JSON.parse(saved)\n searchHistory.value = parsed.map((item: any) =>\n typeof item === 'string'\n ? { query: item, menuItem: null, timestamp: 0 }\n : item\n )\n }\n } catch (error) {\n console.error('加载搜索历史失败:', error)\n }\n }\n\n const clearHistory = () => {\n searchHistory.value = []\n localStorage.removeItem(storageKey)\n }\n\n const removeHistoryItem = (index: number) => {\n searchHistory.value.splice(index, 1)\n localStorage.setItem(storageKey, JSON.stringify(searchHistory.value))\n if (selectedIndex.value >= searchHistory.value.length) {\n selectedIndex.value = Math.max(0, searchHistory.value.length - 1)\n }\n }\n\n // ==================== 对话框控制 ====================\n const openDialog = async () => {\n showDialog.value = true\n searchValue.value = ''\n selectedIndex.value = 0\n await nextTick()\n searchInputRef.value?.focus()\n }\n\n const closeDialog = () => {\n showDialog.value = false\n searchValue.value = ''\n selectedIndex.value = 0\n }\n\n // ==================== 选择项目 ====================\n const selectHistoryItem = (historyItem: HistoryItem) => {\n if (historyItem.menuItem) {\n const hasChildren = !!historyItem.menuItem.children?.length\n onSelect?.(historyItem.menuItem.key, hasChildren)\n closeDialog()\n } else {\n searchValue.value = historyItem.query\n selectedIndex.value = 0\n }\n }\n\n const selectItem = (item: SearchMenuItem) => {\n addToHistory(item)\n const hasChildren = !!item.children?.length\n onSelect?.(item.key, hasChildren)\n closeDialog()\n }\n\n // ==================== 键盘导航 ====================\n const handleEnter = () => {\n const isHistoryMode =\n !searchValue.value.trim() && displayHistory.value.length > 0\n const isSearchMode =\n searchValue.value.trim() && filteredMenuItems.value.length > 0\n\n if (isHistoryMode) {\n const historyItem = displayHistory.value[selectedIndex.value]\n if (historyItem) selectHistoryItem(historyItem)\n } else if (isSearchMode) {\n const item = filteredMenuItems.value[selectedIndex.value]\n if (item) selectItem(item)\n }\n }\n\n const focusNext = () => {\n const maxIndex = !searchValue.value.trim()\n ? displayHistory.value.length - 1\n : filteredMenuItems.value.length - 1\n selectedIndex.value = Math.min(selectedIndex.value + 1, maxIndex)\n }\n\n const focusPrev = () => {\n selectedIndex.value = Math.max(selectedIndex.value - 1, 0)\n }\n\n // ==================== 监听器和生命周期 ====================\n watch(searchValue, () => {\n selectedIndex.value = 0\n })\n\n onMounted(() => {\n loadHistory()\n const handleKeydown = (e: KeyboardEvent) => {\n if ((e.ctrlKey || e.metaKey) && e.key === 'k') {\n e.preventDefault()\n openDialog()\n }\n }\n document.addEventListener('keydown', handleKeydown)\n onUnmounted(() => document.removeEventListener('keydown', handleKeydown))\n })\n\n return {\n showDialog,\n searchValue,\n selectedIndex,\n searchInputRef,\n isDark,\n filteredMenuItems,\n displayHistory,\n hasResults,\n hasContent,\n getItemClasses,\n highlightMatch,\n formatMenuPath,\n clearHistory,\n removeHistoryItem,\n openDialog,\n closeDialog,\n selectHistoryItem,\n selectItem,\n handleEnter,\n focusNext,\n focusPrev,\n }\n}\n","<!--\n * @Description: 全局搜索组件 - 命令面板式搜索\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"robot-search-palette\">\n <!-- 搜索触发按钮 -->\n <div\n class=\"robot-search-trigger\"\n :class=\"{ 'trigger-dark': isDark }\"\n @click=\"openDialog\"\n >\n <div class=\"i-mdi:search robot-trigger-icon\" />\n <span class=\"robot-trigger-text\">搜索</span>\n <span class=\"robot-trigger-shortcut\">Ctrl K</span>\n </div>\n\n <!-- 搜索对话框 -->\n <Teleport to=\"body\">\n <Transition name=\"robot-dialog\">\n <div\n v-if=\"showDialog\"\n class=\"robot-dialog-overlay\"\n @click=\"closeDialog\"\n @keydown.esc=\"closeDialog\"\n >\n <div\n class=\"robot-dialog-container\"\n :class=\"{ 'dialog-dark': isDark }\"\n @click.stop\n >\n <!-- 搜索头部 -->\n <div class=\"robot-search-header\">\n <div class=\"i-mdi:magnify robot-search-icon\" />\n <input\n ref=\"searchInputRef\"\n v-model=\"searchValue\"\n type=\"text\"\n :placeholder=\"placeholder\"\n class=\"robot-search-input\"\n @keydown.esc=\"closeDialog\"\n @keydown.enter=\"handleEnter\"\n @keydown.arrow-down=\"focusNext\"\n @keydown.arrow-up=\"focusPrev\"\n />\n <button\n class=\"robot-close-btn\"\n @click=\"closeDialog\"\n >\n <div class=\"i-mdi:close\" />\n </button>\n </div>\n\n <!-- 搜索内容 -->\n <div class=\"robot-search-content\">\n <!-- 空状态 -->\n <div\n v-if=\"!hasContent\"\n class=\"robot-empty-state\"\n >\n <div class=\"i-mdi:magnify robot-empty-icon\" />\n <p>{{ emptyText }}</p>\n </div>\n\n <!-- 搜索历史 -->\n <template\n v-else-if=\"!searchValue.trim() && displayHistory.length > 0\"\n >\n <div class=\"robot-search-results\">\n <div class=\"robot-result-group\">\n <div class=\"robot-group-title\">\n <div\n class=\"i-mdi:clock-time-four-outline robot-group-icon\"\n />\n 最近搜索\n <span class=\"robot-group-count\">{{\n displayHistory.length\n }}</span>\n <button\n class=\"robot-clear-history\"\n @click=\"clearHistory\"\n title=\"清空历史\"\n >\n <div class=\"i-mdi:delete-outline\" />\n </button>\n </div>\n <div class=\"robot-group-items\">\n <div\n v-for=\"(historyItem, index) in displayHistory\"\n :key=\"`history-${index}`\"\n :class=\"getItemClasses(index, true)\"\n @click=\"selectHistoryItem(historyItem)\"\n @mouseenter=\"selectedIndex = index\"\n >\n <component\n v-if=\"historyItem.menuItem?.icon\"\n :is=\"historyItem.menuItem.icon\"\n class=\"robot-item-icon\"\n />\n <div\n v-else\n class=\"robot-item-icon robot-default-icon\"\n >\n <div class=\"i-mdi:file-document-outline\" />\n </div>\n <div class=\"robot-item-content\">\n <div class=\"robot-item-label\">\n {{ historyItem.query }}\n </div>\n <div class=\"robot-item-desc\">\n {{\n formatMenuPath(historyItem.menuItem?.key || '')\n }}\n </div>\n </div>\n <button\n class=\"robot-history-delete\"\n @click.stop=\"removeHistoryItem(index)\"\n title=\"删除此记录\"\n >\n <div class=\"i-mdi:close\" />\n </button>\n </div>\n </div>\n </div>\n </div>\n </template>\n\n <!-- 搜索结果 -->\n <template v-else-if=\"searchValue.trim()\">\n <div class=\"robot-search-results\">\n <!-- 无结果状态 -->\n <div\n v-if=\"!hasResults\"\n class=\"robot-empty-state\"\n >\n <div class=\"i-mdi:magnify robot-empty-icon\" />\n <p>未找到 \"{{ searchValue }}\" 相关内容</p>\n </div>\n\n <!-- 搜索结果列表 -->\n <div\n v-else\n class=\"robot-result-group\"\n >\n <div class=\"robot-group-title\">\n <div class=\"i-mdi:menu robot-group-icon\" />\n {{ resultGroupTitle }}\n <span class=\"robot-group-count\">{{\n filteredMenuItems.length\n }}</span>\n </div>\n <div class=\"robot-group-items\">\n <div\n v-for=\"(item, index) in filteredMenuItems\"\n :key=\"item.key\"\n :class=\"getItemClasses(index, false)\"\n @click=\"selectItem(item)\"\n @mouseenter=\"selectedIndex = index\"\n >\n <component\n v-if=\"item.icon\"\n :is=\"item.icon\"\n class=\"robot-item-icon\"\n />\n <div\n v-else\n class=\"robot-item-icon robot-default-icon\"\n >\n <div class=\"i-mdi:file-document-outline\" />\n </div>\n <div class=\"robot-item-content\">\n <div\n class=\"robot-item-label\"\n v-html=\"highlightMatch(item.label, searchValue)\"\n ></div>\n <div class=\"robot-item-desc\">\n {{ formatMenuPath(item.key) }}\n </div>\n </div>\n <div\n v-if=\"item.children?.length\"\n class=\"robot-item-badge\"\n >\n {{ item.children.length }}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </div>\n\n <!-- 搜索底部 -->\n <div class=\"robot-search-footer\">\n <div class=\"robot-footer-actions\">\n <div class=\"robot-action-item\">\n <kbd>↵</kbd><span>选择</span>\n </div>\n <div class=\"robot-action-item\">\n <kbd>↑↓</kbd><span>导航</span>\n </div>\n <div class=\"robot-action-item\">\n <kbd>esc</kbd><span>关闭</span>\n </div>\n </div>\n <div\n v-if=\"hasContent\"\n class=\"robot-footer-info\"\n >\n <span v-if=\"searchValue.trim() && hasResults\">\n 共 {{ filteredMenuItems.length }} 项结果\n </span>\n <span\n v-else-if=\"!searchValue.trim() && displayHistory.length > 0\"\n >\n {{ displayHistory.length }} 条历史记录\n </span>\n </div>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { useGlobalSearch } from './composables/useGlobalSearch'\n import type { GlobalSearchOptions } from './types'\n\n defineOptions({ name: 'C_GlobalSearch' })\n\n interface Props {\n /** useGlobalSearch 配置(必须传入 menuItems + onSelect) */\n options: GlobalSearchOptions\n /** 搜索框 placeholder */\n placeholder?: string\n /** 无内容提示文字 */\n emptyText?: string\n /** 结果分组标题 */\n resultGroupTitle?: string\n }\n\n const props = withDefaults(defineProps<Props>(), {\n placeholder: '搜索导航菜单...',\n emptyText: '输入关键词搜索菜单',\n resultGroupTitle: '菜单导航',\n })\n\n const {\n showDialog,\n searchValue,\n selectedIndex,\n searchInputRef,\n isDark,\n filteredMenuItems,\n displayHistory,\n hasResults,\n hasContent,\n getItemClasses,\n highlightMatch,\n formatMenuPath,\n clearHistory,\n removeHistoryItem,\n openDialog,\n closeDialog,\n selectHistoryItem,\n selectItem,\n handleEnter,\n focusNext,\n focusPrev,\n } = useGlobalSearch(props.options)\n\n defineExpose({ openDialog, closeDialog })\n</script>\n\n<style lang=\"scss\" scoped>\n @use './index.scss';\n</style>\n","<!--\n * @Description: 全局搜索组件 - 命令面板式搜索\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"robot-search-palette\">\n <!-- 搜索触发按钮 -->\n <div\n class=\"robot-search-trigger\"\n :class=\"{ 'trigger-dark': isDark }\"\n @click=\"openDialog\"\n >\n <div class=\"i-mdi:search robot-trigger-icon\" />\n <span class=\"robot-trigger-text\">搜索</span>\n <span class=\"robot-trigger-shortcut\">Ctrl K</span>\n </div>\n\n <!-- 搜索对话框 -->\n <Teleport to=\"body\">\n <Transition name=\"robot-dialog\">\n <div\n v-if=\"showDialog\"\n class=\"robot-dialog-overlay\"\n @click=\"closeDialog\"\n @keydown.esc=\"closeDialog\"\n >\n <div\n class=\"robot-dialog-container\"\n :class=\"{ 'dialog-dark': isDark }\"\n @click.stop\n >\n <!-- 搜索头部 -->\n <div class=\"robot-search-header\">\n <div class=\"i-mdi:magnify robot-search-icon\" />\n <input\n ref=\"searchInputRef\"\n v-model=\"searchValue\"\n type=\"text\"\n :placeholder=\"placeholder\"\n class=\"robot-search-input\"\n @keydown.esc=\"closeDialog\"\n @keydown.enter=\"handleEnter\"\n @keydown.arrow-down=\"focusNext\"\n @keydown.arrow-up=\"focusPrev\"\n />\n <button\n class=\"robot-close-btn\"\n @click=\"closeDialog\"\n >\n <div class=\"i-mdi:close\" />\n </button>\n </div>\n\n <!-- 搜索内容 -->\n <div class=\"robot-search-content\">\n <!-- 空状态 -->\n <div\n v-if=\"!hasContent\"\n class=\"robot-empty-state\"\n >\n <div class=\"i-mdi:magnify robot-empty-icon\" />\n <p>{{ emptyText }}</p>\n </div>\n\n <!-- 搜索历史 -->\n <template\n v-else-if=\"!searchValue.trim() && displayHistory.length > 0\"\n >\n <div class=\"robot-search-results\">\n <div class=\"robot-result-group\">\n <div class=\"robot-group-title\">\n <div\n class=\"i-mdi:clock-time-four-outline robot-group-icon\"\n />\n 最近搜索\n <span class=\"robot-group-count\">{{\n displayHistory.length\n }}</span>\n <button\n class=\"robot-clear-history\"\n @click=\"clearHistory\"\n title=\"清空历史\"\n >\n <div class=\"i-mdi:delete-outline\" />\n </button>\n </div>\n <div class=\"robot-group-items\">\n <div\n v-for=\"(historyItem, index) in displayHistory\"\n :key=\"`history-${index}`\"\n :class=\"getItemClasses(index, true)\"\n @click=\"selectHistoryItem(historyItem)\"\n @mouseenter=\"selectedIndex = index\"\n >\n <component\n v-if=\"historyItem.menuItem?.icon\"\n :is=\"historyItem.menuItem.icon\"\n class=\"robot-item-icon\"\n />\n <div\n v-else\n class=\"robot-item-icon robot-default-icon\"\n >\n <div class=\"i-mdi:file-document-outline\" />\n </div>\n <div class=\"robot-item-content\">\n <div class=\"robot-item-label\">\n {{ historyItem.query }}\n </div>\n <div class=\"robot-item-desc\">\n {{\n formatMenuPath(historyItem.menuItem?.key || '')\n }}\n </div>\n </div>\n <button\n class=\"robot-history-delete\"\n @click.stop=\"removeHistoryItem(index)\"\n title=\"删除此记录\"\n >\n <div class=\"i-mdi:close\" />\n </button>\n </div>\n </div>\n </div>\n </div>\n </template>\n\n <!-- 搜索结果 -->\n <template v-else-if=\"searchValue.trim()\">\n <div class=\"robot-search-results\">\n <!-- 无结果状态 -->\n <div\n v-if=\"!hasResults\"\n class=\"robot-empty-state\"\n >\n <div class=\"i-mdi:magnify robot-empty-icon\" />\n <p>未找到 \"{{ searchValue }}\" 相关内容</p>\n </div>\n\n <!-- 搜索结果列表 -->\n <div\n v-else\n class=\"robot-result-group\"\n >\n <div class=\"robot-group-title\">\n <div class=\"i-mdi:menu robot-group-icon\" />\n {{ resultGroupTitle }}\n <span class=\"robot-group-count\">{{\n filteredMenuItems.length\n }}</span>\n </div>\n <div class=\"robot-group-items\">\n <div\n v-for=\"(item, index) in filteredMenuItems\"\n :key=\"item.key\"\n :class=\"getItemClasses(index, false)\"\n @click=\"selectItem(item)\"\n @mouseenter=\"selectedIndex = index\"\n >\n <component\n v-if=\"item.icon\"\n :is=\"item.icon\"\n class=\"robot-item-icon\"\n />\n <div\n v-else\n class=\"robot-item-icon robot-default-icon\"\n >\n <div class=\"i-mdi:file-document-outline\" />\n </div>\n <div class=\"robot-item-content\">\n <div\n class=\"robot-item-label\"\n v-html=\"highlightMatch(item.label, searchValue)\"\n ></div>\n <div class=\"robot-item-desc\">\n {{ formatMenuPath(item.key) }}\n </div>\n </div>\n <div\n v-if=\"item.children?.length\"\n class=\"robot-item-badge\"\n >\n {{ item.children.length }}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </div>\n\n <!-- 搜索底部 -->\n <div class=\"robot-search-footer\">\n <div class=\"robot-footer-actions\">\n <div class=\"robot-action-item\">\n <kbd>↵</kbd><span>选择</span>\n </div>\n <div class=\"robot-action-item\">\n <kbd>↑↓</kbd><span>导航</span>\n </div>\n <div class=\"robot-action-item\">\n <kbd>esc</kbd><span>关闭</span>\n </div>\n </div>\n <div\n v-if=\"hasContent\"\n class=\"robot-footer-info\"\n >\n <span v-if=\"searchValue.trim() && hasResults\">\n 共 {{ filteredMenuItems.length }} 项结果\n </span>\n <span\n v-else-if=\"!searchValue.trim() && displayHistory.length > 0\"\n >\n {{ displayHistory.length }} 条历史记录\n </span>\n </div>\n </div>\n </div>\n </div>\n </Transition>\n </Teleport>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { useGlobalSearch } from './composables/useGlobalSearch'\n import type { GlobalSearchOptions } from './types'\n\n defineOptions({ name: 'C_GlobalSearch' })\n\n interface Props {\n /** useGlobalSearch 配置(必须传入 menuItems + onSelect) */\n options: GlobalSearchOptions\n /** 搜索框 placeholder */\n placeholder?: string\n /** 无内容提示文字 */\n emptyText?: string\n /** 结果分组标题 */\n resultGroupTitle?: string\n }\n\n const props = withDefaults(defineProps<Props>(), {\n placeholder: '搜索导航菜单...',\n emptyText: '输入关键词搜索菜单',\n resultGroupTitle: '菜单导航',\n })\n\n const {\n showDialog,\n searchValue,\n selectedIndex,\n searchInputRef,\n isDark,\n filteredMenuItems,\n displayHistory,\n hasResults,\n hasContent,\n getItemClasses,\n highlightMatch,\n formatMenuPath,\n clearHistory,\n removeHistoryItem,\n openDialog,\n closeDialog,\n selectHistoryItem,\n selectItem,\n handleEnter,\n focusNext,\n focusPrev,\n } = useGlobalSearch(props.options)\n\n defineExpose({ openDialog, closeDialog })\n</script>\n\n<style lang=\"scss\" scoped>\n @use './index.scss';\n</style>\n"],"mappings":"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"}
|
package/dist/C_Guide.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_Guide.js","names":[],"sources":["../src/components/C_Guide/index.vue","../src/components/C_Guide/index.vue"],"sourcesContent":["<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-01\n * @LastEditors: ChenYu ycyplus@gmail.com\n * @LastEditTime: 2026-03-06\n * @Description: 用户引导组件(基于 driver.js)— 增强版\n * 支持:步骤分组 / 键盘导航 / 步骤回调 / 主题自定义 / 完成持久化\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <NTooltip\n v-if=\"props.showTrigger\"\n placement=\"bottom\"\n trigger=\"hover\"\n >\n <template #trigger>\n <NButton\n text\n @click=\"startGuide\"\n >\n <C_Icon\n :name=\"props.triggerIcon\"\n :size=\"18\"\n />\n </NButton>\n </template>\n <span>{{ props.triggerTooltip }}</span>\n </NTooltip>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { NTooltip, NButton } from 'naive-ui'\n import C_Icon from '../C_Icon/index.vue'\n import { driver } from 'driver.js'\n import 'driver.js/dist/driver.css'\n import type { GuideProps, GuideStep } from './types'\n\n defineOptions({ name: 'C_Guide' })\n\n const props = withDefaults(defineProps<GuideProps>(), {\n steps: () => [],\n doneBtnText: '完成',\n nextBtnText: '下一步',\n prevBtnText: '上一步',\n showProgress: true,\n keyboard: true,\n animate: true,\n allowClose: true,\n popoverClass: 'driverjs-theme',\n showTrigger: true,\n triggerTooltip: '功能引导',\n triggerIcon: 'mdi:sign-routes',\n })\n\n const emit = defineEmits<{\n /** 引导开始 */\n start: []\n /** 引导完成(走完全部步骤) */\n complete: []\n /** 引导被用户关闭(未完成) */\n close: [currentStep: number]\n /** 步骤切换 */\n 'step-change': [stepIndex: number, step: GuideStep]\n }>()\n\n /** 持久化 key */\n const persistKey = computed(() => {\n const prefix = props.persistence?.keyPrefix ?? 'c_guide'\n return `${prefix}_completed`\n })\n\n /** 是否已完成引导 */\n const isCompleted = (): boolean => {\n if (!props.persistence?.enabled) return false\n try {\n return localStorage.getItem(persistKey.value) === 'true'\n } catch {\n return false\n }\n }\n\n /** 标记引导已完成 */\n const markCompleted = () => {\n if (!props.persistence?.enabled) return\n try {\n localStorage.setItem(persistKey.value, 'true')\n } catch {\n // localStorage 不可用时静默忽略\n }\n }\n\n /** 重置完成状态 */\n const resetCompleted = () => {\n try {\n localStorage.removeItem(persistKey.value)\n } catch {\n // 静默忽略\n }\n }\n\n /**\n * * @description: 过滤掉需要跳过的步骤\n * ! @return 有效步骤列表\n */\n const getActiveSteps = (): GuideStep[] => {\n return props.steps.filter(step => !step.skipIf?.())\n }\n\n /**\n * * @description: 构建主题相关的 CSS 变量\n * ! @return popoverClass 字符串\n */\n const buildPopoverClass = (): string => {\n const classes = [props.popoverClass]\n if (props.theme) {\n // 通过 CSS 变量注入主题\n const root = document.documentElement\n if (props.theme.popoverBgColor)\n root.style.setProperty(\n '--c-guide-popover-bg',\n props.theme.popoverBgColor\n )\n if (props.theme.popoverTextColor)\n root.style.setProperty(\n '--c-guide-popover-text',\n props.theme.popoverTextColor\n )\n if (props.theme.primaryColor)\n root.style.setProperty(\n '--c-guide-primary',\n props.theme.primaryColor\n )\n if (props.theme.overlayOpacity !== undefined)\n root.style.setProperty(\n '--c-guide-overlay-opacity',\n String(props.theme.overlayOpacity)\n )\n if (props.theme.borderRadius)\n root.style.setProperty(\n '--c-guide-radius',\n props.theme.borderRadius\n )\n }\n return classes.filter(Boolean).join(' ')\n }\n\n /**\n * * @description: 启动引导流程\n * ? @param {boolean} force 是否强制启动(忽略持久化状态)\n */\n const startGuide = (force = false) => {\n if (!force && isCompleted()) return\n\n const activeSteps = getActiveSteps()\n if (!activeSteps.length) {\n console.warn(\n '[C_Guide] 未提供引导步骤(steps),请通过 :steps prop 传入。'\n )\n return\n }\n\n let currentStepIndex = 0\n\n const driverObj = driver({\n popoverClass: buildPopoverClass(),\n animate: props.animate,\n showProgress: props.showProgress,\n allowClose: props.allowClose,\n doneBtnText: props.doneBtnText,\n nextBtnText: props.nextBtnText,\n prevBtnText: props.prevBtnText,\n steps: activeSteps.map(step => ({\n element: step.element,\n popover: step.popover,\n })),\n onHighlightStarted: (_el) => {\n const step = activeSteps[currentStepIndex]\n if (step) {\n step.onHighlightStarted?.(_el as Element | undefined, step)\n emit('step-change', currentStepIndex, step)\n }\n },\n onDeselected: (_el) => {\n const step = activeSteps[currentStepIndex]\n step?.onDeselected?.(_el as Element | undefined, step)\n currentStepIndex++\n },\n onDestroyStarted: () => {\n if (currentStepIndex >= activeSteps.length) {\n markCompleted()\n emit('complete')\n } else {\n emit('close', currentStepIndex)\n }\n driverObj.destroy()\n },\n })\n\n emit('start')\n driverObj.drive()\n }\n\n defineExpose({\n startGuide,\n resetCompleted,\n isCompleted,\n })\n</script>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-01\n * @LastEditors: ChenYu ycyplus@gmail.com\n * @LastEditTime: 2026-03-06\n * @Description: 用户引导组件(基于 driver.js)— 增强版\n * 支持:步骤分组 / 键盘导航 / 步骤回调 / 主题自定义 / 完成持久化\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <NTooltip\n v-if=\"props.showTrigger\"\n placement=\"bottom\"\n trigger=\"hover\"\n >\n <template #trigger>\n <NButton\n text\n @click=\"startGuide\"\n >\n <C_Icon\n :name=\"props.triggerIcon\"\n :size=\"18\"\n />\n </NButton>\n </template>\n <span>{{ props.triggerTooltip }}</span>\n </NTooltip>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { NTooltip, NButton } from 'naive-ui'\n import C_Icon from '../C_Icon/index.vue'\n import { driver } from 'driver.js'\n import 'driver.js/dist/driver.css'\n import type { GuideProps, GuideStep } from './types'\n\n defineOptions({ name: 'C_Guide' })\n\n const props = withDefaults(defineProps<GuideProps>(), {\n steps: () => [],\n doneBtnText: '完成',\n nextBtnText: '下一步',\n prevBtnText: '上一步',\n showProgress: true,\n keyboard: true,\n animate: true,\n allowClose: true,\n popoverClass: 'driverjs-theme',\n showTrigger: true,\n triggerTooltip: '功能引导',\n triggerIcon: 'mdi:sign-routes',\n })\n\n const emit = defineEmits<{\n /** 引导开始 */\n start: []\n /** 引导完成(走完全部步骤) */\n complete: []\n /** 引导被用户关闭(未完成) */\n close: [currentStep: number]\n /** 步骤切换 */\n 'step-change': [stepIndex: number, step: GuideStep]\n }>()\n\n /** 持久化 key */\n const persistKey = computed(() => {\n const prefix = props.persistence?.keyPrefix ?? 'c_guide'\n return `${prefix}_completed`\n })\n\n /** 是否已完成引导 */\n const isCompleted = (): boolean => {\n if (!props.persistence?.enabled) return false\n try {\n return localStorage.getItem(persistKey.value) === 'true'\n } catch {\n return false\n }\n }\n\n /** 标记引导已完成 */\n const markCompleted = () => {\n if (!props.persistence?.enabled) return\n try {\n localStorage.setItem(persistKey.value, 'true')\n } catch {\n // localStorage 不可用时静默忽略\n }\n }\n\n /** 重置完成状态 */\n const resetCompleted = () => {\n try {\n localStorage.removeItem(persistKey.value)\n } catch {\n // 静默忽略\n }\n }\n\n /**\n * * @description: 过滤掉需要跳过的步骤\n * ! @return 有效步骤列表\n */\n const getActiveSteps = (): GuideStep[] => {\n return props.steps.filter(step => !step.skipIf?.())\n }\n\n /**\n * * @description: 构建主题相关的 CSS 变量\n * ! @return popoverClass 字符串\n */\n const buildPopoverClass = (): string => {\n const classes = [props.popoverClass]\n if (props.theme) {\n // 通过 CSS 变量注入主题\n const root = document.documentElement\n if (props.theme.popoverBgColor)\n root.style.setProperty(\n '--c-guide-popover-bg',\n props.theme.popoverBgColor\n )\n if (props.theme.popoverTextColor)\n root.style.setProperty(\n '--c-guide-popover-text',\n props.theme.popoverTextColor\n )\n if (props.theme.primaryColor)\n root.style.setProperty(\n '--c-guide-primary',\n props.theme.primaryColor\n )\n if (props.theme.overlayOpacity !== undefined)\n root.style.setProperty(\n '--c-guide-overlay-opacity',\n String(props.theme.overlayOpacity)\n )\n if (props.theme.borderRadius)\n root.style.setProperty(\n '--c-guide-radius',\n props.theme.borderRadius\n )\n }\n return classes.filter(Boolean).join(' ')\n }\n\n /**\n * * @description: 启动引导流程\n * ? @param {boolean} force 是否强制启动(忽略持久化状态)\n */\n const startGuide = (force = false) => {\n if (!force && isCompleted()) return\n\n const activeSteps = getActiveSteps()\n if (!activeSteps.length) {\n console.warn(\n '[C_Guide] 未提供引导步骤(steps),请通过 :steps prop 传入。'\n )\n return\n }\n\n let currentStepIndex = 0\n\n const driverObj = driver({\n popoverClass: buildPopoverClass(),\n animate: props.animate,\n showProgress: props.showProgress,\n allowClose: props.allowClose,\n doneBtnText: props.doneBtnText,\n nextBtnText: props.nextBtnText,\n prevBtnText: props.prevBtnText,\n steps: activeSteps.map(step => ({\n element: step.element,\n popover: step.popover,\n })),\n onHighlightStarted: (_el) => {\n const step = activeSteps[currentStepIndex]\n if (step) {\n step.onHighlightStarted?.(_el as Element | undefined, step)\n emit('step-change', currentStepIndex, step)\n }\n },\n onDeselected: (_el) => {\n const step = activeSteps[currentStepIndex]\n step?.onDeselected?.(_el as Element | undefined, step)\n currentStepIndex++\n },\n onDestroyStarted: () => {\n if (currentStepIndex >= activeSteps.length) {\n markCompleted()\n emit('complete')\n } else {\n emit('close', currentStepIndex)\n }\n driverObj.destroy()\n },\n })\n\n emit('start')\n driverObj.drive()\n }\n\n defineExpose({\n startGuide,\n resetCompleted,\n isCompleted,\n })\n</script>\n"],"mappings":"stBAwCE,IAAM,EAAQ,EAeR,EAAO,EAYP,GAAA,EAAA,EAAA,cAEG,GADQ,EAAM,aAAa,WAAa,UAC9B,YAClB,CAGK,MAA6B,CACjC,GAAI,CAAC,EAAM,aAAa,QAAS,MAAO,GACxC,GAAI,CACF,OAAO,aAAa,QAAQ,EAAW,MAAM,GAAK,YAC5C,CACN,MAAO,KAKL,MAAsB,CACrB,KAAM,aAAa,QACxB,GAAI,CACF,aAAa,QAAQ,EAAW,MAAO,OAAM,MACvC,IAMJ,MAAuB,CAC3B,GAAI,CACF,aAAa,WAAW,EAAW,MAAK,MAClC,IASJ,MACG,EAAM,MAAM,OAAO,GAAQ,CAAC,EAAK,UAAU,CAAA,CAO9C,MAAkC,CACtC,IAAM,EAAU,CAAC,EAAM,aAAY,CACnC,GAAI,EAAM,MAAO,CAEf,IAAM,EAAO,SAAS,gBAClB,EAAM,MAAM,gBACd,EAAK,MAAM,YACT,uBACA,EAAM,MAAM,eACd,CACE,EAAM,MAAM,kBACd,EAAK,MAAM,YACT,yBACA,EAAM,MAAM,iBACd,CACE,EAAM,MAAM,cACd,EAAK,MAAM,YACT,oBACA,EAAM,MAAM,aACd,CACE,EAAM,MAAM,iBAAmB,IAAA,IACjC,EAAK,MAAM,YACT,4BACA,OAAO,EAAM,MAAM,eAAc,CACnC,CACE,EAAM,MAAM,cACd,EAAK,MAAM,YACT,mBACA,EAAM,MAAM,aACd,CAEJ,OAAO,EAAQ,OAAO,QAAQ,CAAC,KAAK,IAAG,EAOnC,GAAc,EAAQ,KAAU,CACpC,GAAI,CAAC,GAAS,GAAa,CAAE,OAE7B,IAAM,EAAc,GAAe,CACnC,GAAI,CAAC,EAAY,OAAQ,CACvB,QAAQ,KACN,+CACF,CACA,OAGF,IAAI,EAAmB,EAEjB,GAAA,EAAA,EAAA,QAAmB,CACvB,aAAc,GAAmB,CACjC,QAAS,EAAM,QACf,aAAc,EAAM,aACpB,WAAY,EAAM,WAClB,YAAa,EAAM,YACnB,YAAa,EAAM,YACnB,YAAa,EAAM,YACnB,MAAO,EAAY,IAAI,IAAS,CAC9B,QAAS,EAAK,QACd,QAAS,EAAK,QACf,EAAE,CACH,mBAAqB,GAAQ,CAC3B,IAAM,EAAO,EAAY,GACrB,IACF,EAAK,qBAAqB,EAA4B,EAAI,CAC1D,EAAK,cAAe,EAAkB,EAAI,GAG9C,aAAe,GAAQ,CACrB,IAAM,EAAO,EAAY,GACzB,GAAM,eAAe,EAA4B,EAAI,CACrD,KAEF,qBAAwB,CAClB,GAAoB,EAAY,QAClC,GAAc,CACd,EAAK,WAAU,EAEf,EAAK,QAAS,EAAgB,CAEhC,EAAU,SAAQ,EAErB,CAAA,CAED,EAAK,QAAO,CACZ,EAAU,OAAM,SAGlB,EAAa,CACX,aACA,iBACA,cACD,CAAA,QCpMO,EAAM,cAAA,EAAA,EAAA,YAAA,EAAA,EAAA,EAAA,cAAA,EAAA,EAAA,OAgBH,EAAA,SAAA,CAAA,OAfT,UAAU,SACV,QAAQ,UAEG,SAAA,EAAA,EAAA,aASC,EAAA,EAAA,EAAA,cAAA,EAAA,EAAA,OAAA,EAAA,QAAA,CAAA,CAPR,KAAA,GACC,QAAO,8BAKN,EAAA,EAAA,EAAA,aAAA,EAAA,EAAA,CAFC,KAAM,EAAM,YACZ,KAAM,yDAI0B,EAAA,EAAA,EAAA,oBAAA,OAAA,MAAA,EAAA,EAAA,iBAA9B,EAAM,eAAc,CAAA,EAAA,CAAA,CAAA"}
|
package/dist/C_Guide2.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_Guide2.js","names":[],"sources":["../src/components/C_Guide/index.vue","../src/components/C_Guide/index.vue"],"sourcesContent":["<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-01\n * @LastEditors: ChenYu ycyplus@gmail.com\n * @LastEditTime: 2026-03-06\n * @Description: 用户引导组件(基于 driver.js)— 增强版\n * 支持:步骤分组 / 键盘导航 / 步骤回调 / 主题自定义 / 完成持久化\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <NTooltip\n v-if=\"props.showTrigger\"\n placement=\"bottom\"\n trigger=\"hover\"\n >\n <template #trigger>\n <NButton\n text\n @click=\"startGuide\"\n >\n <C_Icon\n :name=\"props.triggerIcon\"\n :size=\"18\"\n />\n </NButton>\n </template>\n <span>{{ props.triggerTooltip }}</span>\n </NTooltip>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { NTooltip, NButton } from 'naive-ui'\n import C_Icon from '../C_Icon/index.vue'\n import { driver } from 'driver.js'\n import 'driver.js/dist/driver.css'\n import type { GuideProps, GuideStep } from './types'\n\n defineOptions({ name: 'C_Guide' })\n\n const props = withDefaults(defineProps<GuideProps>(), {\n steps: () => [],\n doneBtnText: '完成',\n nextBtnText: '下一步',\n prevBtnText: '上一步',\n showProgress: true,\n keyboard: true,\n animate: true,\n allowClose: true,\n popoverClass: 'driverjs-theme',\n showTrigger: true,\n triggerTooltip: '功能引导',\n triggerIcon: 'mdi:sign-routes',\n })\n\n const emit = defineEmits<{\n /** 引导开始 */\n start: []\n /** 引导完成(走完全部步骤) */\n complete: []\n /** 引导被用户关闭(未完成) */\n close: [currentStep: number]\n /** 步骤切换 */\n 'step-change': [stepIndex: number, step: GuideStep]\n }>()\n\n /** 持久化 key */\n const persistKey = computed(() => {\n const prefix = props.persistence?.keyPrefix ?? 'c_guide'\n return `${prefix}_completed`\n })\n\n /** 是否已完成引导 */\n const isCompleted = (): boolean => {\n if (!props.persistence?.enabled) return false\n try {\n return localStorage.getItem(persistKey.value) === 'true'\n } catch {\n return false\n }\n }\n\n /** 标记引导已完成 */\n const markCompleted = () => {\n if (!props.persistence?.enabled) return\n try {\n localStorage.setItem(persistKey.value, 'true')\n } catch {\n // localStorage 不可用时静默忽略\n }\n }\n\n /** 重置完成状态 */\n const resetCompleted = () => {\n try {\n localStorage.removeItem(persistKey.value)\n } catch {\n // 静默忽略\n }\n }\n\n /**\n * * @description: 过滤掉需要跳过的步骤\n * ! @return 有效步骤列表\n */\n const getActiveSteps = (): GuideStep[] => {\n return props.steps.filter(step => !step.skipIf?.())\n }\n\n /**\n * * @description: 构建主题相关的 CSS 变量\n * ! @return popoverClass 字符串\n */\n const buildPopoverClass = (): string => {\n const classes = [props.popoverClass]\n if (props.theme) {\n // 通过 CSS 变量注入主题\n const root = document.documentElement\n if (props.theme.popoverBgColor)\n root.style.setProperty(\n '--c-guide-popover-bg',\n props.theme.popoverBgColor\n )\n if (props.theme.popoverTextColor)\n root.style.setProperty(\n '--c-guide-popover-text',\n props.theme.popoverTextColor\n )\n if (props.theme.primaryColor)\n root.style.setProperty(\n '--c-guide-primary',\n props.theme.primaryColor\n )\n if (props.theme.overlayOpacity !== undefined)\n root.style.setProperty(\n '--c-guide-overlay-opacity',\n String(props.theme.overlayOpacity)\n )\n if (props.theme.borderRadius)\n root.style.setProperty(\n '--c-guide-radius',\n props.theme.borderRadius\n )\n }\n return classes.filter(Boolean).join(' ')\n }\n\n /**\n * * @description: 启动引导流程\n * ? @param {boolean} force 是否强制启动(忽略持久化状态)\n */\n const startGuide = (force = false) => {\n if (!force && isCompleted()) return\n\n const activeSteps = getActiveSteps()\n if (!activeSteps.length) {\n console.warn(\n '[C_Guide] 未提供引导步骤(steps),请通过 :steps prop 传入。'\n )\n return\n }\n\n let currentStepIndex = 0\n\n const driverObj = driver({\n popoverClass: buildPopoverClass(),\n animate: props.animate,\n showProgress: props.showProgress,\n allowClose: props.allowClose,\n doneBtnText: props.doneBtnText,\n nextBtnText: props.nextBtnText,\n prevBtnText: props.prevBtnText,\n steps: activeSteps.map(step => ({\n element: step.element,\n popover: step.popover,\n })),\n onHighlightStarted: (_el) => {\n const step = activeSteps[currentStepIndex]\n if (step) {\n step.onHighlightStarted?.(_el as Element | undefined, step)\n emit('step-change', currentStepIndex, step)\n }\n },\n onDeselected: (_el) => {\n const step = activeSteps[currentStepIndex]\n step?.onDeselected?.(_el as Element | undefined, step)\n currentStepIndex++\n },\n onDestroyStarted: () => {\n if (currentStepIndex >= activeSteps.length) {\n markCompleted()\n emit('complete')\n } else {\n emit('close', currentStepIndex)\n }\n driverObj.destroy()\n },\n })\n\n emit('start')\n driverObj.drive()\n }\n\n defineExpose({\n startGuide,\n resetCompleted,\n isCompleted,\n })\n</script>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-01\n * @LastEditors: ChenYu ycyplus@gmail.com\n * @LastEditTime: 2026-03-06\n * @Description: 用户引导组件(基于 driver.js)— 增强版\n * 支持:步骤分组 / 键盘导航 / 步骤回调 / 主题自定义 / 完成持久化\n * Copyright (c) 2026 by CHENY, All Rights Reserved 😎.\n-->\n<template>\n <NTooltip\n v-if=\"props.showTrigger\"\n placement=\"bottom\"\n trigger=\"hover\"\n >\n <template #trigger>\n <NButton\n text\n @click=\"startGuide\"\n >\n <C_Icon\n :name=\"props.triggerIcon\"\n :size=\"18\"\n />\n </NButton>\n </template>\n <span>{{ props.triggerTooltip }}</span>\n </NTooltip>\n</template>\n\n<script setup lang=\"ts\">\n import { computed } from 'vue'\n import { NTooltip, NButton } from 'naive-ui'\n import C_Icon from '../C_Icon/index.vue'\n import { driver } from 'driver.js'\n import 'driver.js/dist/driver.css'\n import type { GuideProps, GuideStep } from './types'\n\n defineOptions({ name: 'C_Guide' })\n\n const props = withDefaults(defineProps<GuideProps>(), {\n steps: () => [],\n doneBtnText: '完成',\n nextBtnText: '下一步',\n prevBtnText: '上一步',\n showProgress: true,\n keyboard: true,\n animate: true,\n allowClose: true,\n popoverClass: 'driverjs-theme',\n showTrigger: true,\n triggerTooltip: '功能引导',\n triggerIcon: 'mdi:sign-routes',\n })\n\n const emit = defineEmits<{\n /** 引导开始 */\n start: []\n /** 引导完成(走完全部步骤) */\n complete: []\n /** 引导被用户关闭(未完成) */\n close: [currentStep: number]\n /** 步骤切换 */\n 'step-change': [stepIndex: number, step: GuideStep]\n }>()\n\n /** 持久化 key */\n const persistKey = computed(() => {\n const prefix = props.persistence?.keyPrefix ?? 'c_guide'\n return `${prefix}_completed`\n })\n\n /** 是否已完成引导 */\n const isCompleted = (): boolean => {\n if (!props.persistence?.enabled) return false\n try {\n return localStorage.getItem(persistKey.value) === 'true'\n } catch {\n return false\n }\n }\n\n /** 标记引导已完成 */\n const markCompleted = () => {\n if (!props.persistence?.enabled) return\n try {\n localStorage.setItem(persistKey.value, 'true')\n } catch {\n // localStorage 不可用时静默忽略\n }\n }\n\n /** 重置完成状态 */\n const resetCompleted = () => {\n try {\n localStorage.removeItem(persistKey.value)\n } catch {\n // 静默忽略\n }\n }\n\n /**\n * * @description: 过滤掉需要跳过的步骤\n * ! @return 有效步骤列表\n */\n const getActiveSteps = (): GuideStep[] => {\n return props.steps.filter(step => !step.skipIf?.())\n }\n\n /**\n * * @description: 构建主题相关的 CSS 变量\n * ! @return popoverClass 字符串\n */\n const buildPopoverClass = (): string => {\n const classes = [props.popoverClass]\n if (props.theme) {\n // 通过 CSS 变量注入主题\n const root = document.documentElement\n if (props.theme.popoverBgColor)\n root.style.setProperty(\n '--c-guide-popover-bg',\n props.theme.popoverBgColor\n )\n if (props.theme.popoverTextColor)\n root.style.setProperty(\n '--c-guide-popover-text',\n props.theme.popoverTextColor\n )\n if (props.theme.primaryColor)\n root.style.setProperty(\n '--c-guide-primary',\n props.theme.primaryColor\n )\n if (props.theme.overlayOpacity !== undefined)\n root.style.setProperty(\n '--c-guide-overlay-opacity',\n String(props.theme.overlayOpacity)\n )\n if (props.theme.borderRadius)\n root.style.setProperty(\n '--c-guide-radius',\n props.theme.borderRadius\n )\n }\n return classes.filter(Boolean).join(' ')\n }\n\n /**\n * * @description: 启动引导流程\n * ? @param {boolean} force 是否强制启动(忽略持久化状态)\n */\n const startGuide = (force = false) => {\n if (!force && isCompleted()) return\n\n const activeSteps = getActiveSteps()\n if (!activeSteps.length) {\n console.warn(\n '[C_Guide] 未提供引导步骤(steps),请通过 :steps prop 传入。'\n )\n return\n }\n\n let currentStepIndex = 0\n\n const driverObj = driver({\n popoverClass: buildPopoverClass(),\n animate: props.animate,\n showProgress: props.showProgress,\n allowClose: props.allowClose,\n doneBtnText: props.doneBtnText,\n nextBtnText: props.nextBtnText,\n prevBtnText: props.prevBtnText,\n steps: activeSteps.map(step => ({\n element: step.element,\n popover: step.popover,\n })),\n onHighlightStarted: (_el) => {\n const step = activeSteps[currentStepIndex]\n if (step) {\n step.onHighlightStarted?.(_el as Element | undefined, step)\n emit('step-change', currentStepIndex, step)\n }\n },\n onDeselected: (_el) => {\n const step = activeSteps[currentStepIndex]\n step?.onDeselected?.(_el as Element | undefined, step)\n currentStepIndex++\n },\n onDestroyStarted: () => {\n if (currentStepIndex >= activeSteps.length) {\n markCompleted()\n emit('complete')\n } else {\n emit('close', currentStepIndex)\n }\n driverObj.destroy()\n },\n })\n\n emit('start')\n driverObj.drive()\n }\n\n defineExpose({\n startGuide,\n resetCompleted,\n isCompleted,\n })\n</script>\n"],"mappings":"+3BAwCE,IAAM,EAAQ,EAeR,EAAO,EAYP,EAAa,MAEV,GADQ,EAAM,aAAa,WAAa,UAC9B,YAClB,CAGK,MAA6B,CACjC,GAAI,CAAC,EAAM,aAAa,QAAS,MAAO,GACxC,GAAI,CACF,OAAO,aAAa,QAAQ,EAAW,MAAM,GAAK,YAC5C,CACN,MAAO,KAKL,MAAsB,CACrB,KAAM,aAAa,QACxB,GAAI,CACF,aAAa,QAAQ,EAAW,MAAO,OAAM,MACvC,IAMJ,MAAuB,CAC3B,GAAI,CACF,aAAa,WAAW,EAAW,MAAK,MAClC,IASJ,MACG,EAAM,MAAM,OAAO,GAAQ,CAAC,EAAK,UAAU,CAAA,CAO9C,MAAkC,CACtC,IAAM,EAAU,CAAC,EAAM,aAAY,CACnC,GAAI,EAAM,MAAO,CAEf,IAAM,EAAO,SAAS,gBAClB,EAAM,MAAM,gBACd,EAAK,MAAM,YACT,uBACA,EAAM,MAAM,eACd,CACE,EAAM,MAAM,kBACd,EAAK,MAAM,YACT,yBACA,EAAM,MAAM,iBACd,CACE,EAAM,MAAM,cACd,EAAK,MAAM,YACT,oBACA,EAAM,MAAM,aACd,CACE,EAAM,MAAM,iBAAmB,IAAA,IACjC,EAAK,MAAM,YACT,4BACA,OAAO,EAAM,MAAM,eAAc,CACnC,CACE,EAAM,MAAM,cACd,EAAK,MAAM,YACT,mBACA,EAAM,MAAM,aACd,CAEJ,OAAO,EAAQ,OAAO,QAAQ,CAAC,KAAK,IAAG,EAOnC,GAAc,EAAQ,KAAU,CACpC,GAAI,CAAC,GAAS,GAAa,CAAE,OAE7B,IAAM,EAAc,GAAe,CACnC,GAAI,CAAC,EAAY,OAAQ,CACvB,QAAQ,KACN,+CACF,CACA,OAGF,IAAI,EAAmB,EAEjB,EAAY,EAAO,CACvB,aAAc,GAAmB,CACjC,QAAS,EAAM,QACf,aAAc,EAAM,aACpB,WAAY,EAAM,WAClB,YAAa,EAAM,YACnB,YAAa,EAAM,YACnB,YAAa,EAAM,YACnB,MAAO,EAAY,IAAI,IAAS,CAC9B,QAAS,EAAK,QACd,QAAS,EAAK,QACf,EAAE,CACH,mBAAqB,GAAQ,CAC3B,IAAM,EAAO,EAAY,GACrB,IACF,EAAK,qBAAqB,EAA4B,EAAI,CAC1D,EAAK,cAAe,EAAkB,EAAI,GAG9C,aAAe,GAAQ,CACrB,IAAM,EAAO,EAAY,GACzB,GAAM,eAAe,EAA4B,EAAI,CACrD,KAEF,qBAAwB,CAClB,GAAoB,EAAY,QAClC,GAAc,CACd,EAAK,WAAU,EAEf,EAAK,QAAS,EAAgB,CAEhC,EAAU,SAAQ,EAErB,CAAA,CAED,EAAK,QAAO,CACZ,EAAU,OAAM,SAGlB,EAAa,CACX,aACA,iBACA,cACD,CAAA,QCpMO,EAAM,aAAA,GAAA,CADd,EAiBW,EAAA,EAAA,CAAA,OAfT,UAAU,SACV,QAAQ,UAEG,QAAO,MASN,CARV,EAQU,EAAA,EAAA,CAAA,CAPR,KAAA,GACC,QAAO,kBAKN,CAHF,EAGE,EAAA,CAFC,KAAM,EAAM,YACZ,KAAM,6CAI0B,CAAvC,EAAuC,OAAA,KAAA,EAA9B,EAAM,eAAc,CAAA,EAAA,CAAA,CAAA"}
|
package/dist/C_Icon.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_Icon.js","names":["type","clickable","loading","title","ariaLabel","name","size","color","componentProps","svgPath","viewBox","alt","fallbackIcon"],"sources":["../src/hooks/useImage/index.ts","../src/components/C_Icon/index.vue","../src/components/C_Icon/index.vue"],"sourcesContent":["/**\n * @description: 图片路径解析函数(组件库版本)\n * 在组件库中,图片路径由宿主应用管理。\n * 如果传入的是完整 URL(http/https/data:)则直接返回;\n * 否则可通过 imageResolver 配置自定义解析逻辑。\n * @param {string} imagePath 图片路径或完整 URL\n * @param {Function} resolver 可选的自定义解析器\n * @return {Promise<string>} 处理后图片路径\n */\nexport const useImage = async (\n imagePath: string,\n resolver?: (path: string) => string | Promise<string>,\n): Promise<string> => {\n if (!imagePath) return \"\";\n\n // 完整 URL 直接返回\n if (\n imagePath.startsWith(\"http://\") ||\n imagePath.startsWith(\"https://\") ||\n imagePath.startsWith(\"data:\") ||\n imagePath.startsWith(\"blob:\") ||\n imagePath.startsWith(\"/\")\n ) {\n return imagePath;\n }\n\n // 如果提供了自定义解析器,使用自定义解析器\n if (resolver) {\n try {\n return await resolver(imagePath);\n } catch (e) {\n console.error(`[useImage] 自定义解析器处理失败: ${imagePath}`, e);\n return \"\";\n }\n }\n\n // 默认:直接返回路径(宿主应用应保证路径可访问)\n return imagePath;\n};\n\n/**\n * @description: 同步版本的图片路径处理函数\n * @param {string} imagePath 图片路径或完整 URL\n * @return {string} 图片URL\n */\nexport const useImageSync = (imagePath: string): string => {\n if (!imagePath) return \"\";\n\n // 完整 URL 直接返回\n if (\n imagePath.startsWith(\"http://\") ||\n imagePath.startsWith(\"https://\") ||\n imagePath.startsWith(\"data:\") ||\n imagePath.startsWith(\"blob:\") ||\n imagePath.startsWith(\"/\")\n ) {\n return imagePath;\n }\n\n return imagePath;\n};\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-14\r\n * @Description: 支持多种图标使用方式,默认使用Iconify图标,统一错误处理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"iconRef\"\r\n class=\"c-icon\"\r\n :class=\"[\r\n `c-icon--${type}`,\r\n {\r\n 'c-icon--clickable': clickable,\r\n 'c-icon--loading': loading,\r\n 'c-icon--error': hasError,\r\n },\r\n ]\"\r\n :style=\"rootStyle\"\r\n :title=\"title || undefined\"\r\n :aria-label=\"ariaLabel || iconDisplayName\"\r\n :role=\"clickable ? 'button' : 'img'\"\r\n :tabindex=\"clickable ? 0 : -1\"\r\n @click=\"handleClick\"\r\n @keydown=\"handleKeydown\"\r\n >\r\n <!-- 方式1:Iconify图标(默认方式) -->\r\n <Icon\r\n v-if=\"\r\n (type === 'iconify' || !type) && !hasError && typeof name === 'string'\r\n \"\r\n :icon=\"name\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('iconify', 'Iconify图标加载失败')\"\r\n />\r\n\r\n <!-- 方式2:UnoCSS图标 -->\r\n <div\r\n v-else-if=\"type === 'unocss' && !hasError && typeof name === 'string'\"\r\n :class=\"[name, iconClasses]\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('unocss', 'UnoCSS图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式3:组件挂载方式 -->\r\n <component\r\n v-else-if=\"type === 'component' && resolvedComponent && !hasError\"\r\n :is=\"resolvedComponent\"\r\n :style=\"iconStyle\"\r\n v-bind=\"componentProps\"\r\n @error=\"() => handleError('component', '组件图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式4:SVG路径方式 -->\r\n <svg\r\n v-else-if=\"type === 'svg' && svgPath && !hasError\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :viewBox=\"viewBox\"\r\n :style=\"iconStyle\"\r\n :fill=\"color\"\r\n @error=\"() => handleError('svg', 'SVG图标渲染失败')\"\r\n >\r\n <path\r\n :d=\"svgPath\"\r\n :fill=\"color\"\r\n />\r\n </svg>\r\n\r\n <!-- 方式5:图片方式 - 修复异步加载 -->\r\n <img\r\n v-else-if=\"type === 'image' && imageSrc && !hasError && !imageLoading\"\r\n :src=\"imageSrc\"\r\n :alt=\"alt || iconDisplayName\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n style=\"display: inline-block; vertical-align: middle\"\r\n @error=\"() => handleError('image', '图片图标加载失败')\"\r\n @load=\"handleImageLoad\"\r\n />\r\n\r\n <!-- 图片加载状态 -->\r\n <div\r\n v-if=\"(loading || imageLoading) && !hasError\"\r\n class=\"c-icon__loading\"\r\n :style=\"iconStyle\"\r\n >\r\n <div class=\"c-icon__spinner\" />\r\n </div>\r\n\r\n <!-- 统一错误状态显示 -->\r\n <Icon\r\n v-else-if=\"hasError && fallbackIcon\"\r\n :icon=\"fallbackIcon\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n />\r\n <div\r\n v-else-if=\"hasError\"\r\n class=\"c-icon__error\"\r\n :style=\"iconStyle\"\r\n :title=\"`图标解析失败: ${iconDisplayName} (${errorMessage})`\"\r\n >\r\n ⚠️\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\n import { Icon } from '@iconify/vue'\r\n import { useImage } from '../../hooks/useImage'\r\n import { computed, nextTick, onMounted, readonly, ref, watch } from 'vue'\r\n import type { IconProps } from './types'\r\n\r\n interface LegacyIconProps extends IconProps {\r\n /** 图标名称/路径/组件实例 */\r\n name: string | any\r\n /** 图标类型 */\r\n type?: 'iconify' | 'unocss' | 'component' | 'svg' | 'image'\r\n /** 图标颜色 */\r\n color?: string\r\n /** 图标大小(px) */\r\n size?: number | string\r\n /** SVG路径数据(仅type=svg时使用) */\r\n svgPath?: string\r\n /** SVG viewBox(仅type=svg时使用) */\r\n viewBox?: string\r\n /** 图片alt属性(仅type=image时使用) */\r\n alt?: string\r\n /** 是否可点击 */\r\n clickable?: boolean\r\n /** 加载状态 */\r\n loading?: boolean\r\n /** 错误时的回退图标 */\r\n fallbackIcon?: string\r\n /** 工具提示 */\r\n title?: string\r\n /** 无障碍标签 */\r\n ariaLabel?: string\r\n /** 自定义样式类 */\r\n customClass?: string\r\n /** 旋转角度 */\r\n rotate?: number\r\n /** 是否翻转 */\r\n flip?: 'horizontal' | 'vertical' | 'both'\r\n /** 传递给组件的额外属性(仅type=component时使用) */\r\n componentProps?: Record<string, any>\r\n }\r\n\r\n const props = withDefaults(defineProps<LegacyIconProps>(), {\r\n name: undefined,\r\n type: 'iconify',\r\n color: 'currentColor',\r\n size: 18,\r\n svgPath: '',\r\n viewBox: '0 0 24 24',\r\n alt: '',\r\n clickable: false,\r\n loading: false,\r\n fallbackIcon: '',\r\n title: '',\r\n ariaLabel: '',\r\n customClass: '',\r\n rotate: 0,\r\n flip: undefined,\r\n componentProps: () => ({}),\r\n })\r\n\r\n const emit = defineEmits<{\r\n click: [event: MouseEvent]\r\n error: [type: string, error?: any]\r\n load: []\r\n }>()\r\n\r\n const iconRef = ref<HTMLElement>()\r\n const hasError = ref(false)\r\n const errorMessage = ref('')\r\n const imageSrc = ref<string>('') // 存储异步加载的图片URL\r\n const imageLoading = ref(false) // 图片加载状态\r\n\r\n // 计算根元素样式\r\n const rootStyle = computed(() => ({\r\n fontSize: typeof props.size === 'number' ? `${props.size}px` : props.size,\r\n color: props.color,\r\n cursor: props.clickable ? 'pointer' : 'inherit',\r\n }))\r\n\r\n // 计算图标样式\r\n const iconStyle = computed(() => {\r\n const style: Record<string, any> = {\r\n display: 'inline-flex',\r\n justifyContent: 'center',\r\n alignItems: 'center',\r\n width: typeof props.size === 'number' ? `${props.size}px` : props.size,\r\n height: typeof props.size === 'number' ? `${props.size}px` : props.size,\r\n }\r\n\r\n // 旋转\r\n if (props.rotate) {\r\n style.transform = `rotate(${props.rotate}deg)`\r\n }\r\n\r\n // 翻转\r\n if (props.flip) {\r\n const scaleX =\r\n props.flip === 'horizontal' || props.flip === 'both' ? -1 : 1\r\n const scaleY = props.flip === 'vertical' || props.flip === 'both' ? -1 : 1\r\n style.transform = `${\r\n style.transform || ''\r\n } scaleX(${scaleX}) scaleY(${scaleY})`.trim()\r\n }\r\n\r\n return style\r\n })\r\n\r\n // 获取图标名称用于显示\r\n const iconDisplayName = computed(() => {\r\n if (typeof props.name === 'string') {\r\n return props.name\r\n }\r\n if (props.name && props.name.__name) {\r\n return props.name.__name\r\n }\r\n if (props.name && props.name.name) {\r\n return props.name.name\r\n }\r\n return 'Component Icon'\r\n })\r\n\r\n // 图标类名\r\n const iconClasses = computed(() => [\r\n props.customClass,\r\n {\r\n 'c-icon__rotatable': props.rotate !== 0,\r\n 'c-icon__flippable': !!props.flip,\r\n },\r\n ])\r\n\r\n // 解析组件名称(纯计算属性,无副作用)\r\n const resolvedComponent = computed(() => {\r\n if (props.type !== 'component' || !props.name) return null\r\n return props.name\r\n })\r\n\r\n // 统一错误处理函数\r\n const handleError = (type: string, message: string, originalError?: any) => {\r\n console.warn(`[C_Icon] ${message}:`, iconDisplayName.value, originalError)\r\n errorMessage.value = message\r\n imageLoading.value = false // 错误时停止加载状态\r\n emit('error', type, originalError)\r\n\r\n // 如果有回退图标,尝试切换为 fallback Iconify 图标\r\n if (props.fallbackIcon && !hasError.value) {\r\n hasError.value = false\r\n // 将 name 重写为 fallbackIcon 并重置状态\r\n // 因为 props 不可变,通过模板中的 fallback 分支渲染\r\n return\r\n }\r\n\r\n hasError.value = true\r\n }\r\n\r\n // 处理点击事件\r\n const handleClick = (event: MouseEvent) => {\r\n if (!props.clickable || props.loading) return\r\n emit('click', event)\r\n }\r\n\r\n // 处理键盘事件\r\n const handleKeydown = (event: KeyboardEvent) => {\r\n if (!props.clickable || props.loading) return\r\n if (event.key === 'Enter' || event.key === ' ') {\r\n event.preventDefault()\r\n emit('click', event as any)\r\n }\r\n }\r\n\r\n // 处理图片加载成功\r\n const handleImageLoad = () => {\r\n imageLoading.value = false\r\n emit('load')\r\n }\r\n\r\n // 异步加载图片 - 修复关键函数\r\n const loadImageSrc = async () => {\r\n if (props.type !== 'image' || typeof props.name !== 'string') {\r\n imageSrc.value = ''\r\n return\r\n }\r\n\r\n // 如果是完整URL,直接设置\r\n if (\r\n props.name.startsWith('http://') ||\r\n props.name.startsWith('https://') ||\r\n props.name.startsWith('//') ||\r\n props.name.startsWith('/')\r\n ) {\r\n imageSrc.value = props.name\r\n return\r\n }\r\n\r\n // 其他情况都当作本地图片处理\r\n try {\r\n imageLoading.value = true\r\n const imageUrl = await useImage(props.name)\r\n if (imageUrl) {\r\n imageSrc.value = imageUrl\r\n } else {\r\n handleError('image', '图片路径解析失败')\r\n }\r\n } catch (error) {\r\n handleError('image', '图片加载失败', error)\r\n } finally {\r\n imageLoading.value = false\r\n }\r\n }\r\n\r\n // 验证规则映射\r\n const validationRules = {\r\n iconify: (): string | null => {\r\n if (!props.name.includes(':')) {\r\n console.warn(\r\n '[C_Icon] Iconify图标名称格式应为 \"prefix:name\",如 \"mdi:home\"'\r\n )\r\n }\r\n return null\r\n },\r\n unocss: (): string | null => {\r\n if (typeof props.name !== 'string' || !props.name.startsWith('i-')) {\r\n console.warn('[C_Icon] UnoCSS图标名称应以 \"i-\" 开头')\r\n }\r\n return null\r\n },\r\n component: (): string | null =>\r\n !resolvedComponent.value ? '无法解析组件' : null,\r\n svg: (): string | null => (!props.svgPath ? 'SVG路径不能为空' : null),\r\n image: (): string | null => {\r\n // 对于image类型,异步验证在loadImageSrc中处理\r\n return null\r\n },\r\n }\r\n\r\n // 验证配置\r\n const validateProps = () => {\r\n hasError.value = false\r\n errorMessage.value = ''\r\n\r\n // 基础验证\r\n if (!props.name) {\r\n return handleError('validation', '图标名称不能为空')\r\n }\r\n\r\n // 类型验证\r\n const validator = validationRules[props.type]\r\n if (!validator) {\r\n return handleError('type', '不支持的图标类型')\r\n }\r\n\r\n const errorMsg = validator()\r\n if (errorMsg) {\r\n handleError(props.type, errorMsg)\r\n }\r\n\r\n // 如果是图片类型,异步加载图片\r\n if (props.type === 'image') {\r\n loadImageSrc()\r\n }\r\n }\r\n\r\n // 监听关键属性变化\r\n watch(\r\n () => [props.name, props.type, props.svgPath],\r\n () => {\r\n nextTick(validateProps)\r\n },\r\n { immediate: true }\r\n )\r\n\r\n // 组件挂载后:仅对非 image 类型触发 load 事件(watch immediate 已完成验证)\r\n onMounted(() => {\r\n if (!hasError.value && props.type !== 'image') {\r\n emit('load')\r\n }\r\n })\r\n\r\n // 暴露方法给父组件\r\n defineExpose({\r\n /** 重新验证配置 */\r\n validate: validateProps,\r\n /** 是否有错误 */\r\n hasError: readonly(hasError),\r\n /** 错误信息 */\r\n errorMessage: readonly(errorMessage),\r\n /** DOM引用 */\r\n el: iconRef,\r\n })\r\n</script>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-14\r\n * @Description: 支持多种图标使用方式,默认使用Iconify图标,统一错误处理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"iconRef\"\r\n class=\"c-icon\"\r\n :class=\"[\r\n `c-icon--${type}`,\r\n {\r\n 'c-icon--clickable': clickable,\r\n 'c-icon--loading': loading,\r\n 'c-icon--error': hasError,\r\n },\r\n ]\"\r\n :style=\"rootStyle\"\r\n :title=\"title || undefined\"\r\n :aria-label=\"ariaLabel || iconDisplayName\"\r\n :role=\"clickable ? 'button' : 'img'\"\r\n :tabindex=\"clickable ? 0 : -1\"\r\n @click=\"handleClick\"\r\n @keydown=\"handleKeydown\"\r\n >\r\n <!-- 方式1:Iconify图标(默认方式) -->\r\n <Icon\r\n v-if=\"\r\n (type === 'iconify' || !type) && !hasError && typeof name === 'string'\r\n \"\r\n :icon=\"name\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('iconify', 'Iconify图标加载失败')\"\r\n />\r\n\r\n <!-- 方式2:UnoCSS图标 -->\r\n <div\r\n v-else-if=\"type === 'unocss' && !hasError && typeof name === 'string'\"\r\n :class=\"[name, iconClasses]\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('unocss', 'UnoCSS图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式3:组件挂载方式 -->\r\n <component\r\n v-else-if=\"type === 'component' && resolvedComponent && !hasError\"\r\n :is=\"resolvedComponent\"\r\n :style=\"iconStyle\"\r\n v-bind=\"componentProps\"\r\n @error=\"() => handleError('component', '组件图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式4:SVG路径方式 -->\r\n <svg\r\n v-else-if=\"type === 'svg' && svgPath && !hasError\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :viewBox=\"viewBox\"\r\n :style=\"iconStyle\"\r\n :fill=\"color\"\r\n @error=\"() => handleError('svg', 'SVG图标渲染失败')\"\r\n >\r\n <path\r\n :d=\"svgPath\"\r\n :fill=\"color\"\r\n />\r\n </svg>\r\n\r\n <!-- 方式5:图片方式 - 修复异步加载 -->\r\n <img\r\n v-else-if=\"type === 'image' && imageSrc && !hasError && !imageLoading\"\r\n :src=\"imageSrc\"\r\n :alt=\"alt || iconDisplayName\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n style=\"display: inline-block; vertical-align: middle\"\r\n @error=\"() => handleError('image', '图片图标加载失败')\"\r\n @load=\"handleImageLoad\"\r\n />\r\n\r\n <!-- 图片加载状态 -->\r\n <div\r\n v-if=\"(loading || imageLoading) && !hasError\"\r\n class=\"c-icon__loading\"\r\n :style=\"iconStyle\"\r\n >\r\n <div class=\"c-icon__spinner\" />\r\n </div>\r\n\r\n <!-- 统一错误状态显示 -->\r\n <Icon\r\n v-else-if=\"hasError && fallbackIcon\"\r\n :icon=\"fallbackIcon\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n />\r\n <div\r\n v-else-if=\"hasError\"\r\n class=\"c-icon__error\"\r\n :style=\"iconStyle\"\r\n :title=\"`图标解析失败: ${iconDisplayName} (${errorMessage})`\"\r\n >\r\n ⚠️\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\n import { Icon } from '@iconify/vue'\r\n import { useImage } from '../../hooks/useImage'\r\n import { computed, nextTick, onMounted, readonly, ref, watch } from 'vue'\r\n import type { IconProps } from './types'\r\n\r\n interface LegacyIconProps extends IconProps {\r\n /** 图标名称/路径/组件实例 */\r\n name: string | any\r\n /** 图标类型 */\r\n type?: 'iconify' | 'unocss' | 'component' | 'svg' | 'image'\r\n /** 图标颜色 */\r\n color?: string\r\n /** 图标大小(px) */\r\n size?: number | string\r\n /** SVG路径数据(仅type=svg时使用) */\r\n svgPath?: string\r\n /** SVG viewBox(仅type=svg时使用) */\r\n viewBox?: string\r\n /** 图片alt属性(仅type=image时使用) */\r\n alt?: string\r\n /** 是否可点击 */\r\n clickable?: boolean\r\n /** 加载状态 */\r\n loading?: boolean\r\n /** 错误时的回退图标 */\r\n fallbackIcon?: string\r\n /** 工具提示 */\r\n title?: string\r\n /** 无障碍标签 */\r\n ariaLabel?: string\r\n /** 自定义样式类 */\r\n customClass?: string\r\n /** 旋转角度 */\r\n rotate?: number\r\n /** 是否翻转 */\r\n flip?: 'horizontal' | 'vertical' | 'both'\r\n /** 传递给组件的额外属性(仅type=component时使用) */\r\n componentProps?: Record<string, any>\r\n }\r\n\r\n const props = withDefaults(defineProps<LegacyIconProps>(), {\r\n name: undefined,\r\n type: 'iconify',\r\n color: 'currentColor',\r\n size: 18,\r\n svgPath: '',\r\n viewBox: '0 0 24 24',\r\n alt: '',\r\n clickable: false,\r\n loading: false,\r\n fallbackIcon: '',\r\n title: '',\r\n ariaLabel: '',\r\n customClass: '',\r\n rotate: 0,\r\n flip: undefined,\r\n componentProps: () => ({}),\r\n })\r\n\r\n const emit = defineEmits<{\r\n click: [event: MouseEvent]\r\n error: [type: string, error?: any]\r\n load: []\r\n }>()\r\n\r\n const iconRef = ref<HTMLElement>()\r\n const hasError = ref(false)\r\n const errorMessage = ref('')\r\n const imageSrc = ref<string>('') // 存储异步加载的图片URL\r\n const imageLoading = ref(false) // 图片加载状态\r\n\r\n // 计算根元素样式\r\n const rootStyle = computed(() => ({\r\n fontSize: typeof props.size === 'number' ? `${props.size}px` : props.size,\r\n color: props.color,\r\n cursor: props.clickable ? 'pointer' : 'inherit',\r\n }))\r\n\r\n // 计算图标样式\r\n const iconStyle = computed(() => {\r\n const style: Record<string, any> = {\r\n display: 'inline-flex',\r\n justifyContent: 'center',\r\n alignItems: 'center',\r\n width: typeof props.size === 'number' ? `${props.size}px` : props.size,\r\n height: typeof props.size === 'number' ? `${props.size}px` : props.size,\r\n }\r\n\r\n // 旋转\r\n if (props.rotate) {\r\n style.transform = `rotate(${props.rotate}deg)`\r\n }\r\n\r\n // 翻转\r\n if (props.flip) {\r\n const scaleX =\r\n props.flip === 'horizontal' || props.flip === 'both' ? -1 : 1\r\n const scaleY = props.flip === 'vertical' || props.flip === 'both' ? -1 : 1\r\n style.transform = `${\r\n style.transform || ''\r\n } scaleX(${scaleX}) scaleY(${scaleY})`.trim()\r\n }\r\n\r\n return style\r\n })\r\n\r\n // 获取图标名称用于显示\r\n const iconDisplayName = computed(() => {\r\n if (typeof props.name === 'string') {\r\n return props.name\r\n }\r\n if (props.name && props.name.__name) {\r\n return props.name.__name\r\n }\r\n if (props.name && props.name.name) {\r\n return props.name.name\r\n }\r\n return 'Component Icon'\r\n })\r\n\r\n // 图标类名\r\n const iconClasses = computed(() => [\r\n props.customClass,\r\n {\r\n 'c-icon__rotatable': props.rotate !== 0,\r\n 'c-icon__flippable': !!props.flip,\r\n },\r\n ])\r\n\r\n // 解析组件名称(纯计算属性,无副作用)\r\n const resolvedComponent = computed(() => {\r\n if (props.type !== 'component' || !props.name) return null\r\n return props.name\r\n })\r\n\r\n // 统一错误处理函数\r\n const handleError = (type: string, message: string, originalError?: any) => {\r\n console.warn(`[C_Icon] ${message}:`, iconDisplayName.value, originalError)\r\n errorMessage.value = message\r\n imageLoading.value = false // 错误时停止加载状态\r\n emit('error', type, originalError)\r\n\r\n // 如果有回退图标,尝试切换为 fallback Iconify 图标\r\n if (props.fallbackIcon && !hasError.value) {\r\n hasError.value = false\r\n // 将 name 重写为 fallbackIcon 并重置状态\r\n // 因为 props 不可变,通过模板中的 fallback 分支渲染\r\n return\r\n }\r\n\r\n hasError.value = true\r\n }\r\n\r\n // 处理点击事件\r\n const handleClick = (event: MouseEvent) => {\r\n if (!props.clickable || props.loading) return\r\n emit('click', event)\r\n }\r\n\r\n // 处理键盘事件\r\n const handleKeydown = (event: KeyboardEvent) => {\r\n if (!props.clickable || props.loading) return\r\n if (event.key === 'Enter' || event.key === ' ') {\r\n event.preventDefault()\r\n emit('click', event as any)\r\n }\r\n }\r\n\r\n // 处理图片加载成功\r\n const handleImageLoad = () => {\r\n imageLoading.value = false\r\n emit('load')\r\n }\r\n\r\n // 异步加载图片 - 修复关键函数\r\n const loadImageSrc = async () => {\r\n if (props.type !== 'image' || typeof props.name !== 'string') {\r\n imageSrc.value = ''\r\n return\r\n }\r\n\r\n // 如果是完整URL,直接设置\r\n if (\r\n props.name.startsWith('http://') ||\r\n props.name.startsWith('https://') ||\r\n props.name.startsWith('//') ||\r\n props.name.startsWith('/')\r\n ) {\r\n imageSrc.value = props.name\r\n return\r\n }\r\n\r\n // 其他情况都当作本地图片处理\r\n try {\r\n imageLoading.value = true\r\n const imageUrl = await useImage(props.name)\r\n if (imageUrl) {\r\n imageSrc.value = imageUrl\r\n } else {\r\n handleError('image', '图片路径解析失败')\r\n }\r\n } catch (error) {\r\n handleError('image', '图片加载失败', error)\r\n } finally {\r\n imageLoading.value = false\r\n }\r\n }\r\n\r\n // 验证规则映射\r\n const validationRules = {\r\n iconify: (): string | null => {\r\n if (!props.name.includes(':')) {\r\n console.warn(\r\n '[C_Icon] Iconify图标名称格式应为 \"prefix:name\",如 \"mdi:home\"'\r\n )\r\n }\r\n return null\r\n },\r\n unocss: (): string | null => {\r\n if (typeof props.name !== 'string' || !props.name.startsWith('i-')) {\r\n console.warn('[C_Icon] UnoCSS图标名称应以 \"i-\" 开头')\r\n }\r\n return null\r\n },\r\n component: (): string | null =>\r\n !resolvedComponent.value ? '无法解析组件' : null,\r\n svg: (): string | null => (!props.svgPath ? 'SVG路径不能为空' : null),\r\n image: (): string | null => {\r\n // 对于image类型,异步验证在loadImageSrc中处理\r\n return null\r\n },\r\n }\r\n\r\n // 验证配置\r\n const validateProps = () => {\r\n hasError.value = false\r\n errorMessage.value = ''\r\n\r\n // 基础验证\r\n if (!props.name) {\r\n return handleError('validation', '图标名称不能为空')\r\n }\r\n\r\n // 类型验证\r\n const validator = validationRules[props.type]\r\n if (!validator) {\r\n return handleError('type', '不支持的图标类型')\r\n }\r\n\r\n const errorMsg = validator()\r\n if (errorMsg) {\r\n handleError(props.type, errorMsg)\r\n }\r\n\r\n // 如果是图片类型,异步加载图片\r\n if (props.type === 'image') {\r\n loadImageSrc()\r\n }\r\n }\r\n\r\n // 监听关键属性变化\r\n watch(\r\n () => [props.name, props.type, props.svgPath],\r\n () => {\r\n nextTick(validateProps)\r\n },\r\n { immediate: true }\r\n )\r\n\r\n // 组件挂载后:仅对非 image 类型触发 load 事件(watch immediate 已完成验证)\r\n onMounted(() => {\r\n if (!hasError.value && props.type !== 'image') {\r\n emit('load')\r\n }\r\n })\r\n\r\n // 暴露方法给父组件\r\n defineExpose({\r\n /** 重新验证配置 */\r\n validate: validateProps,\r\n /** 是否有错误 */\r\n hasError: readonly(hasError),\r\n /** 错误信息 */\r\n errorMessage: readonly(errorMessage),\r\n /** DOM引用 */\r\n el: iconRef,\r\n })\r\n</script>\r\n"],"mappings":"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"}
|
package/dist/C_Icon2.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_Icon2.js","names":["type","clickable","loading","title","ariaLabel","name","size","color","componentProps","svgPath","viewBox","alt","fallbackIcon"],"sources":["../src/hooks/useImage/index.ts","../src/components/C_Icon/index.vue","../src/components/C_Icon/index.vue"],"sourcesContent":["/**\n * @description: 图片路径解析函数(组件库版本)\n * 在组件库中,图片路径由宿主应用管理。\n * 如果传入的是完整 URL(http/https/data:)则直接返回;\n * 否则可通过 imageResolver 配置自定义解析逻辑。\n * @param {string} imagePath 图片路径或完整 URL\n * @param {Function} resolver 可选的自定义解析器\n * @return {Promise<string>} 处理后图片路径\n */\nexport const useImage = async (\n imagePath: string,\n resolver?: (path: string) => string | Promise<string>,\n): Promise<string> => {\n if (!imagePath) return \"\";\n\n // 完整 URL 直接返回\n if (\n imagePath.startsWith(\"http://\") ||\n imagePath.startsWith(\"https://\") ||\n imagePath.startsWith(\"data:\") ||\n imagePath.startsWith(\"blob:\") ||\n imagePath.startsWith(\"/\")\n ) {\n return imagePath;\n }\n\n // 如果提供了自定义解析器,使用自定义解析器\n if (resolver) {\n try {\n return await resolver(imagePath);\n } catch (e) {\n console.error(`[useImage] 自定义解析器处理失败: ${imagePath}`, e);\n return \"\";\n }\n }\n\n // 默认:直接返回路径(宿主应用应保证路径可访问)\n return imagePath;\n};\n\n/**\n * @description: 同步版本的图片路径处理函数\n * @param {string} imagePath 图片路径或完整 URL\n * @return {string} 图片URL\n */\nexport const useImageSync = (imagePath: string): string => {\n if (!imagePath) return \"\";\n\n // 完整 URL 直接返回\n if (\n imagePath.startsWith(\"http://\") ||\n imagePath.startsWith(\"https://\") ||\n imagePath.startsWith(\"data:\") ||\n imagePath.startsWith(\"blob:\") ||\n imagePath.startsWith(\"/\")\n ) {\n return imagePath;\n }\n\n return imagePath;\n};\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-14\r\n * @Description: 支持多种图标使用方式,默认使用Iconify图标,统一错误处理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"iconRef\"\r\n class=\"c-icon\"\r\n :class=\"[\r\n `c-icon--${type}`,\r\n {\r\n 'c-icon--clickable': clickable,\r\n 'c-icon--loading': loading,\r\n 'c-icon--error': hasError,\r\n },\r\n ]\"\r\n :style=\"rootStyle\"\r\n :title=\"title || undefined\"\r\n :aria-label=\"ariaLabel || iconDisplayName\"\r\n :role=\"clickable ? 'button' : 'img'\"\r\n :tabindex=\"clickable ? 0 : -1\"\r\n @click=\"handleClick\"\r\n @keydown=\"handleKeydown\"\r\n >\r\n <!-- 方式1:Iconify图标(默认方式) -->\r\n <Icon\r\n v-if=\"\r\n (type === 'iconify' || !type) && !hasError && typeof name === 'string'\r\n \"\r\n :icon=\"name\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('iconify', 'Iconify图标加载失败')\"\r\n />\r\n\r\n <!-- 方式2:UnoCSS图标 -->\r\n <div\r\n v-else-if=\"type === 'unocss' && !hasError && typeof name === 'string'\"\r\n :class=\"[name, iconClasses]\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('unocss', 'UnoCSS图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式3:组件挂载方式 -->\r\n <component\r\n v-else-if=\"type === 'component' && resolvedComponent && !hasError\"\r\n :is=\"resolvedComponent\"\r\n :style=\"iconStyle\"\r\n v-bind=\"componentProps\"\r\n @error=\"() => handleError('component', '组件图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式4:SVG路径方式 -->\r\n <svg\r\n v-else-if=\"type === 'svg' && svgPath && !hasError\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :viewBox=\"viewBox\"\r\n :style=\"iconStyle\"\r\n :fill=\"color\"\r\n @error=\"() => handleError('svg', 'SVG图标渲染失败')\"\r\n >\r\n <path\r\n :d=\"svgPath\"\r\n :fill=\"color\"\r\n />\r\n </svg>\r\n\r\n <!-- 方式5:图片方式 - 修复异步加载 -->\r\n <img\r\n v-else-if=\"type === 'image' && imageSrc && !hasError && !imageLoading\"\r\n :src=\"imageSrc\"\r\n :alt=\"alt || iconDisplayName\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n style=\"display: inline-block; vertical-align: middle\"\r\n @error=\"() => handleError('image', '图片图标加载失败')\"\r\n @load=\"handleImageLoad\"\r\n />\r\n\r\n <!-- 图片加载状态 -->\r\n <div\r\n v-if=\"(loading || imageLoading) && !hasError\"\r\n class=\"c-icon__loading\"\r\n :style=\"iconStyle\"\r\n >\r\n <div class=\"c-icon__spinner\" />\r\n </div>\r\n\r\n <!-- 统一错误状态显示 -->\r\n <Icon\r\n v-else-if=\"hasError && fallbackIcon\"\r\n :icon=\"fallbackIcon\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n />\r\n <div\r\n v-else-if=\"hasError\"\r\n class=\"c-icon__error\"\r\n :style=\"iconStyle\"\r\n :title=\"`图标解析失败: ${iconDisplayName} (${errorMessage})`\"\r\n >\r\n ⚠️\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\n import { Icon } from '@iconify/vue'\r\n import { useImage } from '../../hooks/useImage'\r\n import { computed, nextTick, onMounted, readonly, ref, watch } from 'vue'\r\n import type { IconProps } from './types'\r\n\r\n interface LegacyIconProps extends IconProps {\r\n /** 图标名称/路径/组件实例 */\r\n name: string | any\r\n /** 图标类型 */\r\n type?: 'iconify' | 'unocss' | 'component' | 'svg' | 'image'\r\n /** 图标颜色 */\r\n color?: string\r\n /** 图标大小(px) */\r\n size?: number | string\r\n /** SVG路径数据(仅type=svg时使用) */\r\n svgPath?: string\r\n /** SVG viewBox(仅type=svg时使用) */\r\n viewBox?: string\r\n /** 图片alt属性(仅type=image时使用) */\r\n alt?: string\r\n /** 是否可点击 */\r\n clickable?: boolean\r\n /** 加载状态 */\r\n loading?: boolean\r\n /** 错误时的回退图标 */\r\n fallbackIcon?: string\r\n /** 工具提示 */\r\n title?: string\r\n /** 无障碍标签 */\r\n ariaLabel?: string\r\n /** 自定义样式类 */\r\n customClass?: string\r\n /** 旋转角度 */\r\n rotate?: number\r\n /** 是否翻转 */\r\n flip?: 'horizontal' | 'vertical' | 'both'\r\n /** 传递给组件的额外属性(仅type=component时使用) */\r\n componentProps?: Record<string, any>\r\n }\r\n\r\n const props = withDefaults(defineProps<LegacyIconProps>(), {\r\n name: undefined,\r\n type: 'iconify',\r\n color: 'currentColor',\r\n size: 18,\r\n svgPath: '',\r\n viewBox: '0 0 24 24',\r\n alt: '',\r\n clickable: false,\r\n loading: false,\r\n fallbackIcon: '',\r\n title: '',\r\n ariaLabel: '',\r\n customClass: '',\r\n rotate: 0,\r\n flip: undefined,\r\n componentProps: () => ({}),\r\n })\r\n\r\n const emit = defineEmits<{\r\n click: [event: MouseEvent]\r\n error: [type: string, error?: any]\r\n load: []\r\n }>()\r\n\r\n const iconRef = ref<HTMLElement>()\r\n const hasError = ref(false)\r\n const errorMessage = ref('')\r\n const imageSrc = ref<string>('') // 存储异步加载的图片URL\r\n const imageLoading = ref(false) // 图片加载状态\r\n\r\n // 计算根元素样式\r\n const rootStyle = computed(() => ({\r\n fontSize: typeof props.size === 'number' ? `${props.size}px` : props.size,\r\n color: props.color,\r\n cursor: props.clickable ? 'pointer' : 'inherit',\r\n }))\r\n\r\n // 计算图标样式\r\n const iconStyle = computed(() => {\r\n const style: Record<string, any> = {\r\n display: 'inline-flex',\r\n justifyContent: 'center',\r\n alignItems: 'center',\r\n width: typeof props.size === 'number' ? `${props.size}px` : props.size,\r\n height: typeof props.size === 'number' ? `${props.size}px` : props.size,\r\n }\r\n\r\n // 旋转\r\n if (props.rotate) {\r\n style.transform = `rotate(${props.rotate}deg)`\r\n }\r\n\r\n // 翻转\r\n if (props.flip) {\r\n const scaleX =\r\n props.flip === 'horizontal' || props.flip === 'both' ? -1 : 1\r\n const scaleY = props.flip === 'vertical' || props.flip === 'both' ? -1 : 1\r\n style.transform = `${\r\n style.transform || ''\r\n } scaleX(${scaleX}) scaleY(${scaleY})`.trim()\r\n }\r\n\r\n return style\r\n })\r\n\r\n // 获取图标名称用于显示\r\n const iconDisplayName = computed(() => {\r\n if (typeof props.name === 'string') {\r\n return props.name\r\n }\r\n if (props.name && props.name.__name) {\r\n return props.name.__name\r\n }\r\n if (props.name && props.name.name) {\r\n return props.name.name\r\n }\r\n return 'Component Icon'\r\n })\r\n\r\n // 图标类名\r\n const iconClasses = computed(() => [\r\n props.customClass,\r\n {\r\n 'c-icon__rotatable': props.rotate !== 0,\r\n 'c-icon__flippable': !!props.flip,\r\n },\r\n ])\r\n\r\n // 解析组件名称(纯计算属性,无副作用)\r\n const resolvedComponent = computed(() => {\r\n if (props.type !== 'component' || !props.name) return null\r\n return props.name\r\n })\r\n\r\n // 统一错误处理函数\r\n const handleError = (type: string, message: string, originalError?: any) => {\r\n console.warn(`[C_Icon] ${message}:`, iconDisplayName.value, originalError)\r\n errorMessage.value = message\r\n imageLoading.value = false // 错误时停止加载状态\r\n emit('error', type, originalError)\r\n\r\n // 如果有回退图标,尝试切换为 fallback Iconify 图标\r\n if (props.fallbackIcon && !hasError.value) {\r\n hasError.value = false\r\n // 将 name 重写为 fallbackIcon 并重置状态\r\n // 因为 props 不可变,通过模板中的 fallback 分支渲染\r\n return\r\n }\r\n\r\n hasError.value = true\r\n }\r\n\r\n // 处理点击事件\r\n const handleClick = (event: MouseEvent) => {\r\n if (!props.clickable || props.loading) return\r\n emit('click', event)\r\n }\r\n\r\n // 处理键盘事件\r\n const handleKeydown = (event: KeyboardEvent) => {\r\n if (!props.clickable || props.loading) return\r\n if (event.key === 'Enter' || event.key === ' ') {\r\n event.preventDefault()\r\n emit('click', event as any)\r\n }\r\n }\r\n\r\n // 处理图片加载成功\r\n const handleImageLoad = () => {\r\n imageLoading.value = false\r\n emit('load')\r\n }\r\n\r\n // 异步加载图片 - 修复关键函数\r\n const loadImageSrc = async () => {\r\n if (props.type !== 'image' || typeof props.name !== 'string') {\r\n imageSrc.value = ''\r\n return\r\n }\r\n\r\n // 如果是完整URL,直接设置\r\n if (\r\n props.name.startsWith('http://') ||\r\n props.name.startsWith('https://') ||\r\n props.name.startsWith('//') ||\r\n props.name.startsWith('/')\r\n ) {\r\n imageSrc.value = props.name\r\n return\r\n }\r\n\r\n // 其他情况都当作本地图片处理\r\n try {\r\n imageLoading.value = true\r\n const imageUrl = await useImage(props.name)\r\n if (imageUrl) {\r\n imageSrc.value = imageUrl\r\n } else {\r\n handleError('image', '图片路径解析失败')\r\n }\r\n } catch (error) {\r\n handleError('image', '图片加载失败', error)\r\n } finally {\r\n imageLoading.value = false\r\n }\r\n }\r\n\r\n // 验证规则映射\r\n const validationRules = {\r\n iconify: (): string | null => {\r\n if (!props.name.includes(':')) {\r\n console.warn(\r\n '[C_Icon] Iconify图标名称格式应为 \"prefix:name\",如 \"mdi:home\"'\r\n )\r\n }\r\n return null\r\n },\r\n unocss: (): string | null => {\r\n if (typeof props.name !== 'string' || !props.name.startsWith('i-')) {\r\n console.warn('[C_Icon] UnoCSS图标名称应以 \"i-\" 开头')\r\n }\r\n return null\r\n },\r\n component: (): string | null =>\r\n !resolvedComponent.value ? '无法解析组件' : null,\r\n svg: (): string | null => (!props.svgPath ? 'SVG路径不能为空' : null),\r\n image: (): string | null => {\r\n // 对于image类型,异步验证在loadImageSrc中处理\r\n return null\r\n },\r\n }\r\n\r\n // 验证配置\r\n const validateProps = () => {\r\n hasError.value = false\r\n errorMessage.value = ''\r\n\r\n // 基础验证\r\n if (!props.name) {\r\n return handleError('validation', '图标名称不能为空')\r\n }\r\n\r\n // 类型验证\r\n const validator = validationRules[props.type]\r\n if (!validator) {\r\n return handleError('type', '不支持的图标类型')\r\n }\r\n\r\n const errorMsg = validator()\r\n if (errorMsg) {\r\n handleError(props.type, errorMsg)\r\n }\r\n\r\n // 如果是图片类型,异步加载图片\r\n if (props.type === 'image') {\r\n loadImageSrc()\r\n }\r\n }\r\n\r\n // 监听关键属性变化\r\n watch(\r\n () => [props.name, props.type, props.svgPath],\r\n () => {\r\n nextTick(validateProps)\r\n },\r\n { immediate: true }\r\n )\r\n\r\n // 组件挂载后:仅对非 image 类型触发 load 事件(watch immediate 已完成验证)\r\n onMounted(() => {\r\n if (!hasError.value && props.type !== 'image') {\r\n emit('load')\r\n }\r\n })\r\n\r\n // 暴露方法给父组件\r\n defineExpose({\r\n /** 重新验证配置 */\r\n validate: validateProps,\r\n /** 是否有错误 */\r\n hasError: readonly(hasError),\r\n /** 错误信息 */\r\n errorMessage: readonly(errorMessage),\r\n /** DOM引用 */\r\n el: iconRef,\r\n })\r\n</script>\r\n","<!--\r\n * @Author: ChenYu ycyplus@gmail.com\r\n * @Date: 2025-05-14\r\n * @Description: 支持多种图标使用方式,默认使用Iconify图标,统一错误处理\r\n * @Migration: naive-ui-components 组件库迁移版本\r\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\r\n-->\r\n<template>\r\n <div\r\n ref=\"iconRef\"\r\n class=\"c-icon\"\r\n :class=\"[\r\n `c-icon--${type}`,\r\n {\r\n 'c-icon--clickable': clickable,\r\n 'c-icon--loading': loading,\r\n 'c-icon--error': hasError,\r\n },\r\n ]\"\r\n :style=\"rootStyle\"\r\n :title=\"title || undefined\"\r\n :aria-label=\"ariaLabel || iconDisplayName\"\r\n :role=\"clickable ? 'button' : 'img'\"\r\n :tabindex=\"clickable ? 0 : -1\"\r\n @click=\"handleClick\"\r\n @keydown=\"handleKeydown\"\r\n >\r\n <!-- 方式1:Iconify图标(默认方式) -->\r\n <Icon\r\n v-if=\"\r\n (type === 'iconify' || !type) && !hasError && typeof name === 'string'\r\n \"\r\n :icon=\"name\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('iconify', 'Iconify图标加载失败')\"\r\n />\r\n\r\n <!-- 方式2:UnoCSS图标 -->\r\n <div\r\n v-else-if=\"type === 'unocss' && !hasError && typeof name === 'string'\"\r\n :class=\"[name, iconClasses]\"\r\n :style=\"iconStyle\"\r\n @error=\"() => handleError('unocss', 'UnoCSS图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式3:组件挂载方式 -->\r\n <component\r\n v-else-if=\"type === 'component' && resolvedComponent && !hasError\"\r\n :is=\"resolvedComponent\"\r\n :style=\"iconStyle\"\r\n v-bind=\"componentProps\"\r\n @error=\"() => handleError('component', '组件图标渲染失败')\"\r\n />\r\n\r\n <!-- 方式4:SVG路径方式 -->\r\n <svg\r\n v-else-if=\"type === 'svg' && svgPath && !hasError\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :viewBox=\"viewBox\"\r\n :style=\"iconStyle\"\r\n :fill=\"color\"\r\n @error=\"() => handleError('svg', 'SVG图标渲染失败')\"\r\n >\r\n <path\r\n :d=\"svgPath\"\r\n :fill=\"color\"\r\n />\r\n </svg>\r\n\r\n <!-- 方式5:图片方式 - 修复异步加载 -->\r\n <img\r\n v-else-if=\"type === 'image' && imageSrc && !hasError && !imageLoading\"\r\n :src=\"imageSrc\"\r\n :alt=\"alt || iconDisplayName\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n style=\"display: inline-block; vertical-align: middle\"\r\n @error=\"() => handleError('image', '图片图标加载失败')\"\r\n @load=\"handleImageLoad\"\r\n />\r\n\r\n <!-- 图片加载状态 -->\r\n <div\r\n v-if=\"(loading || imageLoading) && !hasError\"\r\n class=\"c-icon__loading\"\r\n :style=\"iconStyle\"\r\n >\r\n <div class=\"c-icon__spinner\" />\r\n </div>\r\n\r\n <!-- 统一错误状态显示 -->\r\n <Icon\r\n v-else-if=\"hasError && fallbackIcon\"\r\n :icon=\"fallbackIcon\"\r\n :width=\"size\"\r\n :height=\"size\"\r\n :color=\"color\"\r\n :style=\"iconStyle\"\r\n />\r\n <div\r\n v-else-if=\"hasError\"\r\n class=\"c-icon__error\"\r\n :style=\"iconStyle\"\r\n :title=\"`图标解析失败: ${iconDisplayName} (${errorMessage})`\"\r\n >\r\n ⚠️\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script lang=\"ts\" setup>\r\n import { Icon } from '@iconify/vue'\r\n import { useImage } from '../../hooks/useImage'\r\n import { computed, nextTick, onMounted, readonly, ref, watch } from 'vue'\r\n import type { IconProps } from './types'\r\n\r\n interface LegacyIconProps extends IconProps {\r\n /** 图标名称/路径/组件实例 */\r\n name: string | any\r\n /** 图标类型 */\r\n type?: 'iconify' | 'unocss' | 'component' | 'svg' | 'image'\r\n /** 图标颜色 */\r\n color?: string\r\n /** 图标大小(px) */\r\n size?: number | string\r\n /** SVG路径数据(仅type=svg时使用) */\r\n svgPath?: string\r\n /** SVG viewBox(仅type=svg时使用) */\r\n viewBox?: string\r\n /** 图片alt属性(仅type=image时使用) */\r\n alt?: string\r\n /** 是否可点击 */\r\n clickable?: boolean\r\n /** 加载状态 */\r\n loading?: boolean\r\n /** 错误时的回退图标 */\r\n fallbackIcon?: string\r\n /** 工具提示 */\r\n title?: string\r\n /** 无障碍标签 */\r\n ariaLabel?: string\r\n /** 自定义样式类 */\r\n customClass?: string\r\n /** 旋转角度 */\r\n rotate?: number\r\n /** 是否翻转 */\r\n flip?: 'horizontal' | 'vertical' | 'both'\r\n /** 传递给组件的额外属性(仅type=component时使用) */\r\n componentProps?: Record<string, any>\r\n }\r\n\r\n const props = withDefaults(defineProps<LegacyIconProps>(), {\r\n name: undefined,\r\n type: 'iconify',\r\n color: 'currentColor',\r\n size: 18,\r\n svgPath: '',\r\n viewBox: '0 0 24 24',\r\n alt: '',\r\n clickable: false,\r\n loading: false,\r\n fallbackIcon: '',\r\n title: '',\r\n ariaLabel: '',\r\n customClass: '',\r\n rotate: 0,\r\n flip: undefined,\r\n componentProps: () => ({}),\r\n })\r\n\r\n const emit = defineEmits<{\r\n click: [event: MouseEvent]\r\n error: [type: string, error?: any]\r\n load: []\r\n }>()\r\n\r\n const iconRef = ref<HTMLElement>()\r\n const hasError = ref(false)\r\n const errorMessage = ref('')\r\n const imageSrc = ref<string>('') // 存储异步加载的图片URL\r\n const imageLoading = ref(false) // 图片加载状态\r\n\r\n // 计算根元素样式\r\n const rootStyle = computed(() => ({\r\n fontSize: typeof props.size === 'number' ? `${props.size}px` : props.size,\r\n color: props.color,\r\n cursor: props.clickable ? 'pointer' : 'inherit',\r\n }))\r\n\r\n // 计算图标样式\r\n const iconStyle = computed(() => {\r\n const style: Record<string, any> = {\r\n display: 'inline-flex',\r\n justifyContent: 'center',\r\n alignItems: 'center',\r\n width: typeof props.size === 'number' ? `${props.size}px` : props.size,\r\n height: typeof props.size === 'number' ? `${props.size}px` : props.size,\r\n }\r\n\r\n // 旋转\r\n if (props.rotate) {\r\n style.transform = `rotate(${props.rotate}deg)`\r\n }\r\n\r\n // 翻转\r\n if (props.flip) {\r\n const scaleX =\r\n props.flip === 'horizontal' || props.flip === 'both' ? -1 : 1\r\n const scaleY = props.flip === 'vertical' || props.flip === 'both' ? -1 : 1\r\n style.transform = `${\r\n style.transform || ''\r\n } scaleX(${scaleX}) scaleY(${scaleY})`.trim()\r\n }\r\n\r\n return style\r\n })\r\n\r\n // 获取图标名称用于显示\r\n const iconDisplayName = computed(() => {\r\n if (typeof props.name === 'string') {\r\n return props.name\r\n }\r\n if (props.name && props.name.__name) {\r\n return props.name.__name\r\n }\r\n if (props.name && props.name.name) {\r\n return props.name.name\r\n }\r\n return 'Component Icon'\r\n })\r\n\r\n // 图标类名\r\n const iconClasses = computed(() => [\r\n props.customClass,\r\n {\r\n 'c-icon__rotatable': props.rotate !== 0,\r\n 'c-icon__flippable': !!props.flip,\r\n },\r\n ])\r\n\r\n // 解析组件名称(纯计算属性,无副作用)\r\n const resolvedComponent = computed(() => {\r\n if (props.type !== 'component' || !props.name) return null\r\n return props.name\r\n })\r\n\r\n // 统一错误处理函数\r\n const handleError = (type: string, message: string, originalError?: any) => {\r\n console.warn(`[C_Icon] ${message}:`, iconDisplayName.value, originalError)\r\n errorMessage.value = message\r\n imageLoading.value = false // 错误时停止加载状态\r\n emit('error', type, originalError)\r\n\r\n // 如果有回退图标,尝试切换为 fallback Iconify 图标\r\n if (props.fallbackIcon && !hasError.value) {\r\n hasError.value = false\r\n // 将 name 重写为 fallbackIcon 并重置状态\r\n // 因为 props 不可变,通过模板中的 fallback 分支渲染\r\n return\r\n }\r\n\r\n hasError.value = true\r\n }\r\n\r\n // 处理点击事件\r\n const handleClick = (event: MouseEvent) => {\r\n if (!props.clickable || props.loading) return\r\n emit('click', event)\r\n }\r\n\r\n // 处理键盘事件\r\n const handleKeydown = (event: KeyboardEvent) => {\r\n if (!props.clickable || props.loading) return\r\n if (event.key === 'Enter' || event.key === ' ') {\r\n event.preventDefault()\r\n emit('click', event as any)\r\n }\r\n }\r\n\r\n // 处理图片加载成功\r\n const handleImageLoad = () => {\r\n imageLoading.value = false\r\n emit('load')\r\n }\r\n\r\n // 异步加载图片 - 修复关键函数\r\n const loadImageSrc = async () => {\r\n if (props.type !== 'image' || typeof props.name !== 'string') {\r\n imageSrc.value = ''\r\n return\r\n }\r\n\r\n // 如果是完整URL,直接设置\r\n if (\r\n props.name.startsWith('http://') ||\r\n props.name.startsWith('https://') ||\r\n props.name.startsWith('//') ||\r\n props.name.startsWith('/')\r\n ) {\r\n imageSrc.value = props.name\r\n return\r\n }\r\n\r\n // 其他情况都当作本地图片处理\r\n try {\r\n imageLoading.value = true\r\n const imageUrl = await useImage(props.name)\r\n if (imageUrl) {\r\n imageSrc.value = imageUrl\r\n } else {\r\n handleError('image', '图片路径解析失败')\r\n }\r\n } catch (error) {\r\n handleError('image', '图片加载失败', error)\r\n } finally {\r\n imageLoading.value = false\r\n }\r\n }\r\n\r\n // 验证规则映射\r\n const validationRules = {\r\n iconify: (): string | null => {\r\n if (!props.name.includes(':')) {\r\n console.warn(\r\n '[C_Icon] Iconify图标名称格式应为 \"prefix:name\",如 \"mdi:home\"'\r\n )\r\n }\r\n return null\r\n },\r\n unocss: (): string | null => {\r\n if (typeof props.name !== 'string' || !props.name.startsWith('i-')) {\r\n console.warn('[C_Icon] UnoCSS图标名称应以 \"i-\" 开头')\r\n }\r\n return null\r\n },\r\n component: (): string | null =>\r\n !resolvedComponent.value ? '无法解析组件' : null,\r\n svg: (): string | null => (!props.svgPath ? 'SVG路径不能为空' : null),\r\n image: (): string | null => {\r\n // 对于image类型,异步验证在loadImageSrc中处理\r\n return null\r\n },\r\n }\r\n\r\n // 验证配置\r\n const validateProps = () => {\r\n hasError.value = false\r\n errorMessage.value = ''\r\n\r\n // 基础验证\r\n if (!props.name) {\r\n return handleError('validation', '图标名称不能为空')\r\n }\r\n\r\n // 类型验证\r\n const validator = validationRules[props.type]\r\n if (!validator) {\r\n return handleError('type', '不支持的图标类型')\r\n }\r\n\r\n const errorMsg = validator()\r\n if (errorMsg) {\r\n handleError(props.type, errorMsg)\r\n }\r\n\r\n // 如果是图片类型,异步加载图片\r\n if (props.type === 'image') {\r\n loadImageSrc()\r\n }\r\n }\r\n\r\n // 监听关键属性变化\r\n watch(\r\n () => [props.name, props.type, props.svgPath],\r\n () => {\r\n nextTick(validateProps)\r\n },\r\n { immediate: true }\r\n )\r\n\r\n // 组件挂载后:仅对非 image 类型触发 load 事件(watch immediate 已完成验证)\r\n onMounted(() => {\r\n if (!hasError.value && props.type !== 'image') {\r\n emit('load')\r\n }\r\n })\r\n\r\n // 暴露方法给父组件\r\n defineExpose({\r\n /** 重新验证配置 */\r\n validate: validateProps,\r\n /** 是否有错误 */\r\n hasError: readonly(hasError),\r\n /** 错误信息 */\r\n errorMessage: readonly(errorMessage),\r\n /** DOM引用 */\r\n el: iconRef,\r\n })\r\n</script>\r\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_ImageCropper-CdTX2nxU.css","names":[],"sources":["../src/components/C_ImageCropper/components/CropperToolbar.vue?vue&type=style&index=0&scoped=499ffd01&lang.scss","../src/components/C_ImageCropper/components/CropperPreview.vue?vue&type=style&index=0&scoped=cefefdc0&lang.scss","../src/components/C_ImageCropper/index.vue?vue&type=style&index=0&scoped=296916a4&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.cropper-toolbar[data-v-499ffd01] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n align-items: center;\n padding: 8px 0;\n}","/* unplugin-vue-components disabled */.cropper-preview[data-v-cefefdc0] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n.cropper-preview .preview-label[data-v-cefefdc0] {\n display: flex;\n gap: 6px;\n align-items: center;\n font-size: 13px;\n font-weight: 500;\n color: var(--text-color-2);\n}\n.cropper-preview .preview-main[data-v-cefefdc0] {\n width: 100%;\n overflow: hidden;\n border: 1px solid var(--border-color);\n border-radius: 6px;\n background: linear-gradient(45deg, #f0f0f0 25%, transparent 25%), linear-gradient(-45deg, #f0f0f0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #f0f0f0 75%), linear-gradient(-45deg, transparent 75%, #f0f0f0 75%);\n background-position: 0 0, 0 8px, 8px -8px, -8px 0;\n background-size: 16px 16px;\n}\n.cropper-preview .preview-main--circular[data-v-cefefdc0] {\n border-radius: 50%;\n}\n.cropper-preview .preview-main__viewport img[data-v-cefefdc0] {\n display: block;\n max-width: none !important;\n}\n.cropper-preview .preview-empty[data-v-cefefdc0] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n min-height: 120px;\n}\n.cropper-preview .preview-thumbs[data-v-cefefdc0] {\n display: flex;\n gap: 12px;\n align-items: flex-end;\n justify-content: center;\n}\n.cropper-preview .preview-thumb[data-v-cefefdc0] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n align-items: center;\n}\n.cropper-preview .preview-thumb__box[data-v-cefefdc0] {\n overflow: hidden;\n border: 1px solid var(--border-color);\n border-radius: 4px;\n background: var(--body-color);\n}\n.cropper-preview .preview-thumb__box--circular[data-v-cefefdc0] {\n border-radius: 50%;\n}\n.cropper-preview .preview-thumb__viewport img[data-v-cefefdc0] {\n display: block;\n max-width: none !important;\n}\n.cropper-preview .preview-thumb__label[data-v-cefefdc0] {\n font-size: 11px;\n color: var(--text-color-3);\n}","/* unplugin-vue-components disabled *//* C_ImageCropper styles */\n.c-image-cropper[data-v-296916a4] {\n width: 100%;\n}\n.c-image-cropper__body[data-v-296916a4] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.c-image-cropper__workspace[data-v-296916a4] {\n display: flex;\n gap: 16px;\n align-items: stretch;\n}\n.c-image-cropper__canvas[data-v-296916a4] {\n position: relative;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n border: 1px solid var(--border-color);\n border-radius: 6px;\n background: var(--body-color);\n}\n.c-image-cropper__canvas[data-v-296916a4] .vue-cropper {\n width: 100% !important;\n height: 100% !important;\n}\n.c-image-cropper__preview-panel[data-v-296916a4] {\n flex: 0 1 280px;\n min-width: 200px;\n padding: 16px;\n overflow: hidden;\n border: 1px solid var(--border-color);\n border-radius: 6px;\n background: var(--card-color);\n}\n.c-image-cropper__placeholder[data-v-296916a4] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--text-color-3);\n font-size: 14px;\n}\n@media (width <= 768px) {\n.c-image-cropper__workspace[data-v-296916a4] {\n flex-direction: column;\n}\n.c-image-cropper__preview-panel[data-v-296916a4] {\n max-width: none;\n}\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;ACNA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACjEA;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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_ImageCropper-ilOqPklJ.css","names":[],"sources":["../src/components/C_ImageCropper/components/CropperToolbar.vue?vue&type=style&index=0&scoped=499ffd01&lang.scss","../src/components/C_ImageCropper/components/CropperPreview.vue?vue&type=style&index=0&scoped=cefefdc0&lang.scss","../src/components/C_ImageCropper/index.vue?vue&type=style&index=0&scoped=296916a4&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.cropper-toolbar[data-v-499ffd01] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n align-items: center;\n padding: 8px 0;\n}","/* unplugin-vue-components disabled */.cropper-preview[data-v-cefefdc0] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n.cropper-preview .preview-label[data-v-cefefdc0] {\n display: flex;\n gap: 6px;\n align-items: center;\n font-size: 13px;\n font-weight: 500;\n color: var(--text-color-2);\n}\n.cropper-preview .preview-main[data-v-cefefdc0] {\n width: 100%;\n overflow: hidden;\n border: 1px solid var(--border-color);\n border-radius: 6px;\n background: linear-gradient(45deg, #f0f0f0 25%, transparent 25%), linear-gradient(-45deg, #f0f0f0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #f0f0f0 75%), linear-gradient(-45deg, transparent 75%, #f0f0f0 75%);\n background-position: 0 0, 0 8px, 8px -8px, -8px 0;\n background-size: 16px 16px;\n}\n.cropper-preview .preview-main--circular[data-v-cefefdc0] {\n border-radius: 50%;\n}\n.cropper-preview .preview-main__viewport img[data-v-cefefdc0] {\n display: block;\n max-width: none !important;\n}\n.cropper-preview .preview-empty[data-v-cefefdc0] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n min-height: 120px;\n}\n.cropper-preview .preview-thumbs[data-v-cefefdc0] {\n display: flex;\n gap: 12px;\n align-items: flex-end;\n justify-content: center;\n}\n.cropper-preview .preview-thumb[data-v-cefefdc0] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n align-items: center;\n}\n.cropper-preview .preview-thumb__box[data-v-cefefdc0] {\n overflow: hidden;\n border: 1px solid var(--border-color);\n border-radius: 4px;\n background: var(--body-color);\n}\n.cropper-preview .preview-thumb__box--circular[data-v-cefefdc0] {\n border-radius: 50%;\n}\n.cropper-preview .preview-thumb__viewport img[data-v-cefefdc0] {\n display: block;\n max-width: none !important;\n}\n.cropper-preview .preview-thumb__label[data-v-cefefdc0] {\n font-size: 11px;\n color: var(--text-color-3);\n}","/* unplugin-vue-components disabled *//* C_ImageCropper styles */\n.c-image-cropper[data-v-296916a4] {\n width: 100%;\n}\n.c-image-cropper__body[data-v-296916a4] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.c-image-cropper__workspace[data-v-296916a4] {\n display: flex;\n gap: 16px;\n align-items: stretch;\n}\n.c-image-cropper__canvas[data-v-296916a4] {\n position: relative;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n border: 1px solid var(--border-color);\n border-radius: 6px;\n background: var(--body-color);\n}\n.c-image-cropper__canvas[data-v-296916a4] .vue-cropper {\n width: 100% !important;\n height: 100% !important;\n}\n.c-image-cropper__preview-panel[data-v-296916a4] {\n flex: 0 1 280px;\n min-width: 200px;\n padding: 16px;\n overflow: hidden;\n border: 1px solid var(--border-color);\n border-radius: 6px;\n background: var(--card-color);\n}\n.c-image-cropper__placeholder[data-v-296916a4] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--text-color-3);\n font-size: 14px;\n}\n@media (width <= 768px) {\n.c-image-cropper__workspace[data-v-296916a4] {\n flex-direction: column;\n}\n.c-image-cropper__preview-panel[data-v-296916a4] {\n max-width: none;\n}\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;ACNA;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACjEA;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"}
|