@robot-admin/naive-ui-components 0.10.3 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +117 -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 +1 -2
- package/dist/constants2.d.cts +177 -0
- package/dist/constants2.d.ts +3 -4
- package/dist/constants3.d.cts +479 -0
- package/dist/constants3.d.ts +15 -11
- 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 +11 -11
- 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 +50 -46
- 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 +6 -7
- 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} +2 -3
- package/dist/index24.vue.d.ts +650 -86
- package/dist/index25.vue.d.cts +96 -0
- package/dist/index25.vue.d.ts +86 -10
- package/dist/index26.vue.d.cts +111 -0
- package/dist/index26.vue.d.ts +4 -5
- package/dist/index27.vue.d.cts +80 -0
- package/dist/index27.vue.d.ts +4 -5
- package/dist/index3.vue.d.cts +87 -0
- package/dist/index3.vue.d.ts +4 -5
- package/dist/index4.vue.d.cts +37 -0
- package/dist/index4.vue.d.ts +4 -5
- 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 +2 -3
- package/dist/useCollapsePanel.d.cts +133 -0
- package/dist/useCollapsePanel.d.ts +7 -8
- package/dist/useCropperCore.d.cts +104 -0
- package/dist/useCropperCore.d.ts +8 -6
- package/dist/useDraggableLayout.d.cts +196 -0
- package/dist/useDraggableLayout.d.ts +17 -15
- package/dist/useDynamicFormState.d.cts +749 -0
- package/dist/useDynamicFormState.d.ts +234 -112
- package/dist/useEdgeInteraction.d.cts +324 -0
- package/dist/useEdgeInteraction.d.ts +5 -6
- package/dist/useFullscreen.d.cts +165 -0
- package/dist/useFullscreen.d.ts +3 -4
- 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 +6 -7
- package/dist/useTableQuery.cjs +3 -0
- package/dist/useTableQuery.d.cts +1684 -0
- package/dist/useTableQuery.d.ts +1684 -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 +10 -8
- package/dist/useWorkflowValidation.d.cts +1051 -0
- package/dist/useWorkflowValidation.d.ts +62 -63
- package/dist/verification.d.cts +58 -0
- package/dist/verification.d.ts +58 -0
- package/package.json +102 -69
- package/dist/C_ActionBar-BAcfOfRw.css.map +0 -1
- package/dist/C_ActionBar-DG2HW7t5.css.map +0 -1
- package/dist/C_ActionBar.js.map +0 -1
- package/dist/C_ActionBar2.js.map +0 -1
- package/dist/C_AntV-D_jTK8oZ.css.map +0 -1
- package/dist/C_AntV-DanTAq22.css.map +0 -1
- package/dist/C_AntV.js.map +0 -1
- package/dist/C_AntV2.js.map +0 -1
- package/dist/C_AudioPlayer-BnYIJXPD.css.map +0 -1
- package/dist/C_AudioPlayer-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/useModalEdit.d.ts +0 -1308
- /package/dist/{chunk.js → chunk.cjs} +0 -0
package/dist/C_Transfer2.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_Transfer2.js","names":[],"sources":["../src/components/C_Transfer/types.ts","../src/components/C_Transfer/index.vue","../src/components/C_Transfer/index.vue"],"sourcesContent":["/**\n * @Description: 穿梭框组件类型定义\n */\n\n/** 穿梭框数据项 */\nexport interface TransferItem {\n /** 唯一标识 */\n key: string | number\n /** 显示标签 */\n label: string\n /** 是否禁用 */\n disabled?: boolean\n /** 分组名称 */\n group?: string\n /** 描述信息(用于详细模式) */\n description?: string\n /** 图标(Iconify) */\n icon?: string\n /** 额外数据 */\n extra?: Record<string, unknown>\n}\n\n/** 组件 Props */\nexport interface TransferProps {\n /** 数据源 */\n data: TransferItem[]\n /** 右侧已选 key 列表 (v-model) */\n modelValue: Array<string | number>\n /** 左右栏标题 */\n titles?: [string, string]\n /** 是否可搜索 */\n filterable?: boolean\n /** 搜索占位符 */\n filterPlaceholder?: string\n /** 自定义筛选函数 */\n filterMethod?: (query: string, item: TransferItem) => boolean\n /** 是否显示全选复选框 */\n showSelectAll?: boolean\n /** 左侧空状态描述 */\n sourceEmptyText?: string\n /** 右侧空状态描述 */\n targetEmptyText?: string\n /** 尺寸 */\n size?: 'small' | 'medium' | 'large'\n}\n\n/** 默认 Props */\nexport const DEFAULT_TRANSFER_PROPS: Partial<TransferProps> = {\n titles: ['可选列表', '已选列表'],\n filterable: false,\n filterPlaceholder: '搜索...',\n showSelectAll: true,\n sourceEmptyText: '暂无数据',\n targetEmptyText: '未选择任何项',\n size: 'medium',\n}\n","<!--\n * @Description: 穿梭框组件\n-->\n<template>\n <div\n class=\"c-transfer\"\n :class=\"[`is-${mergedProps.size}`]\"\n >\n <!-- ================ Source Panel ================ -->\n <div class=\"c-transfer__panel\">\n <div class=\"c-transfer__header\">\n <NCheckbox\n v-if=\"mergedProps.showSelectAll\"\n :checked=\"isSourceAllChecked\"\n :indeterminate=\"isSourceIndeterminate\"\n @update:checked=\"toggleSourceAll\"\n >\n {{ mergedProps.titles?.[0] }}\n </NCheckbox>\n <span\n v-else\n class=\"c-transfer__title\"\n >\n {{ mergedProps.titles?.[0] }}\n </span>\n <span class=\"c-transfer__count\"\n >{{ sourceChecked.size }} / {{ sourceList.length }}</span\n >\n </div>\n\n <div\n v-if=\"mergedProps.filterable\"\n class=\"c-transfer__filter\"\n >\n <NInput\n v-model:value=\"sourceQuery\"\n clearable\n size=\"small\"\n :placeholder=\"mergedProps.filterPlaceholder\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n </div>\n\n <div\n v-if=\"filteredSourceList.length\"\n class=\"c-transfer__list\"\n >\n <template\n v-for=\"item in filteredSourceList\"\n :key=\"item.key\"\n >\n <div\n class=\"c-transfer__item\"\n :class=\"{\n 'is-disabled': item.disabled,\n 'is-checked': sourceChecked.has(item.key),\n }\"\n @click=\"!item.disabled && toggleSourceCheck(item.key)\"\n >\n <NCheckbox\n :checked=\"sourceChecked.has(item.key)\"\n :disabled=\"item.disabled\"\n size=\"small\"\n @update:checked=\"toggleSourceCheck(item.key)\"\n @click.stop\n />\n <C_Icon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n class=\"c-transfer__item-icon\"\n />\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\n </div>\n </template>\n </div>\n <div\n v-else\n class=\"c-transfer__empty\"\n >\n {{ mergedProps.sourceEmptyText }}\n </div>\n </div>\n\n <!-- ================ Actions ================ -->\n <div class=\"c-transfer__actions\">\n <button\n class=\"c-transfer__btn\"\n :class=\"{ 'is-disabled': sourceChecked.size === 0 }\"\n :disabled=\"sourceChecked.size === 0\"\n @click=\"moveToTarget\"\n >\n <C_Icon name=\"mdi:chevron-right\" />\n </button>\n <button\n class=\"c-transfer__btn\"\n :class=\"{ 'is-disabled': targetChecked.size === 0 }\"\n :disabled=\"targetChecked.size === 0\"\n @click=\"moveToSource\"\n >\n <C_Icon name=\"mdi:chevron-left\" />\n </button>\n </div>\n\n <!-- ================ Target Panel ================ -->\n <div class=\"c-transfer__panel\">\n <div class=\"c-transfer__header\">\n <NCheckbox\n v-if=\"mergedProps.showSelectAll\"\n :checked=\"isTargetAllChecked\"\n :indeterminate=\"isTargetIndeterminate\"\n @update:checked=\"toggleTargetAll\"\n >\n {{ mergedProps.titles?.[1] }}\n </NCheckbox>\n <span\n v-else\n class=\"c-transfer__title\"\n >\n {{ mergedProps.titles?.[1] }}\n </span>\n <span class=\"c-transfer__count\"\n >{{ targetChecked.size }} / {{ targetList.length }}</span\n >\n </div>\n\n <div\n v-if=\"mergedProps.filterable\"\n class=\"c-transfer__filter\"\n >\n <NInput\n v-model:value=\"targetQuery\"\n clearable\n size=\"small\"\n :placeholder=\"mergedProps.filterPlaceholder\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n </div>\n\n <div\n v-if=\"filteredTargetList.length\"\n class=\"c-transfer__list\"\n >\n <template\n v-for=\"item in filteredTargetList\"\n :key=\"item.key\"\n >\n <div\n class=\"c-transfer__item\"\n :class=\"{\n 'is-disabled': item.disabled,\n 'is-checked': targetChecked.has(item.key),\n }\"\n @click=\"!item.disabled && toggleTargetCheck(item.key)\"\n >\n <NCheckbox\n :checked=\"targetChecked.has(item.key)\"\n :disabled=\"item.disabled\"\n size=\"small\"\n @update:checked=\"toggleTargetCheck(item.key)\"\n @click.stop\n />\n <C_Icon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n class=\"c-transfer__item-icon\"\n />\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\n </div>\n </template>\n </div>\n <div\n v-else\n class=\"c-transfer__empty\"\n >\n {{ mergedProps.targetEmptyText }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed, reactive, ref } from 'vue'\n import C_Icon from '../C_Icon/index.vue'\n import {\n DEFAULT_TRANSFER_PROPS,\n type TransferItem,\n type TransferProps,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n data: TransferProps['data']\n modelValue: TransferProps['modelValue']\n titles?: TransferProps['titles']\n filterable?: boolean\n filterPlaceholder?: string\n filterMethod?: TransferProps['filterMethod']\n showSelectAll?: boolean\n sourceEmptyText?: string\n targetEmptyText?: string\n size?: TransferProps['size']\n }>(),\n {\n titles: () => DEFAULT_TRANSFER_PROPS.titles as [string, string],\n filterable: DEFAULT_TRANSFER_PROPS.filterable,\n filterPlaceholder: DEFAULT_TRANSFER_PROPS.filterPlaceholder,\n showSelectAll: DEFAULT_TRANSFER_PROPS.showSelectAll,\n sourceEmptyText: DEFAULT_TRANSFER_PROPS.sourceEmptyText,\n targetEmptyText: DEFAULT_TRANSFER_PROPS.targetEmptyText,\n size: DEFAULT_TRANSFER_PROPS.size,\n }\n )\n\n const emit = defineEmits<{\n 'update:modelValue': [value: Array<string | number>]\n change: [\n targetKeys: Array<string | number>,\n direction: 'left' | 'right',\n moveKeys: Array<string | number>,\n ]\n }>()\n\n const mergedProps = computed(() => ({ ...DEFAULT_TRANSFER_PROPS, ...props }))\n\n // ==================== 数据列表 ====================\n\n const dataMap = computed(() => {\n const m = new Map<string | number, TransferItem>()\n for (const item of props.data) m.set(item.key, item)\n return m\n })\n\n const targetKeySet = computed(() => new Set(props.modelValue))\n\n const sourceList = computed(() =>\n props.data.filter(d => !targetKeySet.value.has(d.key))\n )\n\n const targetList = computed(() => {\n const result: TransferItem[] = []\n for (const key of props.modelValue) {\n const item = dataMap.value.get(key)\n if (item) result.push(item)\n }\n return result\n })\n\n // ==================== 搜索 ====================\n\n const sourceQuery = ref('')\n const targetQuery = ref('')\n\n /** 默认过滤方法 */\n function defaultFilter(query: string, item: TransferItem): boolean {\n return item.label.toLowerCase().includes(query.toLowerCase())\n }\n\n const filterFn = computed(() => props.filterMethod ?? defaultFilter)\n\n const filteredSourceList = computed(() => {\n const q = sourceQuery.value.trim()\n return q\n ? sourceList.value.filter(item => filterFn.value(q, item))\n : sourceList.value\n })\n\n const filteredTargetList = computed(() => {\n const q = targetQuery.value.trim()\n return q\n ? targetList.value.filter(item => filterFn.value(q, item))\n : targetList.value\n })\n\n // ==================== 选中态 ====================\n\n const sourceChecked = reactive(new Set<string | number>())\n const targetChecked = reactive(new Set<string | number>())\n\n /** 切换源列表项选中状态 */\n function toggleSourceCheck(key: string | number) {\n if (sourceChecked.has(key)) sourceChecked.delete(key)\n else sourceChecked.add(key)\n }\n\n /** 切换目标列表项选中状态 */\n function toggleTargetCheck(key: string | number) {\n if (targetChecked.has(key)) targetChecked.delete(key)\n else targetChecked.add(key)\n }\n\n const isSourceAllChecked = computed(\n () =>\n filteredSourceList.value.length > 0 &&\n filteredSourceList.value.every(\n i => i.disabled || sourceChecked.has(i.key)\n )\n )\n const isSourceIndeterminate = computed(\n () =>\n !isSourceAllChecked.value &&\n filteredSourceList.value.some(i => sourceChecked.has(i.key))\n )\n const isTargetAllChecked = computed(\n () =>\n filteredTargetList.value.length > 0 &&\n filteredTargetList.value.every(\n i => i.disabled || targetChecked.has(i.key)\n )\n )\n const isTargetIndeterminate = computed(\n () =>\n !isTargetAllChecked.value &&\n filteredTargetList.value.some(i => targetChecked.has(i.key))\n )\n\n /** 切换源列表全选状态 */\n function toggleSourceAll(checked: boolean) {\n for (const item of filteredSourceList.value) {\n if (item.disabled) continue\n if (checked) sourceChecked.add(item.key)\n else sourceChecked.delete(item.key)\n }\n }\n\n /** 切换目标列表全选状态 */\n function toggleTargetAll(checked: boolean) {\n for (const item of filteredTargetList.value) {\n if (item.disabled) continue\n if (checked) targetChecked.add(item.key)\n else targetChecked.delete(item.key)\n }\n }\n\n // ==================== 穿梭操作 ====================\n\n /** 将选中项移至目标列表 */\n function moveToTarget() {\n const keys = [...sourceChecked]\n const newVal = [...props.modelValue, ...keys]\n emit('update:modelValue', newVal)\n emit('change', newVal, 'right', keys)\n sourceChecked.clear()\n }\n\n /** 将选中项移回源列表 */\n function moveToSource() {\n const keys = [...targetChecked]\n const removeSet = new Set(keys)\n const newVal = props.modelValue.filter(k => !removeSet.has(k))\n emit('update:modelValue', newVal)\n emit('change', newVal, 'left', keys)\n targetChecked.clear()\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n","<!--\n * @Description: 穿梭框组件\n-->\n<template>\n <div\n class=\"c-transfer\"\n :class=\"[`is-${mergedProps.size}`]\"\n >\n <!-- ================ Source Panel ================ -->\n <div class=\"c-transfer__panel\">\n <div class=\"c-transfer__header\">\n <NCheckbox\n v-if=\"mergedProps.showSelectAll\"\n :checked=\"isSourceAllChecked\"\n :indeterminate=\"isSourceIndeterminate\"\n @update:checked=\"toggleSourceAll\"\n >\n {{ mergedProps.titles?.[0] }}\n </NCheckbox>\n <span\n v-else\n class=\"c-transfer__title\"\n >\n {{ mergedProps.titles?.[0] }}\n </span>\n <span class=\"c-transfer__count\"\n >{{ sourceChecked.size }} / {{ sourceList.length }}</span\n >\n </div>\n\n <div\n v-if=\"mergedProps.filterable\"\n class=\"c-transfer__filter\"\n >\n <NInput\n v-model:value=\"sourceQuery\"\n clearable\n size=\"small\"\n :placeholder=\"mergedProps.filterPlaceholder\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n </div>\n\n <div\n v-if=\"filteredSourceList.length\"\n class=\"c-transfer__list\"\n >\n <template\n v-for=\"item in filteredSourceList\"\n :key=\"item.key\"\n >\n <div\n class=\"c-transfer__item\"\n :class=\"{\n 'is-disabled': item.disabled,\n 'is-checked': sourceChecked.has(item.key),\n }\"\n @click=\"!item.disabled && toggleSourceCheck(item.key)\"\n >\n <NCheckbox\n :checked=\"sourceChecked.has(item.key)\"\n :disabled=\"item.disabled\"\n size=\"small\"\n @update:checked=\"toggleSourceCheck(item.key)\"\n @click.stop\n />\n <C_Icon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n class=\"c-transfer__item-icon\"\n />\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\n </div>\n </template>\n </div>\n <div\n v-else\n class=\"c-transfer__empty\"\n >\n {{ mergedProps.sourceEmptyText }}\n </div>\n </div>\n\n <!-- ================ Actions ================ -->\n <div class=\"c-transfer__actions\">\n <button\n class=\"c-transfer__btn\"\n :class=\"{ 'is-disabled': sourceChecked.size === 0 }\"\n :disabled=\"sourceChecked.size === 0\"\n @click=\"moveToTarget\"\n >\n <C_Icon name=\"mdi:chevron-right\" />\n </button>\n <button\n class=\"c-transfer__btn\"\n :class=\"{ 'is-disabled': targetChecked.size === 0 }\"\n :disabled=\"targetChecked.size === 0\"\n @click=\"moveToSource\"\n >\n <C_Icon name=\"mdi:chevron-left\" />\n </button>\n </div>\n\n <!-- ================ Target Panel ================ -->\n <div class=\"c-transfer__panel\">\n <div class=\"c-transfer__header\">\n <NCheckbox\n v-if=\"mergedProps.showSelectAll\"\n :checked=\"isTargetAllChecked\"\n :indeterminate=\"isTargetIndeterminate\"\n @update:checked=\"toggleTargetAll\"\n >\n {{ mergedProps.titles?.[1] }}\n </NCheckbox>\n <span\n v-else\n class=\"c-transfer__title\"\n >\n {{ mergedProps.titles?.[1] }}\n </span>\n <span class=\"c-transfer__count\"\n >{{ targetChecked.size }} / {{ targetList.length }}</span\n >\n </div>\n\n <div\n v-if=\"mergedProps.filterable\"\n class=\"c-transfer__filter\"\n >\n <NInput\n v-model:value=\"targetQuery\"\n clearable\n size=\"small\"\n :placeholder=\"mergedProps.filterPlaceholder\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" />\n </template>\n </NInput>\n </div>\n\n <div\n v-if=\"filteredTargetList.length\"\n class=\"c-transfer__list\"\n >\n <template\n v-for=\"item in filteredTargetList\"\n :key=\"item.key\"\n >\n <div\n class=\"c-transfer__item\"\n :class=\"{\n 'is-disabled': item.disabled,\n 'is-checked': targetChecked.has(item.key),\n }\"\n @click=\"!item.disabled && toggleTargetCheck(item.key)\"\n >\n <NCheckbox\n :checked=\"targetChecked.has(item.key)\"\n :disabled=\"item.disabled\"\n size=\"small\"\n @update:checked=\"toggleTargetCheck(item.key)\"\n @click.stop\n />\n <C_Icon\n v-if=\"item.icon\"\n :name=\"item.icon\"\n class=\"c-transfer__item-icon\"\n />\n <span class=\"c-transfer__item-label\">{{ item.label }}</span>\n </div>\n </template>\n </div>\n <div\n v-else\n class=\"c-transfer__empty\"\n >\n {{ mergedProps.targetEmptyText }}\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { computed, reactive, ref } from 'vue'\n import C_Icon from '../C_Icon/index.vue'\n import {\n DEFAULT_TRANSFER_PROPS,\n type TransferItem,\n type TransferProps,\n } from './types'\n\n const props = withDefaults(\n defineProps<{\n data: TransferProps['data']\n modelValue: TransferProps['modelValue']\n titles?: TransferProps['titles']\n filterable?: boolean\n filterPlaceholder?: string\n filterMethod?: TransferProps['filterMethod']\n showSelectAll?: boolean\n sourceEmptyText?: string\n targetEmptyText?: string\n size?: TransferProps['size']\n }>(),\n {\n titles: () => DEFAULT_TRANSFER_PROPS.titles as [string, string],\n filterable: DEFAULT_TRANSFER_PROPS.filterable,\n filterPlaceholder: DEFAULT_TRANSFER_PROPS.filterPlaceholder,\n showSelectAll: DEFAULT_TRANSFER_PROPS.showSelectAll,\n sourceEmptyText: DEFAULT_TRANSFER_PROPS.sourceEmptyText,\n targetEmptyText: DEFAULT_TRANSFER_PROPS.targetEmptyText,\n size: DEFAULT_TRANSFER_PROPS.size,\n }\n )\n\n const emit = defineEmits<{\n 'update:modelValue': [value: Array<string | number>]\n change: [\n targetKeys: Array<string | number>,\n direction: 'left' | 'right',\n moveKeys: Array<string | number>,\n ]\n }>()\n\n const mergedProps = computed(() => ({ ...DEFAULT_TRANSFER_PROPS, ...props }))\n\n // ==================== 数据列表 ====================\n\n const dataMap = computed(() => {\n const m = new Map<string | number, TransferItem>()\n for (const item of props.data) m.set(item.key, item)\n return m\n })\n\n const targetKeySet = computed(() => new Set(props.modelValue))\n\n const sourceList = computed(() =>\n props.data.filter(d => !targetKeySet.value.has(d.key))\n )\n\n const targetList = computed(() => {\n const result: TransferItem[] = []\n for (const key of props.modelValue) {\n const item = dataMap.value.get(key)\n if (item) result.push(item)\n }\n return result\n })\n\n // ==================== 搜索 ====================\n\n const sourceQuery = ref('')\n const targetQuery = ref('')\n\n /** 默认过滤方法 */\n function defaultFilter(query: string, item: TransferItem): boolean {\n return item.label.toLowerCase().includes(query.toLowerCase())\n }\n\n const filterFn = computed(() => props.filterMethod ?? defaultFilter)\n\n const filteredSourceList = computed(() => {\n const q = sourceQuery.value.trim()\n return q\n ? sourceList.value.filter(item => filterFn.value(q, item))\n : sourceList.value\n })\n\n const filteredTargetList = computed(() => {\n const q = targetQuery.value.trim()\n return q\n ? targetList.value.filter(item => filterFn.value(q, item))\n : targetList.value\n })\n\n // ==================== 选中态 ====================\n\n const sourceChecked = reactive(new Set<string | number>())\n const targetChecked = reactive(new Set<string | number>())\n\n /** 切换源列表项选中状态 */\n function toggleSourceCheck(key: string | number) {\n if (sourceChecked.has(key)) sourceChecked.delete(key)\n else sourceChecked.add(key)\n }\n\n /** 切换目标列表项选中状态 */\n function toggleTargetCheck(key: string | number) {\n if (targetChecked.has(key)) targetChecked.delete(key)\n else targetChecked.add(key)\n }\n\n const isSourceAllChecked = computed(\n () =>\n filteredSourceList.value.length > 0 &&\n filteredSourceList.value.every(\n i => i.disabled || sourceChecked.has(i.key)\n )\n )\n const isSourceIndeterminate = computed(\n () =>\n !isSourceAllChecked.value &&\n filteredSourceList.value.some(i => sourceChecked.has(i.key))\n )\n const isTargetAllChecked = computed(\n () =>\n filteredTargetList.value.length > 0 &&\n filteredTargetList.value.every(\n i => i.disabled || targetChecked.has(i.key)\n )\n )\n const isTargetIndeterminate = computed(\n () =>\n !isTargetAllChecked.value &&\n filteredTargetList.value.some(i => targetChecked.has(i.key))\n )\n\n /** 切换源列表全选状态 */\n function toggleSourceAll(checked: boolean) {\n for (const item of filteredSourceList.value) {\n if (item.disabled) continue\n if (checked) sourceChecked.add(item.key)\n else sourceChecked.delete(item.key)\n }\n }\n\n /** 切换目标列表全选状态 */\n function toggleTargetAll(checked: boolean) {\n for (const item of filteredTargetList.value) {\n if (item.disabled) continue\n if (checked) targetChecked.add(item.key)\n else targetChecked.delete(item.key)\n }\n }\n\n // ==================== 穿梭操作 ====================\n\n /** 将选中项移至目标列表 */\n function moveToTarget() {\n const keys = [...sourceChecked]\n const newVal = [...props.modelValue, ...keys]\n emit('update:modelValue', newVal)\n emit('change', newVal, 'right', keys)\n sourceChecked.clear()\n }\n\n /** 将选中项移回源列表 */\n function moveToSource() {\n const keys = [...targetChecked]\n const removeSet = new Set(keys)\n const newVal = props.modelValue.filter(k => !removeSet.has(k))\n emit('update:modelValue', newVal)\n emit('change', newVal, 'left', keys)\n targetChecked.clear()\n }\n</script>\n\n<style scoped lang=\"scss\">\n @use './index.scss';\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_Tree-CsXgGfhn.css","names":[],"sources":["../src/components/C_Tree/index.vue?vue&type=style&index=0&scoped=30316c81&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.c-tree[data-v-30316c81] {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n.c-tree .c-tree-toolbar[data-v-30316c81] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: #fafafa;\n border-radius: 6px;\n margin-bottom: 12px;\n flex-shrink: 0;\n}\n.c-tree .c-tree-toolbar .toolbar-left .search-input[data-v-30316c81] {\n width: 260px;\n}\n.c-tree .c-tree-toolbar .toolbar-right[data-v-30316c81] {\n display: flex;\n gap: 8px;\n}\n.c-tree .c-tree-container[data-v-30316c81] {\n flex: 1;\n min-height: 0;\n border: 1px solid #e0e0e6;\n border-radius: 6px;\n padding: 12px;\n overflow-y: auto;\n scrollbar-width: thin;\n scrollbar-color: rgba(0, 0, 0, 0.12) transparent;\n}\n.c-tree .c-tree-container[data-v-30316c81]::-webkit-scrollbar {\n width: 6px;\n}\n.c-tree .c-tree-container[data-v-30316c81]::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.12);\n border-radius: 3px;\n transition: background 0.2s;\n}\n.c-tree .c-tree-container[data-v-30316c81]::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.25);\n}\n.c-tree .c-tree-container[data-v-30316c81]::-webkit-scrollbar-track {\n background: transparent;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node {\n margin-bottom: 4px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-wrapper {\n padding: 2px 0;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-content {\n padding: 12px 8px;\n border-radius: 6px;\n transition: all 0.2s ease;\n align-items: center;\n min-height: 44px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-content:hover {\n background-color: #f0f9ff;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-content:hover .tree-actions {\n opacity: 1 !important;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__prefix {\n margin-right: 0;\n display: flex;\n align-items: center;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__text {\n flex: 1;\n display: flex;\n align-items: center;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__suffix {\n margin-left: auto;\n display: flex;\n align-items: center;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-switcher {\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-right: 8px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-switcher .n-tree-node-switcher__icon {\n font-size: 14px;\n transition: transform 0.2s ease;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node.n-tree-node--selected .n-tree-node-content {\n background-color: #e6f3ff;\n border: 1px solid #1890ff;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node.n-tree-node--disabled .n-tree-node-content {\n opacity: 0.6;\n cursor: not-allowed;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node-indent {\n width: 24px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree-node:hover .tree-actions {\n opacity: 1 !important;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .tree-actions {\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .tree-actions:hover {\n opacity: 1 !important;\n}\n.c-tree[data-v-30316c81] .n-tree-node-content:hover .tree-actions,\n.c-tree[data-v-30316c81] .n-tree-node:hover .tree-actions {\n opacity: 1 !important;\n}\n.c-tree[data-v-30316c81] .tree-actions {\n opacity: 0;\n transition: opacity 0.2s ease-in-out;\n}\n\n/* responsive */\n@media (max-width: 768px) {\n.c-tree .c-tree-toolbar[data-v-30316c81] {\n flex-direction: column;\n gap: 12px;\n}\n.c-tree .c-tree-toolbar .toolbar-left .search-input[data-v-30316c81] {\n width: 100%;\n}\n.c-tree .c-tree-toolbar .toolbar-right[data-v-30316c81] {\n width: 100%;\n justify-content: center;\n}\n.c-tree[data-v-30316c81] .tree-actions {\n opacity: 1 !important;\n}\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_Tree-P7eRpnOa.css","names":[],"sources":["../src/components/C_Tree/index.vue?vue&type=style&index=0&scoped=30316c81&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.c-tree[data-v-30316c81] {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n.c-tree .c-tree-toolbar[data-v-30316c81] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background: #fafafa;\n border-radius: 6px;\n margin-bottom: 12px;\n flex-shrink: 0;\n}\n.c-tree .c-tree-toolbar .toolbar-left .search-input[data-v-30316c81] {\n width: 260px;\n}\n.c-tree .c-tree-toolbar .toolbar-right[data-v-30316c81] {\n display: flex;\n gap: 8px;\n}\n.c-tree .c-tree-container[data-v-30316c81] {\n flex: 1;\n min-height: 0;\n border: 1px solid #e0e0e6;\n border-radius: 6px;\n padding: 12px;\n overflow-y: auto;\n scrollbar-width: thin;\n scrollbar-color: rgba(0, 0, 0, 0.12) transparent;\n}\n.c-tree .c-tree-container[data-v-30316c81]::-webkit-scrollbar {\n width: 6px;\n}\n.c-tree .c-tree-container[data-v-30316c81]::-webkit-scrollbar-thumb {\n background: rgba(0, 0, 0, 0.12);\n border-radius: 3px;\n transition: background 0.2s;\n}\n.c-tree .c-tree-container[data-v-30316c81]::-webkit-scrollbar-thumb:hover {\n background: rgba(0, 0, 0, 0.25);\n}\n.c-tree .c-tree-container[data-v-30316c81]::-webkit-scrollbar-track {\n background: transparent;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node {\n margin-bottom: 4px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-wrapper {\n padding: 2px 0;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-content {\n padding: 12px 8px;\n border-radius: 6px;\n transition: all 0.2s ease;\n align-items: center;\n min-height: 44px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-content:hover {\n background-color: #f0f9ff;\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-content:hover .tree-actions {\n opacity: 1 !important;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__prefix {\n margin-right: 0;\n display: flex;\n align-items: center;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__text {\n flex: 1;\n display: flex;\n align-items: center;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-content .n-tree-node-content__suffix {\n margin-left: auto;\n display: flex;\n align-items: center;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-switcher {\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-right: 8px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node .n-tree-node-switcher .n-tree-node-switcher__icon {\n font-size: 14px;\n transition: transform 0.2s ease;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node.n-tree-node--selected .n-tree-node-content {\n background-color: #e6f3ff;\n border: 1px solid #1890ff;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node.n-tree-node--disabled .n-tree-node-content {\n opacity: 0.6;\n cursor: not-allowed;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree .n-tree-node-indent {\n width: 24px;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .n-tree-node:hover .tree-actions {\n opacity: 1 !important;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .tree-actions {\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n.c-tree .c-tree-container .tree-instance[data-v-30316c81] .tree-actions:hover {\n opacity: 1 !important;\n}\n.c-tree[data-v-30316c81] .n-tree-node-content:hover .tree-actions,\n.c-tree[data-v-30316c81] .n-tree-node:hover .tree-actions {\n opacity: 1 !important;\n}\n.c-tree[data-v-30316c81] .tree-actions {\n opacity: 0;\n transition: opacity 0.2s ease-in-out;\n}\n\n/* responsive */\n@media (max-width: 768px) {\n.c-tree .c-tree-toolbar[data-v-30316c81] {\n flex-direction: column;\n gap: 12px;\n}\n.c-tree .c-tree-toolbar .toolbar-left .search-input[data-v-30316c81] {\n width: 100%;\n}\n.c-tree .c-tree-toolbar .toolbar-right[data-v-30316c81] {\n width: 100%;\n justify-content: center;\n}\n.c-tree[data-v-30316c81] .tree-actions {\n opacity: 1 !important;\n}\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
package/dist/C_Tree.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_Tree.js","names":["C_Icon","NTag","NPopconfirm","NButton","NSpace","showToolbar","searchable","searchPlaceholder","addable","addText","refreshable","draggable","showLine","keyField","labelField","childrenField"],"sources":["../src/components/C_Tree/data.ts","../src/components/C_Tree/composables/useTreeOperations.ts","../src/components/C_Tree/index.vue","../src/components/C_Tree/index.vue"],"sourcesContent":["import type { TreeMode, TreeProps } from \"./types\";\r\n\r\nexport const presetConfigs: Record<TreeMode, Partial<TreeProps>> = {\r\n menu: {\r\n draggable: true,\r\n showLine: true,\r\n iconConfig: {\r\n typeMap: {\r\n directory: \"mdi:folder\",\r\n menu: \"mdi:file-document\",\r\n button: \"mdi:button-cursor\",\r\n },\r\n },\r\n actions: [\r\n { key: \"add\", text: \"新增子菜单\", icon: \"mdi:plus\", type: \"primary\" },\r\n { key: \"edit\", text: \"编辑\", icon: \"mdi:pencil\", type: \"info\" },\r\n {\r\n key: \"delete\",\r\n text: \"删除\",\r\n icon: \"mdi:delete\",\r\n type: \"error\",\r\n confirm: \"确认删除该菜单吗?\",\r\n },\r\n ],\r\n },\r\n file: {\r\n draggable: false,\r\n showLine: false,\r\n iconConfig: {\r\n typeMap: {\r\n folder: \"mdi:folder\",\r\n file: \"mdi:file-document\",\r\n image: \"mdi:file-image\",\r\n video: \"mdi:file-video\",\r\n audio: \"mdi:file-music\",\r\n },\r\n },\r\n actions: [\r\n { key: \"open\", text: \"打开\", icon: \"mdi:folder-open\", type: \"primary\" },\r\n { key: \"rename\", text: \"重命名\", icon: \"mdi:rename-box\", type: \"info\" },\r\n {\r\n key: \"delete\",\r\n text: \"删除\",\r\n icon: \"mdi:delete\",\r\n type: \"error\",\r\n confirm: \"确认删除该文件吗?\",\r\n },\r\n ],\r\n },\r\n org: {\r\n draggable: false,\r\n showLine: true,\r\n iconConfig: {\r\n typeMap: {\r\n department: \"mdi:domain\",\r\n user: \"mdi:account\",\r\n manager: \"mdi:account-tie\",\r\n },\r\n },\r\n actions: [\r\n { key: \"add\", text: \"新增下级\", icon: \"mdi:plus\", type: \"primary\" },\r\n { key: \"edit\", text: \"编辑\", icon: \"mdi:pencil\", type: \"info\" },\r\n ],\r\n },\r\n custom: {},\r\n};\r\n","import { ref, computed, h } from \"vue\";\r\nimport { NTag, NButton, NPopconfirm, NSpace } from \"naive-ui\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport { presetConfigs } from \"../data\";\r\nimport type {\r\n TreeOption,\r\n TreeNodeData,\r\n TreeProps,\r\n TreeExpose,\r\n DropInfo,\r\n} from \"../types\";\r\n\r\ntype EmitFn = {\r\n (\r\n event: \"node-select\",\r\n node: TreeNodeData | null,\r\n selectedKeys: (string | number)[],\r\n ): void;\r\n (event: \"node-action\", action: string, node: TreeNodeData): void;\r\n (event: \"node-drop\", info: DropInfo): void;\r\n (event: \"add\", parentNode?: TreeNodeData): void;\r\n (event: \"refresh\"): void;\r\n};\r\n\r\nexport function useTreeOperations(props: TreeProps, emit: EmitFn) {\r\n const internalSearchPattern = ref(\"\");\r\n const expandedKeys = ref<(string | number)[]>(\r\n props.defaultExpandedKeys ?? [],\r\n );\r\n const selectedKeys = ref<(string | number)[]>(\r\n props.defaultSelectedKeys ?? [],\r\n );\r\n const isAllExpanded = ref(props.defaultExpandAll ?? false);\r\n\r\n const keyField = computed(() => props.keyField ?? \"id\");\r\n const labelField = computed(() => props.labelField ?? \"name\");\r\n const childrenField = computed(() => props.childrenField ?? \"children\");\r\n const iconField = computed(() => props.iconField ?? \"icon\");\r\n\r\n const mergedConfig = computed(() => {\r\n const preset = presetConfigs[props.mode ?? \"custom\"] || {};\r\n return {\r\n ...preset,\r\n ...props,\r\n iconConfig: { ...preset.iconConfig, ...props.iconConfig },\r\n actions: props.actions?.length ? props.actions : preset.actions || [],\r\n };\r\n });\r\n\r\n const treeData = computed((): TreeOption[] => props.data as TreeOption[]);\r\n\r\n const currentSearchPattern = computed(\r\n () => props.searchPattern || internalSearchPattern.value,\r\n );\r\n\r\n const selectedNode = computed((): TreeNodeData | null => {\r\n if (selectedKeys.value.length === 0) return null;\r\n const findNode = (\r\n nodes: TreeNodeData[],\r\n id: string | number,\r\n ): TreeNodeData | null => {\r\n for (const node of nodes) {\r\n if (node[keyField.value] === id) return node;\r\n if (node[childrenField.value]) {\r\n const found = findNode(node[childrenField.value], id);\r\n if (found) return found;\r\n }\r\n }\r\n return null;\r\n };\r\n return findNode(props.data, selectedKeys.value[0]);\r\n });\r\n\r\n const getAllKeys = (nodes: TreeNodeData[]): (string | number)[] => {\r\n const keys: (string | number)[] = [];\r\n nodes.forEach((node) => {\r\n keys.push(node[keyField.value]);\r\n if (node[childrenField.value]) {\r\n keys.push(...getAllKeys(node[childrenField.value]));\r\n }\r\n });\r\n return keys;\r\n };\r\n\r\n const getNodeIcon = (node: TreeNodeData): string => {\r\n const config = mergedConfig.value.iconConfig!;\r\n if (node[iconField.value]) return node[iconField.value];\r\n if (node.type && config.typeMap?.[node.type])\r\n return config.typeMap[node.type];\r\n return config.default || \"mdi:circle-outline\";\r\n };\r\n\r\n const getNodeIconColor = (node: TreeNodeData): string => {\r\n const config = mergedConfig.value.iconConfig!;\r\n if (node.type && config.colorMap?.[node.type])\r\n return config.colorMap[node.type];\r\n return \"currentColor\";\r\n };\r\n\r\n const renderPrefix = ({ option }: { option: TreeOption }) => {\r\n const node = option as TreeNodeData;\r\n return h(C_Icon, {\r\n name: getNodeIcon(node),\r\n size: 18,\r\n color: getNodeIconColor(node),\r\n class: \"mr-3 flex-shrink-0\",\r\n });\r\n };\r\n\r\n const renderLabel = ({ option }: { option: TreeOption }) => {\r\n const node = option as TreeNodeData;\r\n const statusTags =\r\n props.statusConfigs\r\n ?.map((config) => {\r\n const value = node[config.field];\r\n const statusConfig = config.values[value];\r\n if (!statusConfig?.text) return null;\r\n return h(\r\n NTag,\r\n {\r\n type: statusConfig.type,\r\n size: \"small\",\r\n class: \"ml-2\",\r\n style: { fontSize: \"12px\" },\r\n },\r\n { default: () => statusConfig.text },\r\n );\r\n })\r\n .filter(Boolean) || [];\r\n\r\n return h(\r\n \"div\",\r\n {\r\n class: \"flex items-center flex-1 min-w-0 py-1\",\r\n style: { lineHeight: \"1.5\" },\r\n },\r\n [\r\n h(\r\n \"span\",\r\n { class: \"mr-3 font-medium text-sm\" },\r\n node[labelField.value],\r\n ),\r\n ...statusTags,\r\n ],\r\n );\r\n };\r\n\r\n const renderSuffix = ({ option }: { option: TreeOption }) => {\r\n const node = option as TreeNodeData;\r\n const actions = mergedConfig.value.actions || [];\r\n\r\n const actionButtons = actions\r\n .filter((action) => !action.show || action.show(node))\r\n .map((action) => {\r\n const buttonProps = {\r\n size: \"tiny\" as const,\r\n type: action.type || (\"default\" as const),\r\n secondary: true,\r\n style: { padding: \"4px 6px\", minWidth: \"24px\", height: \"24px\" },\r\n onClick: (e: Event) => {\r\n e.stopPropagation();\r\n handleNodeAction(action.key, node);\r\n },\r\n };\r\n const iconEl = h(C_Icon, {\r\n name: action.icon,\r\n size: 12,\r\n title: action.text,\r\n });\r\n\r\n if (action.confirm) {\r\n return h(\r\n NPopconfirm,\r\n { onPositiveClick: () => handleNodeAction(action.key, node) },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n ...buttonProps,\r\n onClick: (e: Event) => e.stopPropagation(),\r\n },\r\n { icon: () => iconEl },\r\n ),\r\n default: () => action.confirm,\r\n },\r\n );\r\n }\r\n return h(NButton, buttonProps, { icon: () => iconEl });\r\n });\r\n\r\n return h(\r\n \"div\",\r\n {\r\n class: \"tree-actions\",\r\n style: {\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n marginLeft: \"8px\",\r\n opacity: \"0\",\r\n transition: \"opacity 0.2s ease\",\r\n },\r\n },\r\n [h(NSpace, { size: 4 }, { default: () => actionButtons })],\r\n );\r\n };\r\n\r\n const toggleExpandAll = (): void => {\r\n if (isAllExpanded.value) {\r\n expandedKeys.value = [];\r\n isAllExpanded.value = false;\r\n } else {\r\n expandedKeys.value = getAllKeys(props.data);\r\n isAllExpanded.value = true;\r\n }\r\n };\r\n\r\n const handleExpandedKeysChange = (keys: (string | number)[]): void => {\r\n expandedKeys.value = keys;\r\n isAllExpanded.value = keys.length === getAllKeys(props.data).length;\r\n };\r\n\r\n const handleSelectedKeysChange = (keys: (string | number)[]): void => {\r\n selectedKeys.value = keys;\r\n emit(\"node-select\", selectedNode.value, keys);\r\n };\r\n\r\n const handleDrop = (info: DropInfo): void => emit(\"node-drop\", info);\r\n\r\n const handleNodeAction = (action: string, node: TreeNodeData): void => {\r\n if (action === \"add\") {\r\n emit(\"add\", node);\r\n } else {\r\n emit(\"node-action\", action, node);\r\n }\r\n };\r\n\r\n const handleAdd = (): void => emit(\"add\");\r\n const handleRefresh = (): void => emit(\"refresh\");\r\n\r\n const expose: TreeExpose = {\r\n expandAll: () => {\r\n expandedKeys.value = getAllKeys(props.data);\r\n isAllExpanded.value = true;\r\n },\r\n collapseAll: () => {\r\n expandedKeys.value = [];\r\n isAllExpanded.value = false;\r\n },\r\n selectNode: (key: string | number) => {\r\n selectedKeys.value = [key];\r\n },\r\n getSelectedNode: () => selectedNode.value,\r\n expandedKeys,\r\n selectedKeys,\r\n };\r\n\r\n return {\r\n internalSearchPattern,\r\n expandedKeys,\r\n selectedKeys,\r\n isAllExpanded,\r\n treeData,\r\n currentSearchPattern,\r\n renderPrefix,\r\n renderLabel,\r\n renderSuffix,\r\n toggleExpandAll,\r\n handleExpandedKeysChange,\r\n handleSelectedKeysChange,\r\n handleDrop,\r\n handleAdd,\r\n handleRefresh,\r\n expose,\r\n };\r\n}\r\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-27\n * @Description: 树型组件 — 薄 UI 壳,逻辑由 useTreeOperations 驱动\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-tree\">\n <div v-if=\"showToolbar\" class=\"c-tree-toolbar\">\n <div class=\"toolbar-left\">\n <NInput\n v-if=\"searchable\"\n v-model:value=\"internalSearchPattern\"\n :placeholder=\"searchPlaceholder\"\n clearable\n class=\"search-input\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" :size=\"16\" />\n </template>\n </NInput>\n </div>\n <div class=\"toolbar-right\">\n <slot name=\"toolbar-actions\">\n <NButton v-if=\"addable\" type=\"primary\" @click=\"handleAdd()\">\n <template #icon>\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\n </template>\n {{ addText }}\n </NButton>\n <NButton @click=\"toggleExpandAll\">\n <template #icon>\n <C_Icon name=\"mdi:file-tree\" :size=\"16\" />\n </template>\n {{ isAllExpanded ? \"收起全部\" : \"展开全部\" }}\n </NButton>\n <NButton v-if=\"refreshable\" @click=\"handleRefresh\">\n <template #icon>\n <C_Icon name=\"mdi:refresh\" :size=\"16\" />\n </template>\n 刷新\n </NButton>\n </slot>\n </div>\n </div>\n\n <div class=\"c-tree-container\">\n <NTree\n :data=\"treeData\"\n :pattern=\"currentSearchPattern\"\n :expanded-keys=\"expandedKeys\"\n :selected-keys=\"selectedKeys\"\n :draggable=\"draggable\"\n :show-line=\"showLine\"\n :render-prefix=\"renderPrefix\"\n :render-suffix=\"renderSuffix\"\n :render-label=\"renderLabel\"\n :key-field=\"keyField\"\n :label-field=\"labelField\"\n :children-field=\"childrenField\"\n @update:expanded-keys=\"handleExpandedKeysChange\"\n @update:selected-keys=\"handleSelectedKeysChange\"\n @drop=\"handleDrop\"\n class=\"tree-instance\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { NInput, NButton, NTree } from \"naive-ui\";\nimport C_Icon from \"../C_Icon/index.vue\";\nimport { useTreeOperations } from \"./composables/useTreeOperations\";\nimport type { TreeProps, TreeEmits, TreeExpose } from \"./types\";\n\ndefineOptions({ name: \"C_Tree\" });\n\nconst props = withDefaults(defineProps<TreeProps>(), {\n mode: \"custom\",\n keyField: \"id\",\n labelField: \"name\",\n childrenField: \"children\",\n searchPattern: \"\",\n searchable: true,\n searchPlaceholder: \"搜索...\",\n draggable: false,\n showLine: true,\n showToolbar: true,\n addable: true,\n addText: \"新增\",\n refreshable: true,\n iconField: \"icon\",\n iconConfig: () => ({\n default: \"mdi:circle-outline\",\n typeMap: {},\n colorMap: {},\n }),\n statusConfigs: () => [],\n actions: () => [],\n defaultExpandAll: false,\n defaultExpandedKeys: () => [],\n defaultSelectedKeys: () => [],\n});\n\nconst emit = defineEmits<TreeEmits>();\n\nconst {\n internalSearchPattern,\n expandedKeys,\n selectedKeys,\n isAllExpanded,\n treeData,\n currentSearchPattern,\n renderPrefix,\n renderLabel,\n renderSuffix,\n toggleExpandAll,\n handleExpandedKeysChange,\n handleSelectedKeysChange,\n handleDrop,\n handleAdd,\n handleRefresh,\n expose,\n} = useTreeOperations(props, emit);\n\ndefineExpose<TreeExpose>(expose);\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-27\n * @Description: 树型组件 — 薄 UI 壳,逻辑由 useTreeOperations 驱动\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-tree\">\n <div v-if=\"showToolbar\" class=\"c-tree-toolbar\">\n <div class=\"toolbar-left\">\n <NInput\n v-if=\"searchable\"\n v-model:value=\"internalSearchPattern\"\n :placeholder=\"searchPlaceholder\"\n clearable\n class=\"search-input\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" :size=\"16\" />\n </template>\n </NInput>\n </div>\n <div class=\"toolbar-right\">\n <slot name=\"toolbar-actions\">\n <NButton v-if=\"addable\" type=\"primary\" @click=\"handleAdd()\">\n <template #icon>\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\n </template>\n {{ addText }}\n </NButton>\n <NButton @click=\"toggleExpandAll\">\n <template #icon>\n <C_Icon name=\"mdi:file-tree\" :size=\"16\" />\n </template>\n {{ isAllExpanded ? \"收起全部\" : \"展开全部\" }}\n </NButton>\n <NButton v-if=\"refreshable\" @click=\"handleRefresh\">\n <template #icon>\n <C_Icon name=\"mdi:refresh\" :size=\"16\" />\n </template>\n 刷新\n </NButton>\n </slot>\n </div>\n </div>\n\n <div class=\"c-tree-container\">\n <NTree\n :data=\"treeData\"\n :pattern=\"currentSearchPattern\"\n :expanded-keys=\"expandedKeys\"\n :selected-keys=\"selectedKeys\"\n :draggable=\"draggable\"\n :show-line=\"showLine\"\n :render-prefix=\"renderPrefix\"\n :render-suffix=\"renderSuffix\"\n :render-label=\"renderLabel\"\n :key-field=\"keyField\"\n :label-field=\"labelField\"\n :children-field=\"childrenField\"\n @update:expanded-keys=\"handleExpandedKeysChange\"\n @update:selected-keys=\"handleSelectedKeysChange\"\n @drop=\"handleDrop\"\n class=\"tree-instance\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { NInput, NButton, NTree } from \"naive-ui\";\nimport C_Icon from \"../C_Icon/index.vue\";\nimport { useTreeOperations } from \"./composables/useTreeOperations\";\nimport type { TreeProps, TreeEmits, TreeExpose } from \"./types\";\n\ndefineOptions({ name: \"C_Tree\" });\n\nconst props = withDefaults(defineProps<TreeProps>(), {\n mode: \"custom\",\n keyField: \"id\",\n labelField: \"name\",\n childrenField: \"children\",\n searchPattern: \"\",\n searchable: true,\n searchPlaceholder: \"搜索...\",\n draggable: false,\n showLine: true,\n showToolbar: true,\n addable: true,\n addText: \"新增\",\n refreshable: true,\n iconField: \"icon\",\n iconConfig: () => ({\n default: \"mdi:circle-outline\",\n typeMap: {},\n colorMap: {},\n }),\n statusConfigs: () => [],\n actions: () => [],\n defaultExpandAll: false,\n defaultExpandedKeys: () => [],\n defaultSelectedKeys: () => [],\n});\n\nconst emit = defineEmits<TreeEmits>();\n\nconst {\n internalSearchPattern,\n expandedKeys,\n selectedKeys,\n isAllExpanded,\n treeData,\n currentSearchPattern,\n renderPrefix,\n renderLabel,\n renderSuffix,\n toggleExpandAll,\n handleExpandedKeysChange,\n handleSelectedKeysChange,\n handleDrop,\n handleAdd,\n handleRefresh,\n expose,\n} = useTreeOperations(props, emit);\n\ndefineExpose<TreeExpose>(expose);\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n"],"mappings":"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"}
|
package/dist/C_Tree2.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_Tree2.js","names":["C_Icon","showToolbar","searchable","searchPlaceholder","addable","addText","refreshable","draggable","showLine","keyField","labelField","childrenField"],"sources":["../src/components/C_Tree/data.ts","../src/components/C_Tree/composables/useTreeOperations.ts","../src/components/C_Tree/index.vue","../src/components/C_Tree/index.vue"],"sourcesContent":["import type { TreeMode, TreeProps } from \"./types\";\r\n\r\nexport const presetConfigs: Record<TreeMode, Partial<TreeProps>> = {\r\n menu: {\r\n draggable: true,\r\n showLine: true,\r\n iconConfig: {\r\n typeMap: {\r\n directory: \"mdi:folder\",\r\n menu: \"mdi:file-document\",\r\n button: \"mdi:button-cursor\",\r\n },\r\n },\r\n actions: [\r\n { key: \"add\", text: \"新增子菜单\", icon: \"mdi:plus\", type: \"primary\" },\r\n { key: \"edit\", text: \"编辑\", icon: \"mdi:pencil\", type: \"info\" },\r\n {\r\n key: \"delete\",\r\n text: \"删除\",\r\n icon: \"mdi:delete\",\r\n type: \"error\",\r\n confirm: \"确认删除该菜单吗?\",\r\n },\r\n ],\r\n },\r\n file: {\r\n draggable: false,\r\n showLine: false,\r\n iconConfig: {\r\n typeMap: {\r\n folder: \"mdi:folder\",\r\n file: \"mdi:file-document\",\r\n image: \"mdi:file-image\",\r\n video: \"mdi:file-video\",\r\n audio: \"mdi:file-music\",\r\n },\r\n },\r\n actions: [\r\n { key: \"open\", text: \"打开\", icon: \"mdi:folder-open\", type: \"primary\" },\r\n { key: \"rename\", text: \"重命名\", icon: \"mdi:rename-box\", type: \"info\" },\r\n {\r\n key: \"delete\",\r\n text: \"删除\",\r\n icon: \"mdi:delete\",\r\n type: \"error\",\r\n confirm: \"确认删除该文件吗?\",\r\n },\r\n ],\r\n },\r\n org: {\r\n draggable: false,\r\n showLine: true,\r\n iconConfig: {\r\n typeMap: {\r\n department: \"mdi:domain\",\r\n user: \"mdi:account\",\r\n manager: \"mdi:account-tie\",\r\n },\r\n },\r\n actions: [\r\n { key: \"add\", text: \"新增下级\", icon: \"mdi:plus\", type: \"primary\" },\r\n { key: \"edit\", text: \"编辑\", icon: \"mdi:pencil\", type: \"info\" },\r\n ],\r\n },\r\n custom: {},\r\n};\r\n","import { ref, computed, h } from \"vue\";\r\nimport { NTag, NButton, NPopconfirm, NSpace } from \"naive-ui\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport { presetConfigs } from \"../data\";\r\nimport type {\r\n TreeOption,\r\n TreeNodeData,\r\n TreeProps,\r\n TreeExpose,\r\n DropInfo,\r\n} from \"../types\";\r\n\r\ntype EmitFn = {\r\n (\r\n event: \"node-select\",\r\n node: TreeNodeData | null,\r\n selectedKeys: (string | number)[],\r\n ): void;\r\n (event: \"node-action\", action: string, node: TreeNodeData): void;\r\n (event: \"node-drop\", info: DropInfo): void;\r\n (event: \"add\", parentNode?: TreeNodeData): void;\r\n (event: \"refresh\"): void;\r\n};\r\n\r\nexport function useTreeOperations(props: TreeProps, emit: EmitFn) {\r\n const internalSearchPattern = ref(\"\");\r\n const expandedKeys = ref<(string | number)[]>(\r\n props.defaultExpandedKeys ?? [],\r\n );\r\n const selectedKeys = ref<(string | number)[]>(\r\n props.defaultSelectedKeys ?? [],\r\n );\r\n const isAllExpanded = ref(props.defaultExpandAll ?? false);\r\n\r\n const keyField = computed(() => props.keyField ?? \"id\");\r\n const labelField = computed(() => props.labelField ?? \"name\");\r\n const childrenField = computed(() => props.childrenField ?? \"children\");\r\n const iconField = computed(() => props.iconField ?? \"icon\");\r\n\r\n const mergedConfig = computed(() => {\r\n const preset = presetConfigs[props.mode ?? \"custom\"] || {};\r\n return {\r\n ...preset,\r\n ...props,\r\n iconConfig: { ...preset.iconConfig, ...props.iconConfig },\r\n actions: props.actions?.length ? props.actions : preset.actions || [],\r\n };\r\n });\r\n\r\n const treeData = computed((): TreeOption[] => props.data as TreeOption[]);\r\n\r\n const currentSearchPattern = computed(\r\n () => props.searchPattern || internalSearchPattern.value,\r\n );\r\n\r\n const selectedNode = computed((): TreeNodeData | null => {\r\n if (selectedKeys.value.length === 0) return null;\r\n const findNode = (\r\n nodes: TreeNodeData[],\r\n id: string | number,\r\n ): TreeNodeData | null => {\r\n for (const node of nodes) {\r\n if (node[keyField.value] === id) return node;\r\n if (node[childrenField.value]) {\r\n const found = findNode(node[childrenField.value], id);\r\n if (found) return found;\r\n }\r\n }\r\n return null;\r\n };\r\n return findNode(props.data, selectedKeys.value[0]);\r\n });\r\n\r\n const getAllKeys = (nodes: TreeNodeData[]): (string | number)[] => {\r\n const keys: (string | number)[] = [];\r\n nodes.forEach((node) => {\r\n keys.push(node[keyField.value]);\r\n if (node[childrenField.value]) {\r\n keys.push(...getAllKeys(node[childrenField.value]));\r\n }\r\n });\r\n return keys;\r\n };\r\n\r\n const getNodeIcon = (node: TreeNodeData): string => {\r\n const config = mergedConfig.value.iconConfig!;\r\n if (node[iconField.value]) return node[iconField.value];\r\n if (node.type && config.typeMap?.[node.type])\r\n return config.typeMap[node.type];\r\n return config.default || \"mdi:circle-outline\";\r\n };\r\n\r\n const getNodeIconColor = (node: TreeNodeData): string => {\r\n const config = mergedConfig.value.iconConfig!;\r\n if (node.type && config.colorMap?.[node.type])\r\n return config.colorMap[node.type];\r\n return \"currentColor\";\r\n };\r\n\r\n const renderPrefix = ({ option }: { option: TreeOption }) => {\r\n const node = option as TreeNodeData;\r\n return h(C_Icon, {\r\n name: getNodeIcon(node),\r\n size: 18,\r\n color: getNodeIconColor(node),\r\n class: \"mr-3 flex-shrink-0\",\r\n });\r\n };\r\n\r\n const renderLabel = ({ option }: { option: TreeOption }) => {\r\n const node = option as TreeNodeData;\r\n const statusTags =\r\n props.statusConfigs\r\n ?.map((config) => {\r\n const value = node[config.field];\r\n const statusConfig = config.values[value];\r\n if (!statusConfig?.text) return null;\r\n return h(\r\n NTag,\r\n {\r\n type: statusConfig.type,\r\n size: \"small\",\r\n class: \"ml-2\",\r\n style: { fontSize: \"12px\" },\r\n },\r\n { default: () => statusConfig.text },\r\n );\r\n })\r\n .filter(Boolean) || [];\r\n\r\n return h(\r\n \"div\",\r\n {\r\n class: \"flex items-center flex-1 min-w-0 py-1\",\r\n style: { lineHeight: \"1.5\" },\r\n },\r\n [\r\n h(\r\n \"span\",\r\n { class: \"mr-3 font-medium text-sm\" },\r\n node[labelField.value],\r\n ),\r\n ...statusTags,\r\n ],\r\n );\r\n };\r\n\r\n const renderSuffix = ({ option }: { option: TreeOption }) => {\r\n const node = option as TreeNodeData;\r\n const actions = mergedConfig.value.actions || [];\r\n\r\n const actionButtons = actions\r\n .filter((action) => !action.show || action.show(node))\r\n .map((action) => {\r\n const buttonProps = {\r\n size: \"tiny\" as const,\r\n type: action.type || (\"default\" as const),\r\n secondary: true,\r\n style: { padding: \"4px 6px\", minWidth: \"24px\", height: \"24px\" },\r\n onClick: (e: Event) => {\r\n e.stopPropagation();\r\n handleNodeAction(action.key, node);\r\n },\r\n };\r\n const iconEl = h(C_Icon, {\r\n name: action.icon,\r\n size: 12,\r\n title: action.text,\r\n });\r\n\r\n if (action.confirm) {\r\n return h(\r\n NPopconfirm,\r\n { onPositiveClick: () => handleNodeAction(action.key, node) },\r\n {\r\n trigger: () =>\r\n h(\r\n NButton,\r\n {\r\n ...buttonProps,\r\n onClick: (e: Event) => e.stopPropagation(),\r\n },\r\n { icon: () => iconEl },\r\n ),\r\n default: () => action.confirm,\r\n },\r\n );\r\n }\r\n return h(NButton, buttonProps, { icon: () => iconEl });\r\n });\r\n\r\n return h(\r\n \"div\",\r\n {\r\n class: \"tree-actions\",\r\n style: {\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n marginLeft: \"8px\",\r\n opacity: \"0\",\r\n transition: \"opacity 0.2s ease\",\r\n },\r\n },\r\n [h(NSpace, { size: 4 }, { default: () => actionButtons })],\r\n );\r\n };\r\n\r\n const toggleExpandAll = (): void => {\r\n if (isAllExpanded.value) {\r\n expandedKeys.value = [];\r\n isAllExpanded.value = false;\r\n } else {\r\n expandedKeys.value = getAllKeys(props.data);\r\n isAllExpanded.value = true;\r\n }\r\n };\r\n\r\n const handleExpandedKeysChange = (keys: (string | number)[]): void => {\r\n expandedKeys.value = keys;\r\n isAllExpanded.value = keys.length === getAllKeys(props.data).length;\r\n };\r\n\r\n const handleSelectedKeysChange = (keys: (string | number)[]): void => {\r\n selectedKeys.value = keys;\r\n emit(\"node-select\", selectedNode.value, keys);\r\n };\r\n\r\n const handleDrop = (info: DropInfo): void => emit(\"node-drop\", info);\r\n\r\n const handleNodeAction = (action: string, node: TreeNodeData): void => {\r\n if (action === \"add\") {\r\n emit(\"add\", node);\r\n } else {\r\n emit(\"node-action\", action, node);\r\n }\r\n };\r\n\r\n const handleAdd = (): void => emit(\"add\");\r\n const handleRefresh = (): void => emit(\"refresh\");\r\n\r\n const expose: TreeExpose = {\r\n expandAll: () => {\r\n expandedKeys.value = getAllKeys(props.data);\r\n isAllExpanded.value = true;\r\n },\r\n collapseAll: () => {\r\n expandedKeys.value = [];\r\n isAllExpanded.value = false;\r\n },\r\n selectNode: (key: string | number) => {\r\n selectedKeys.value = [key];\r\n },\r\n getSelectedNode: () => selectedNode.value,\r\n expandedKeys,\r\n selectedKeys,\r\n };\r\n\r\n return {\r\n internalSearchPattern,\r\n expandedKeys,\r\n selectedKeys,\r\n isAllExpanded,\r\n treeData,\r\n currentSearchPattern,\r\n renderPrefix,\r\n renderLabel,\r\n renderSuffix,\r\n toggleExpandAll,\r\n handleExpandedKeysChange,\r\n handleSelectedKeysChange,\r\n handleDrop,\r\n handleAdd,\r\n handleRefresh,\r\n expose,\r\n };\r\n}\r\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-27\n * @Description: 树型组件 — 薄 UI 壳,逻辑由 useTreeOperations 驱动\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-tree\">\n <div v-if=\"showToolbar\" class=\"c-tree-toolbar\">\n <div class=\"toolbar-left\">\n <NInput\n v-if=\"searchable\"\n v-model:value=\"internalSearchPattern\"\n :placeholder=\"searchPlaceholder\"\n clearable\n class=\"search-input\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" :size=\"16\" />\n </template>\n </NInput>\n </div>\n <div class=\"toolbar-right\">\n <slot name=\"toolbar-actions\">\n <NButton v-if=\"addable\" type=\"primary\" @click=\"handleAdd()\">\n <template #icon>\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\n </template>\n {{ addText }}\n </NButton>\n <NButton @click=\"toggleExpandAll\">\n <template #icon>\n <C_Icon name=\"mdi:file-tree\" :size=\"16\" />\n </template>\n {{ isAllExpanded ? \"收起全部\" : \"展开全部\" }}\n </NButton>\n <NButton v-if=\"refreshable\" @click=\"handleRefresh\">\n <template #icon>\n <C_Icon name=\"mdi:refresh\" :size=\"16\" />\n </template>\n 刷新\n </NButton>\n </slot>\n </div>\n </div>\n\n <div class=\"c-tree-container\">\n <NTree\n :data=\"treeData\"\n :pattern=\"currentSearchPattern\"\n :expanded-keys=\"expandedKeys\"\n :selected-keys=\"selectedKeys\"\n :draggable=\"draggable\"\n :show-line=\"showLine\"\n :render-prefix=\"renderPrefix\"\n :render-suffix=\"renderSuffix\"\n :render-label=\"renderLabel\"\n :key-field=\"keyField\"\n :label-field=\"labelField\"\n :children-field=\"childrenField\"\n @update:expanded-keys=\"handleExpandedKeysChange\"\n @update:selected-keys=\"handleSelectedKeysChange\"\n @drop=\"handleDrop\"\n class=\"tree-instance\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { NInput, NButton, NTree } from \"naive-ui\";\nimport C_Icon from \"../C_Icon/index.vue\";\nimport { useTreeOperations } from \"./composables/useTreeOperations\";\nimport type { TreeProps, TreeEmits, TreeExpose } from \"./types\";\n\ndefineOptions({ name: \"C_Tree\" });\n\nconst props = withDefaults(defineProps<TreeProps>(), {\n mode: \"custom\",\n keyField: \"id\",\n labelField: \"name\",\n childrenField: \"children\",\n searchPattern: \"\",\n searchable: true,\n searchPlaceholder: \"搜索...\",\n draggable: false,\n showLine: true,\n showToolbar: true,\n addable: true,\n addText: \"新增\",\n refreshable: true,\n iconField: \"icon\",\n iconConfig: () => ({\n default: \"mdi:circle-outline\",\n typeMap: {},\n colorMap: {},\n }),\n statusConfigs: () => [],\n actions: () => [],\n defaultExpandAll: false,\n defaultExpandedKeys: () => [],\n defaultSelectedKeys: () => [],\n});\n\nconst emit = defineEmits<TreeEmits>();\n\nconst {\n internalSearchPattern,\n expandedKeys,\n selectedKeys,\n isAllExpanded,\n treeData,\n currentSearchPattern,\n renderPrefix,\n renderLabel,\n renderSuffix,\n toggleExpandAll,\n handleExpandedKeysChange,\n handleSelectedKeysChange,\n handleDrop,\n handleAdd,\n handleRefresh,\n expose,\n} = useTreeOperations(props, emit);\n\ndefineExpose<TreeExpose>(expose);\n</script>\n\n<style lang=\"scss\" scoped>\n@use \"./index.scss\";\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2025-06-27\n * @Description: 树型组件 — 薄 UI 壳,逻辑由 useTreeOperations 驱动\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2025 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-tree\">\n <div v-if=\"showToolbar\" class=\"c-tree-toolbar\">\n <div class=\"toolbar-left\">\n <NInput\n v-if=\"searchable\"\n v-model:value=\"internalSearchPattern\"\n :placeholder=\"searchPlaceholder\"\n clearable\n class=\"search-input\"\n >\n <template #prefix>\n <C_Icon name=\"mdi:magnify\" :size=\"16\" />\n </template>\n </NInput>\n </div>\n <div class=\"toolbar-right\">\n <slot name=\"toolbar-actions\">\n <NButton v-if=\"addable\" type=\"primary\" @click=\"handleAdd()\">\n <template #icon>\n <C_Icon name=\"mdi:plus\" :size=\"16\" />\n </template>\n {{ addText }}\n </NButton>\n <NButton @click=\"toggleExpandAll\">\n <template #icon>\n <C_Icon name=\"mdi:file-tree\" :size=\"16\" />\n </template>\n {{ isAllExpanded ? \"收起全部\" : \"展开全部\" }}\n </NButton>\n <NButton v-if=\"refreshable\" @click=\"handleRefresh\">\n <template #icon>\n <C_Icon name=\"mdi:refresh\" :size=\"16\" />\n </template>\n 刷新\n </NButton>\n </slot>\n </div>\n </div>\n\n <div class=\"c-tree-container\">\n <NTree\n :data=\"treeData\"\n :pattern=\"currentSearchPattern\"\n :expanded-keys=\"expandedKeys\"\n :selected-keys=\"selectedKeys\"\n :draggable=\"draggable\"\n :show-line=\"showLine\"\n :render-prefix=\"renderPrefix\"\n :render-suffix=\"renderSuffix\"\n :render-label=\"renderLabel\"\n :key-field=\"keyField\"\n :label-field=\"labelField\"\n :children-field=\"childrenField\"\n @update:expanded-keys=\"handleExpandedKeysChange\"\n @update:selected-keys=\"handleSelectedKeysChange\"\n @drop=\"handleDrop\"\n class=\"tree-instance\"\n />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { NInput, NButton, NTree } from \"naive-ui\";\nimport C_Icon from \"../C_Icon/index.vue\";\nimport { useTreeOperations } from \"./composables/useTreeOperations\";\nimport type { TreeProps, TreeEmits, TreeExpose } from \"./types\";\n\ndefineOptions({ name: \"C_Tree\" });\n\nconst props = withDefaults(defineProps<TreeProps>(), {\n mode: \"custom\",\n keyField: \"id\",\n labelField: \"name\",\n childrenField: \"children\",\n searchPattern: \"\",\n searchable: true,\n searchPlaceholder: \"搜索...\",\n draggable: false,\n showLine: true,\n showToolbar: true,\n addable: true,\n addText: \"新增\",\n refreshable: true,\n iconField: \"icon\",\n iconConfig: () => ({\n default: \"mdi:circle-outline\",\n typeMap: {},\n colorMap: {},\n }),\n statusConfigs: () => [],\n actions: () => [],\n defaultExpandAll: false,\n defaultExpandedKeys: () => [],\n defaultSelectedKeys: () => [],\n});\n\nconst emit = defineEmits<TreeEmits>();\n\nconst {\n internalSearchPattern,\n expandedKeys,\n selectedKeys,\n isAllExpanded,\n treeData,\n currentSearchPattern,\n renderPrefix,\n renderLabel,\n renderSuffix,\n toggleExpandAll,\n handleExpandedKeysChange,\n handleSelectedKeysChange,\n handleDrop,\n handleAdd,\n handleRefresh,\n expose,\n} = useTreeOperations(props, emit);\n\ndefineExpose<TreeExpose>(expose);\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_Upload-CHjiVVh7.css","names":[],"sources":["../src/components/C_Upload/components/UploadArea.vue?vue&type=style&index=0&scoped=2e419e04&lang.scss","../src/components/C_Upload/components/FileList.vue?vue&type=style&index=0&scoped=8e4ad054&lang.scss","../src/components/C_Upload/components/ImagePreview.vue?vue&type=style&index=0&scoped=76efbe9c&lang.scss","../src/components/C_Upload/index.vue?vue&type=style&index=0&scoped=ec48178d&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.upload-area[data-v-2e419e04] {\n position: relative;\n box-sizing: border-box;\n padding: 32px 24px;\n border: 2px dashed var(--border-color);\n border-radius: 10px;\n cursor: pointer;\n background: var(--card-color);\n transition: all 0.25s ease;\n text-align: center;\n}\n.upload-area[data-v-2e419e04]:hover:not(.upload-area--disabled) {\n border-color: var(--primary-color);\n background: color-mix(in srgb, var(--primary-color) 4%, var(--card-color));\n}\n.upload-area--drag-over[data-v-2e419e04] {\n border-color: var(--primary-color);\n border-style: solid;\n background: color-mix(in srgb, var(--primary-color) 8%, var(--card-color));\n}\n.upload-area--drag-over .upload-area__icon[data-v-2e419e04] {\n color: var(--primary-color);\n transform: scale(1.1);\n}\n.upload-area--disabled[data-v-2e419e04] {\n cursor: not-allowed;\n opacity: 0.5;\n}\n.upload-area__input[data-v-2e419e04] {\n display: none;\n}\n.upload-area__default[data-v-2e419e04] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n align-items: center;\n}\n.upload-area__icon[data-v-2e419e04] {\n font-size: 48px;\n color: var(--text-color-3);\n transition: all 0.25s ease;\n}\n.upload-area__text[data-v-2e419e04] {\n font-size: 14px;\n color: var(--text-color-2);\n line-height: 1.6;\n}\n.upload-area__text kbd[data-v-2e419e04] {\n padding: 1px 5px;\n font-size: 12px;\n border: 1px solid var(--border-color);\n border-radius: 3px;\n background: var(--body-color);\n}\n.upload-area__tip[data-v-2e419e04] {\n font-size: 12px;\n color: var(--text-color-4);\n}","/* unplugin-vue-components disabled */.file-list[data-v-8e4ad054] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 12px;\n}\n.file-list__item[data-v-8e4ad054] {\n display: flex;\n gap: 12px;\n align-items: flex-start;\n padding: 10px 12px;\n border-radius: 8px;\n border: 1px solid var(--border-color);\n background: var(--card-color);\n transition: all 0.25s ease;\n}\n.file-list__item[data-v-8e4ad054]:hover {\n border-color: color-mix(in srgb, var(--primary-color) 40%, var(--border-color));\n box-shadow: 0 1px 6px rgba(0, 0, 0, 0.04);\n}\n.file-list__item--error[data-v-8e4ad054] {\n border-color: var(--error-color);\n background: color-mix(in srgb, var(--error-color) 4%, var(--card-color));\n}\n.file-list__item--success[data-v-8e4ad054], .file-list__item--instant[data-v-8e4ad054] {\n border-color: color-mix(in srgb, var(--success-color) 30%, var(--border-color));\n}\n.file-list__thumb[data-v-8e4ad054] {\n flex-shrink: 0;\n width: 40px;\n height: 40px;\n border-radius: 6px;\n overflow: hidden;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--body-color);\n}\n.file-list__thumb-img[data-v-8e4ad054] {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.file-list__thumb-icon[data-v-8e4ad054] {\n font-size: 22px;\n color: var(--text-color-3);\n}\n.file-list__info[data-v-8e4ad054] {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.file-list__name[data-v-8e4ad054] {\n font-size: 13px;\n font-weight: 500;\n color: var(--text-color-1);\n}\n.file-list__meta[data-v-8e4ad054] {\n display: flex;\n gap: 8px;\n align-items: center;\n font-size: 12px;\n}\n.file-list__size[data-v-8e4ad054] {\n color: var(--text-color-3);\n}\n.file-list__instant[data-v-8e4ad054] {\n font-size: 14px;\n}\n.file-list__chunk-info[data-v-8e4ad054] {\n font-size: 11px;\n color: var(--text-color-4);\n}\n.file-list__error[data-v-8e4ad054] {\n font-size: 12px;\n color: var(--error-color);\n}\n.file-list__actions[data-v-8e4ad054] {\n flex-shrink: 0;\n display: flex;\n gap: 4px;\n align-items: center;\n}\n\n/* ─── 过渡动画 ─────────────────────────────── */\n.file-list-enter-active[data-v-8e4ad054],\n.file-list-leave-active[data-v-8e4ad054] {\n transition: all 0.3s ease;\n}\n.file-list-enter-from[data-v-8e4ad054] {\n opacity: 0;\n transform: translateX(-20px);\n}\n.file-list-leave-to[data-v-8e4ad054] {\n opacity: 0;\n transform: translateX(20px);\n}\n.file-list-move[data-v-8e4ad054] {\n transition: transform 0.3s ease;\n}","/* unplugin-vue-components disabled */.image-preview[data-v-76efbe9c] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));\n gap: 10px;\n margin-top: 12px;\n /* 上传中始终显示遮罩 */\n}\n.image-preview__item[data-v-76efbe9c] {\n position: relative;\n aspect-ratio: 1;\n border-radius: 8px;\n overflow: hidden;\n border: 1px solid var(--border-color);\n background: var(--body-color);\n}\n.image-preview__item--error[data-v-76efbe9c] {\n border-color: var(--error-color);\n}\n.image-preview__item--success[data-v-76efbe9c], .image-preview__item--instant[data-v-76efbe9c] {\n border-color: color-mix(in srgb, var(--success-color) 30%, var(--border-color));\n}\n.image-preview__item:hover .image-preview__overlay[data-v-76efbe9c] {\n opacity: 1;\n}\n.image-preview__img[data-v-76efbe9c] {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.image-preview__overlay[data-v-76efbe9c] {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n background: rgba(0, 0, 0, 0.45);\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n.image-preview__item--uploading .image-preview__overlay[data-v-76efbe9c], .image-preview__item--hashing .image-preview__overlay[data-v-76efbe9c], .image-preview__item--error .image-preview__overlay[data-v-76efbe9c] {\n opacity: 1;\n}\n.image-preview__progress[data-v-76efbe9c] .n-progress {\n --n-fill-color: #fff;\n}\n.image-preview__status-icon[data-v-76efbe9c] {\n font-size: 28px;\n}\n.image-preview__status-icon--success[data-v-76efbe9c] {\n color: var(--success-color);\n}\n.image-preview__status-icon--error[data-v-76efbe9c] {\n color: var(--error-color);\n}\n.image-preview__actions[data-v-76efbe9c] {\n display: flex;\n gap: 6px;\n}\n.image-preview__name[data-v-76efbe9c] {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 2px 6px;\n font-size: 11px;\n color: #fff;\n background: linear-gradient(transparent, rgba(0, 0, 0, 0.6));\n text-align: center;\n}","/* unplugin-vue-components disabled */.c-upload[data-v-ec48178d] {\n --c-upload-progress-bg: var(--body-color, #fff);\n --c-upload-progress-border: var(--border-color, #e5e7eb);\n --c-upload-progress-radius: 8px;\n --c-upload-label-color: var(--text-color-3, #999);\n --c-upload-label-size: 12px;\n width: 100%;\n}\n.c-upload__total-progress[data-v-ec48178d] {\n margin-top: 12px;\n padding: 8px 12px;\n border-radius: var(--c-upload-progress-radius);\n background: var(--c-upload-progress-bg);\n border: 1px solid var(--c-upload-progress-border);\n}\n.c-upload__total-label[data-v-ec48178d] {\n display: flex;\n justify-content: space-between;\n margin-bottom: 6px;\n font-size: var(--c-upload-label-size);\n color: var(--c-upload-label-color);\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACzDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACrGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACvEA;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_Upload-DE3OLsZD.css","names":[],"sources":["../src/components/C_Upload/components/UploadArea.vue?vue&type=style&index=0&scoped=2e419e04&lang.scss","../src/components/C_Upload/components/FileList.vue?vue&type=style&index=0&scoped=8e4ad054&lang.scss","../src/components/C_Upload/components/ImagePreview.vue?vue&type=style&index=0&scoped=76efbe9c&lang.scss","../src/components/C_Upload/index.vue?vue&type=style&index=0&scoped=ec48178d&lang.scss"],"sourcesContent":["/* unplugin-vue-components disabled */.upload-area[data-v-2e419e04] {\n position: relative;\n box-sizing: border-box;\n padding: 32px 24px;\n border: 2px dashed var(--border-color);\n border-radius: 10px;\n cursor: pointer;\n background: var(--card-color);\n transition: all 0.25s ease;\n text-align: center;\n}\n.upload-area[data-v-2e419e04]:hover:not(.upload-area--disabled) {\n border-color: var(--primary-color);\n background: color-mix(in srgb, var(--primary-color) 4%, var(--card-color));\n}\n.upload-area--drag-over[data-v-2e419e04] {\n border-color: var(--primary-color);\n border-style: solid;\n background: color-mix(in srgb, var(--primary-color) 8%, var(--card-color));\n}\n.upload-area--drag-over .upload-area__icon[data-v-2e419e04] {\n color: var(--primary-color);\n transform: scale(1.1);\n}\n.upload-area--disabled[data-v-2e419e04] {\n cursor: not-allowed;\n opacity: 0.5;\n}\n.upload-area__input[data-v-2e419e04] {\n display: none;\n}\n.upload-area__default[data-v-2e419e04] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n align-items: center;\n}\n.upload-area__icon[data-v-2e419e04] {\n font-size: 48px;\n color: var(--text-color-3);\n transition: all 0.25s ease;\n}\n.upload-area__text[data-v-2e419e04] {\n font-size: 14px;\n color: var(--text-color-2);\n line-height: 1.6;\n}\n.upload-area__text kbd[data-v-2e419e04] {\n padding: 1px 5px;\n font-size: 12px;\n border: 1px solid var(--border-color);\n border-radius: 3px;\n background: var(--body-color);\n}\n.upload-area__tip[data-v-2e419e04] {\n font-size: 12px;\n color: var(--text-color-4);\n}","/* unplugin-vue-components disabled */.file-list[data-v-8e4ad054] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 12px;\n}\n.file-list__item[data-v-8e4ad054] {\n display: flex;\n gap: 12px;\n align-items: flex-start;\n padding: 10px 12px;\n border-radius: 8px;\n border: 1px solid var(--border-color);\n background: var(--card-color);\n transition: all 0.25s ease;\n}\n.file-list__item[data-v-8e4ad054]:hover {\n border-color: color-mix(in srgb, var(--primary-color) 40%, var(--border-color));\n box-shadow: 0 1px 6px rgba(0, 0, 0, 0.04);\n}\n.file-list__item--error[data-v-8e4ad054] {\n border-color: var(--error-color);\n background: color-mix(in srgb, var(--error-color) 4%, var(--card-color));\n}\n.file-list__item--success[data-v-8e4ad054], .file-list__item--instant[data-v-8e4ad054] {\n border-color: color-mix(in srgb, var(--success-color) 30%, var(--border-color));\n}\n.file-list__thumb[data-v-8e4ad054] {\n flex-shrink: 0;\n width: 40px;\n height: 40px;\n border-radius: 6px;\n overflow: hidden;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--body-color);\n}\n.file-list__thumb-img[data-v-8e4ad054] {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.file-list__thumb-icon[data-v-8e4ad054] {\n font-size: 22px;\n color: var(--text-color-3);\n}\n.file-list__info[data-v-8e4ad054] {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.file-list__name[data-v-8e4ad054] {\n font-size: 13px;\n font-weight: 500;\n color: var(--text-color-1);\n}\n.file-list__meta[data-v-8e4ad054] {\n display: flex;\n gap: 8px;\n align-items: center;\n font-size: 12px;\n}\n.file-list__size[data-v-8e4ad054] {\n color: var(--text-color-3);\n}\n.file-list__instant[data-v-8e4ad054] {\n font-size: 14px;\n}\n.file-list__chunk-info[data-v-8e4ad054] {\n font-size: 11px;\n color: var(--text-color-4);\n}\n.file-list__error[data-v-8e4ad054] {\n font-size: 12px;\n color: var(--error-color);\n}\n.file-list__actions[data-v-8e4ad054] {\n flex-shrink: 0;\n display: flex;\n gap: 4px;\n align-items: center;\n}\n\n/* ─── 过渡动画 ─────────────────────────────── */\n.file-list-enter-active[data-v-8e4ad054],\n.file-list-leave-active[data-v-8e4ad054] {\n transition: all 0.3s ease;\n}\n.file-list-enter-from[data-v-8e4ad054] {\n opacity: 0;\n transform: translateX(-20px);\n}\n.file-list-leave-to[data-v-8e4ad054] {\n opacity: 0;\n transform: translateX(20px);\n}\n.file-list-move[data-v-8e4ad054] {\n transition: transform 0.3s ease;\n}","/* unplugin-vue-components disabled */.image-preview[data-v-76efbe9c] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));\n gap: 10px;\n margin-top: 12px;\n /* 上传中始终显示遮罩 */\n}\n.image-preview__item[data-v-76efbe9c] {\n position: relative;\n aspect-ratio: 1;\n border-radius: 8px;\n overflow: hidden;\n border: 1px solid var(--border-color);\n background: var(--body-color);\n}\n.image-preview__item--error[data-v-76efbe9c] {\n border-color: var(--error-color);\n}\n.image-preview__item--success[data-v-76efbe9c], .image-preview__item--instant[data-v-76efbe9c] {\n border-color: color-mix(in srgb, var(--success-color) 30%, var(--border-color));\n}\n.image-preview__item:hover .image-preview__overlay[data-v-76efbe9c] {\n opacity: 1;\n}\n.image-preview__img[data-v-76efbe9c] {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n.image-preview__overlay[data-v-76efbe9c] {\n position: absolute;\n inset: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n background: rgba(0, 0, 0, 0.45);\n opacity: 0;\n transition: opacity 0.2s ease;\n}\n.image-preview__item--uploading .image-preview__overlay[data-v-76efbe9c], .image-preview__item--hashing .image-preview__overlay[data-v-76efbe9c], .image-preview__item--error .image-preview__overlay[data-v-76efbe9c] {\n opacity: 1;\n}\n.image-preview__progress[data-v-76efbe9c] .n-progress {\n --n-fill-color: #fff;\n}\n.image-preview__status-icon[data-v-76efbe9c] {\n font-size: 28px;\n}\n.image-preview__status-icon--success[data-v-76efbe9c] {\n color: var(--success-color);\n}\n.image-preview__status-icon--error[data-v-76efbe9c] {\n color: var(--error-color);\n}\n.image-preview__actions[data-v-76efbe9c] {\n display: flex;\n gap: 6px;\n}\n.image-preview__name[data-v-76efbe9c] {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n padding: 2px 6px;\n font-size: 11px;\n color: #fff;\n background: linear-gradient(transparent, rgba(0, 0, 0, 0.6));\n text-align: center;\n}","/* unplugin-vue-components disabled */.c-upload[data-v-ec48178d] {\n --c-upload-progress-bg: var(--body-color, #fff);\n --c-upload-progress-border: var(--border-color, #e5e7eb);\n --c-upload-progress-radius: 8px;\n --c-upload-label-color: var(--text-color-3, #999);\n --c-upload-label-size: 12px;\n width: 100%;\n}\n.c-upload__total-progress[data-v-ec48178d] {\n margin-top: 12px;\n padding: 8px 12px;\n border-radius: var(--c-upload-progress-radius);\n background: var(--c-upload-progress-bg);\n border: 1px solid var(--c-upload-progress-border);\n}\n.c-upload__total-label[data-v-ec48178d] {\n display: flex;\n justify-content: space-between;\n margin-bottom: 6px;\n font-size: var(--c-upload-label-size);\n color: var(--c-upload-label-color);\n}"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACzDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACrGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;ACvEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
|
package/dist/C_Upload.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"C_Upload.js","names":["disabled","accept","multiple","directory","pasteable","tip","fileList","showThumbnail","hashProgress"],"sources":["../src/components/C_Upload/constants.ts","../src/components/C_Upload/composables/useFileHash.ts","../src/components/C_Upload/composables/useChunkUpload.ts","../src/components/C_Upload/composables/useUploadQueue.ts","../src/components/C_Upload/composables/useUploadCore.ts","../src/components/C_Upload/composables/useDragDrop.ts","../src/components/C_Upload/components/UploadArea.vue","../src/components/C_Upload/components/UploadArea.vue","../src/components/C_Upload/components/FileList.vue","../src/components/C_Upload/components/FileList.vue","../src/components/C_Upload/components/ImagePreview.vue","../src/components/C_Upload/components/ImagePreview.vue","../src/components/C_Upload/index.vue","../src/components/C_Upload/index.vue"],"sourcesContent":["/**\r\n * C_Upload 组件常量\r\n */\r\n\r\n/** 默认分片大小 2MB */\r\nexport const DEFAULT_CHUNK_SIZE = 2 * 1024 * 1024;\r\n\r\n/** 默认最大并发数 */\r\nexport const DEFAULT_CONCURRENCY = 3;\r\n\r\n/** 默认最大文件数 */\r\nexport const DEFAULT_MAX_COUNT = 10;\r\n\r\n/** 大文件阈值(超过此大小自动启用分片) */\r\nexport const LARGE_FILE_THRESHOLD = 10 * 1024 * 1024;\r\n\r\n/** 状态文案映射 */\r\nexport const STATUS_TEXT: Record<string, string> = {\r\n pending: \"等待上传\",\r\n hashing: \"计算校验…\",\r\n uploading: \"上传中…\",\r\n success: \"上传成功\",\r\n error: \"上传失败\",\r\n paused: \"已暂停\",\r\n instant: \"秒传成功\",\r\n};\r\n\r\n/** 状态颜色映射(Naive UI NTag type) */\r\nexport const STATUS_TYPE: Record<string, string> = {\r\n pending: \"default\",\r\n hashing: \"info\",\r\n uploading: \"info\",\r\n success: \"success\",\r\n error: \"error\",\r\n paused: \"warning\",\r\n instant: \"success\",\r\n};\r\n\r\n/** 文件图标映射 */\r\nexport const FILE_ICON_MAP: Record<string, string> = {\r\n \"image/\": \"mdi:file-image-outline\",\r\n \"video/\": \"mdi:file-video-outline\",\r\n \"audio/\": \"mdi:file-music-outline\",\r\n \"application/pdf\": \"mdi:file-pdf-box\",\r\n \"application/zip\": \"mdi:folder-zip-outline\",\r\n \"application/x-rar\": \"mdi:folder-zip-outline\",\r\n \"application/vnd.ms-excel\": \"mdi:file-excel-outline\",\r\n \"application/vnd.openxmlformats-officedocument.spreadsheetml\":\r\n \"mdi:file-excel-outline\",\r\n \"application/msword\": \"mdi:file-word-outline\",\r\n \"application/vnd.openxmlformats-officedocument.wordprocessingml\":\r\n \"mdi:file-word-outline\",\r\n \"text/\": \"mdi:file-document-outline\",\r\n default: \"mdi:file-outline\",\r\n};\r\n\r\n/** 根据 MIME 类型获取图标 */\r\nexport function getFileIcon(type: string): string {\r\n for (const [key, icon] of Object.entries(FILE_ICON_MAP)) {\r\n if (key !== \"default\" && type.startsWith(key)) return icon;\r\n }\r\n return FILE_ICON_MAP.default;\r\n}\r\n\r\n/** 格式化文件大小 */\r\nexport function formatFileSize(bytes: number): string {\r\n if (bytes === 0) return \"0 B\";\r\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\r\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\r\n return `${(bytes / 1024 ** i).toFixed(i > 0 ? 1 : 0)} ${units[i]}`;\r\n}\r\n","/**\r\n * 文件哈希计算(Web Worker + spark-md5)\r\n */\r\n\r\nimport { ref, readonly, type Ref } from \"vue\";\r\n\r\n/**\r\n * 文件哈希计算\r\n *\r\n * 使用 spark-md5 在 Web Worker 中对文件分片计算 MD5 hash,\r\n * 不阻塞主线程,支持进度回调。\r\n *\r\n * @param chunkSize 分片大小(bytes),也用于哈希分块读取\r\n */\r\nexport function useFileHash(chunkSize: Ref<number>) {\r\n const hashing = ref(false);\r\n const hashProgress = ref(0);\r\n\r\n /**\r\n * 计算文件 hash\r\n * @param file 原生 File 对象\r\n * @returns MD5 hash 字符串\r\n */\r\n async function calculateHash(file: File): Promise<string> {\r\n hashing.value = true;\r\n hashProgress.value = 0;\r\n\r\n return new Promise((resolve, reject) => {\r\n // 使用 inline Web Worker(避免额外文件)\r\n const workerCode = `\r\n self.importScripts('https://cdn.jsdelivr.net/npm/spark-md5@3.0.2/spark-md5.min.js');\r\n\r\n self.onmessage = function(e) {\r\n const { chunks } = e.data;\r\n const spark = new self.SparkMD5.ArrayBuffer();\r\n let current = 0;\r\n\r\n function processNext() {\r\n if (current >= chunks.length) {\r\n self.postMessage({ type: 'done', hash: spark.end() });\r\n return;\r\n }\r\n const reader = new FileReaderSync();\r\n const buffer = reader.readAsArrayBuffer(chunks[current]);\r\n spark.append(buffer);\r\n current++;\r\n self.postMessage({ type: 'progress', percent: Math.round((current / chunks.length) * 100) });\r\n processNext();\r\n }\r\n\r\n processNext();\r\n };\r\n `;\r\n\r\n // 将文件分片\r\n const size = chunkSize.value;\r\n const chunks: Blob[] = [];\r\n let offset = 0;\r\n while (offset < file.size) {\r\n chunks.push(file.slice(offset, offset + size));\r\n offset += size;\r\n }\r\n\r\n // 如果浏览器不支持 Worker(SSR / 旧环境),走主线程降级\r\n if (typeof Worker === \"undefined\") {\r\n computeHashMainThread(file, chunks)\r\n .then(resolve)\r\n .catch(reject)\r\n .finally(() => {\r\n hashing.value = false;\r\n });\r\n return;\r\n }\r\n\r\n try {\r\n const blob = new Blob([workerCode], { type: \"application/javascript\" });\r\n const workerUrl = URL.createObjectURL(blob);\r\n const worker = new Worker(workerUrl);\r\n\r\n worker.onmessage = (e: MessageEvent) => {\r\n const { type, hash, percent } = e.data;\r\n if (type === \"progress\") {\r\n hashProgress.value = percent;\r\n } else if (type === \"done\") {\r\n hashing.value = false;\r\n worker.terminate();\r\n URL.revokeObjectURL(workerUrl);\r\n resolve(hash);\r\n }\r\n };\r\n\r\n worker.onerror = () => {\r\n hashing.value = false;\r\n worker.terminate();\r\n URL.revokeObjectURL(workerUrl);\r\n // 降级到主线程\r\n computeHashMainThread(file, chunks).then(resolve).catch(reject);\r\n };\r\n\r\n worker.postMessage({ chunks });\r\n } catch {\r\n // Worker 创建失败,降级\r\n computeHashMainThread(file, chunks)\r\n .then(resolve)\r\n .catch(reject)\r\n .finally(() => {\r\n hashing.value = false;\r\n });\r\n }\r\n });\r\n }\r\n\r\n /** 主线程降级计算 */\r\n async function computeHashMainThread(\r\n _file: File,\r\n chunks: Blob[],\r\n ): Promise<string> {\r\n const SparkMD5 = (await import(\"spark-md5\" as string)).default;\r\n const spark = new SparkMD5.ArrayBuffer();\r\n\r\n for (let i = 0; i < chunks.length; i++) {\r\n const buffer = await chunks[i].arrayBuffer();\r\n spark.append(buffer);\r\n hashProgress.value = Math.round(((i + 1) / chunks.length) * 100);\r\n }\r\n\r\n hashing.value = false;\r\n return spark.end();\r\n }\r\n\r\n return {\r\n /** 是否正在计算 hash */\r\n hashing: readonly(hashing),\r\n /** 哈希计算进度(0-100) */\r\n hashProgress: readonly(hashProgress),\r\n /** 计算文件 hash */\r\n calculateHash,\r\n };\r\n}\r\n","/**\r\n * 分片上传引擎\r\n */\r\n\r\nimport type { Ref } from \"vue\";\r\nimport type {\r\n UploadChunk,\r\n ChunkProgress,\r\n UploadRequestOptions,\r\n CustomUploadRequest,\r\n UploadedChunksQueryFn,\r\n MergeChunksFn,\r\n} from \"../types\";\r\n\r\ninterface UseChunkUploadOptions {\r\n /** 分片大小 */\r\n chunkSize: Ref<number>;\r\n /** 最大并发数 */\r\n concurrency: Ref<number>;\r\n /** 上传地址 */\r\n action: Ref<string>;\r\n /** 请求头 */\r\n headers: Ref<Record<string, string>>;\r\n /** 附加字段 */\r\n data: Ref<Record<string, any>>;\r\n /** 自定义上传函数 */\r\n customRequest?: Ref<CustomUploadRequest | undefined>;\r\n /** 已上传分片查询 */\r\n uploadedChunksQuery?: Ref<UploadedChunksQueryFn | undefined>;\r\n /** 分片合并函数 */\r\n mergeChunks?: Ref<MergeChunksFn | undefined>;\r\n}\r\n\r\n/**\r\n * 分片上传引擎\r\n *\r\n * 处理大文件分片切割、并发上传、断点续传、分片合并。\r\n */\r\nexport function useChunkUpload(options: UseChunkUploadOptions) {\r\n /** 正在进行的上传中止控制器映射 uid → abort[] */\r\n const abortMap = new Map<string, (() => void)[]>();\r\n\r\n /**\r\n * 将文件切割为分片\r\n */\r\n function createChunks(file: File): UploadChunk[] {\r\n const size = options.chunkSize.value;\r\n const chunks: UploadChunk[] = [];\r\n let index = 0;\r\n let offset = 0;\r\n\r\n while (offset < file.size) {\r\n const end = Math.min(offset + size, file.size);\r\n chunks.push({\r\n index,\r\n blob: file.slice(offset, end),\r\n size: end - offset,\r\n uploaded: false,\r\n });\r\n offset = end;\r\n index++;\r\n }\r\n\r\n return chunks;\r\n }\r\n\r\n /**\r\n * 查询已上传的分片(断点续传)\r\n */\r\n async function queryExistingChunks(hash: string, chunks: UploadChunk[]) {\r\n if (!options.uploadedChunksQuery?.value) return;\r\n\r\n try {\r\n const uploaded = await options.uploadedChunksQuery.value(hash);\r\n for (const idx of uploaded) {\r\n const chunk = chunks[idx];\r\n if (chunk) chunk.uploaded = true;\r\n }\r\n } catch {\r\n // 查询失败,全部重传\r\n }\r\n }\r\n\r\n /**\r\n * 上传单个分片\r\n */\r\n function uploadSingleChunk(ctx: {\r\n chunk: UploadChunk;\r\n hash: string;\r\n file: File;\r\n totalChunks: number;\r\n chunks: UploadChunk[];\r\n totalBytes: number;\r\n abortControllers: (() => void)[];\r\n onProgress: (progress: ChunkProgress) => void;\r\n setUploadedBytes: (bytes: number) => void;\r\n setError: () => void;\r\n }): Promise<void> {\r\n return new Promise<void>((resolve, reject) => {\r\n const requestOptions: UploadRequestOptions = {\r\n action: options.action.value,\r\n headers: options.headers.value,\r\n data: {\r\n ...options.data.value,\r\n hash: ctx.hash,\r\n chunkIndex: ctx.chunk.index,\r\n totalChunks: ctx.totalChunks,\r\n filename: ctx.file.name,\r\n },\r\n file: ctx.chunk.blob,\r\n filename: ctx.file.name,\r\n hash: ctx.hash,\r\n chunkIndex: ctx.chunk.index,\r\n totalChunks: ctx.totalChunks,\r\n onProgress: () => {\r\n /* 分片内部进度可选 */\r\n },\r\n onSuccess: () => {\r\n ctx.chunk.uploaded = true;\r\n ctx.setUploadedBytes(ctx.chunk.size);\r\n ctx.onProgress({\r\n uploadedChunks: ctx.chunks.filter((c) => c.uploaded).length,\r\n totalChunks: ctx.totalChunks,\r\n uploadedBytes: ctx.chunks\r\n .filter((c) => c.uploaded)\r\n .reduce((sum, c) => sum + c.size, 0),\r\n totalBytes: ctx.totalBytes,\r\n });\r\n resolve();\r\n },\r\n onError: (err) => {\r\n ctx.setError();\r\n reject(err);\r\n },\r\n };\r\n\r\n const req = options.customRequest?.value\r\n ? options.customRequest.value(requestOptions)\r\n : defaultUploadRequest(requestOptions);\r\n ctx.abortControllers.push(req.abort);\r\n });\r\n }\r\n\r\n /**\r\n * 执行分片上传\r\n */\r\n async function uploadChunks(params: {\r\n uid: string;\r\n file: File;\r\n hash: string;\r\n onProgress: (progress: ChunkProgress) => void;\r\n onSuccess: (response: any) => void;\r\n onError: (error: Error) => void;\r\n isPaused: () => boolean;\r\n }) {\r\n const { uid, file, hash, onProgress, onSuccess, onError, isPaused } =\r\n params;\r\n const chunks = createChunks(file);\r\n const totalChunks = chunks.length;\r\n const totalBytes = file.size;\r\n let uploadedBytes = 0;\r\n\r\n // 查询已上传分片(断点续传)\r\n await queryExistingChunks(hash, chunks);\r\n uploadedBytes = chunks\r\n .filter((c) => c.uploaded)\r\n .reduce((sum, c) => sum + c.size, 0);\r\n\r\n // 初始进度\r\n onProgress({\r\n uploadedChunks: chunks.filter((c) => c.uploaded).length,\r\n totalChunks,\r\n uploadedBytes,\r\n totalBytes,\r\n });\r\n\r\n // 过滤待上传分片\r\n const pendingChunks = chunks.filter((c) => !c.uploaded);\r\n\r\n if (pendingChunks.length === 0) {\r\n // 全部已上传,直接合并\r\n await mergeAndFinish(hash, file.name, totalChunks, onSuccess, onError);\r\n return;\r\n }\r\n\r\n // 并发控制上传\r\n const abortControllers: (() => void)[] = [];\r\n abortMap.set(uid, abortControllers);\r\n\r\n const concurrency = options.concurrency.value;\r\n let current = 0;\r\n let hasError = false;\r\n\r\n /** 上传下一个分片 */\r\n async function uploadNext(): Promise<void> {\r\n while (current < pendingChunks.length && !hasError && !isPaused()) {\r\n const chunk = pendingChunks[current++];\r\n\r\n await uploadSingleChunk({\r\n chunk,\r\n hash,\r\n file,\r\n totalChunks,\r\n chunks,\r\n totalBytes,\r\n abortControllers,\r\n onProgress,\r\n setUploadedBytes: (bytes: number) => {\r\n uploadedBytes += bytes;\r\n },\r\n setError: () => {\r\n hasError = true;\r\n },\r\n });\r\n }\r\n }\r\n\r\n // 启动并发池\r\n const pool = Array.from(\r\n { length: Math.min(concurrency, pendingChunks.length) },\r\n () => uploadNext(),\r\n );\r\n\r\n try {\r\n await Promise.all(pool);\r\n\r\n if (!hasError && !isPaused()) {\r\n // 全部分片完成 → 合并\r\n await mergeAndFinish(hash, file.name, totalChunks, onSuccess, onError);\r\n }\r\n } catch (err) {\r\n onError(err instanceof Error ? err : new Error(String(err)));\r\n } finally {\r\n abortMap.delete(uid);\r\n }\r\n }\r\n\r\n /** 合并分片 */\r\n async function mergeAndFinish(\r\n hash: string,\r\n filename: string,\r\n totalChunks: number,\r\n onSuccess: (response: any) => void,\r\n onError: (error: Error) => void,\r\n ) {\r\n if (options.mergeChunks?.value) {\r\n try {\r\n const result = await options.mergeChunks.value(\r\n hash,\r\n filename,\r\n totalChunks,\r\n );\r\n onSuccess(result);\r\n } catch (err) {\r\n onError(err instanceof Error ? err : new Error(\"分片合并失败\"));\r\n }\r\n } else {\r\n onSuccess({ message: \"分片上传完成(未配置合并函数)\" });\r\n }\r\n }\r\n\r\n /** 中止指定文件的分片上传 */\r\n function abortUpload(uid: string) {\r\n const controllers = abortMap.get(uid);\r\n controllers?.forEach((abort) => abort());\r\n abortMap.delete(uid);\r\n }\r\n\r\n /** 中止所有 */\r\n function abortAll() {\r\n abortMap.forEach((controllers) => {\r\n controllers.forEach((abort) => abort());\r\n });\r\n abortMap.clear();\r\n }\r\n\r\n return {\r\n createChunks,\r\n uploadChunks,\r\n abortUpload,\r\n abortAll,\r\n };\r\n}\r\n\r\n/** 默认 XMLHttpRequest 上传实现 */\r\nfunction defaultUploadRequest(options: UploadRequestOptions) {\r\n const xhr = new XMLHttpRequest();\r\n\r\n xhr.open(\"POST\", options.action);\r\n\r\n // 设置请求头\r\n if (options.headers) {\r\n Object.entries(options.headers).forEach(([key, value]) => {\r\n xhr.setRequestHeader(key, value);\r\n });\r\n }\r\n\r\n // 上传进度\r\n xhr.upload.addEventListener(\"progress\", (e) => {\r\n if (e.lengthComputable) {\r\n options.onProgress?.(Math.round((e.loaded / e.total) * 100));\r\n }\r\n });\r\n\r\n xhr.addEventListener(\"load\", () => {\r\n if (xhr.status >= 200 && xhr.status < 300) {\r\n let response: any;\r\n try {\r\n response = JSON.parse(xhr.responseText);\r\n } catch {\r\n response = xhr.responseText;\r\n }\r\n options.onSuccess?.(response);\r\n } else {\r\n options.onError?.(new Error(`上传失败: HTTP ${xhr.status}`));\r\n }\r\n });\r\n\r\n xhr.addEventListener(\"error\", () => {\r\n options.onError?.(new Error(\"网络错误\"));\r\n });\r\n\r\n const formData = new FormData();\r\n formData.append(\"file\", options.file, options.filename);\r\n\r\n if (options.data) {\r\n Object.entries(options.data).forEach(([key, value]) => {\r\n formData.append(key, String(value));\r\n });\r\n }\r\n\r\n xhr.send(formData);\r\n\r\n return {\r\n abort: () => xhr.abort(),\r\n };\r\n}\r\n","/**\r\n * 并发队列管理\r\n */\r\n\r\nimport { ref, readonly } from \"vue\";\r\nimport type { Ref } from \"vue\";\r\nimport type {\r\n UploadFileItem,\r\n UploadRequestOptions,\r\n CustomUploadRequest,\r\n} from \"../types\";\r\n\r\ninterface UseUploadQueueOptions {\r\n /** 最大并发数 */\r\n concurrency: Ref<number>;\r\n /** 上传地址 */\r\n action: Ref<string>;\r\n /** 请求头 */\r\n headers: Ref<Record<string, string>>;\r\n /** 附加字段 */\r\n data: Ref<Record<string, any>>;\r\n /** 自定义上传函数 */\r\n customRequest?: Ref<CustomUploadRequest | undefined>;\r\n}\r\n\r\n/**\r\n * 并发队列管理\r\n *\r\n * 控制普通文件(非分片)的并发上传数量,\r\n * 先进先出排队,自动从队列中取出执行。\r\n */\r\nexport function useUploadQueue(options: UseUploadQueueOptions) {\r\n /** 等待队列 */\r\n const queue: UploadFileItem[] = [];\r\n /** 正在上传的数量 */\r\n const activeCount = ref(0);\r\n /** uid → abort */\r\n const abortMap = new Map<string, () => void>();\r\n\r\n /**\r\n * 添加文件到队列\r\n */\r\n function enqueue(\r\n file: UploadFileItem,\r\n onProgress: (uid: string, percent: number) => void,\r\n onSuccess: (uid: string, response: any) => void,\r\n onError: (uid: string, error: Error) => void,\r\n ) {\r\n queue.push(file);\r\n processQueue(onProgress, onSuccess, onError);\r\n }\r\n\r\n /**\r\n * 处理队列\r\n */\r\n function processQueue(\r\n onProgress: (uid: string, percent: number) => void,\r\n onSuccess: (uid: string, response: any) => void,\r\n onError: (uid: string, error: Error) => void,\r\n ) {\r\n while (activeCount.value < options.concurrency.value && queue.length > 0) {\r\n const file = queue.shift()!;\r\n if (!file.raw) continue;\r\n\r\n activeCount.value++;\r\n\r\n const requestOptions: UploadRequestOptions = {\r\n action: options.action.value,\r\n headers: options.headers.value,\r\n data: options.data.value,\r\n file: file.raw,\r\n filename: file.name,\r\n onProgress: (percent) => {\r\n onProgress(file.uid, percent);\r\n },\r\n onSuccess: (response) => {\r\n activeCount.value--;\r\n abortMap.delete(file.uid);\r\n onSuccess(file.uid, response);\r\n processQueue(onProgress, onSuccess, onError);\r\n },\r\n onError: (error) => {\r\n activeCount.value--;\r\n abortMap.delete(file.uid);\r\n onError(file.uid, error);\r\n processQueue(onProgress, onSuccess, onError);\r\n },\r\n };\r\n\r\n if (options.customRequest?.value) {\r\n const { abort } = options.customRequest.value(requestOptions);\r\n abortMap.set(file.uid, abort);\r\n } else {\r\n const { abort } = defaultRequest(requestOptions);\r\n abortMap.set(file.uid, abort);\r\n }\r\n }\r\n }\r\n\r\n /** 中止指定文件 */\r\n function abort(uid: string) {\r\n abortMap.get(uid)?.();\r\n abortMap.delete(uid);\r\n // 从队列移除\r\n const idx = queue.findIndex((f) => f.uid === uid);\r\n if (idx !== -1) queue.splice(idx, 1);\r\n }\r\n\r\n /** 中止所有 */\r\n function abortAll() {\r\n abortMap.forEach((fn) => fn());\r\n abortMap.clear();\r\n queue.length = 0;\r\n activeCount.value = 0;\r\n }\r\n\r\n return {\r\n /** 当前活跃上传数 */\r\n activeCount: readonly(activeCount),\r\n enqueue,\r\n abort,\r\n abortAll,\r\n };\r\n}\r\n\r\n/** 默认 XHR 上传 */\r\nfunction defaultRequest(options: UploadRequestOptions) {\r\n const xhr = new XMLHttpRequest();\r\n xhr.open(\"POST\", options.action);\r\n\r\n if (options.headers) {\r\n Object.entries(options.headers).forEach(([key, value]) => {\r\n xhr.setRequestHeader(key, value);\r\n });\r\n }\r\n\r\n xhr.upload.addEventListener(\"progress\", (e) => {\r\n if (e.lengthComputable) {\r\n options.onProgress?.(Math.round((e.loaded / e.total) * 100));\r\n }\r\n });\r\n\r\n xhr.addEventListener(\"load\", () => {\r\n if (xhr.status >= 200 && xhr.status < 300) {\r\n let response: any;\r\n try {\r\n response = JSON.parse(xhr.responseText);\r\n } catch {\r\n response = xhr.responseText;\r\n }\r\n options.onSuccess?.(response);\r\n } else {\r\n options.onError?.(new Error(`上传失败: HTTP ${xhr.status}`));\r\n }\r\n });\r\n\r\n xhr.addEventListener(\"error\", () => {\r\n options.onError?.(new Error(\"网络错误\"));\r\n });\r\n\r\n const formData = new FormData();\r\n formData.append(\"file\", options.file as File, options.filename);\r\n\r\n if (options.data) {\r\n Object.entries(options.data).forEach(([key, value]) => {\r\n formData.append(key, String(value));\r\n });\r\n }\r\n\r\n xhr.send(formData);\r\n\r\n return { abort: () => xhr.abort() };\r\n}\r\n","/**\n * 核心上传逻辑\n */\n\nimport { ref, computed, reactive, onBeforeUnmount } from \"vue\";\nimport type { UploadFileItem, UploadProps, ChunkProgress } from \"../types\";\nimport { DEFAULT_CHUNK_SIZE, DEFAULT_CONCURRENCY } from \"../constants\";\nimport { useFileHash } from \"./useFileHash\";\nimport { useChunkUpload } from \"./useChunkUpload\";\nimport { useUploadQueue } from \"./useUploadQueue\";\n\n/**\n * 核心上传逻辑\n *\n * 统一调度文件校验、hash 计算、秒传检查、分片/普通上传、\n * 进度汇总和状态管理。\n */\nexport function useUploadCore(props: UploadProps) {\n // ─── 响应式 Props 引用 ────────────────────────\n\n const action = computed(() => props.action ?? \"\");\n const headers = computed(() => props.headers ?? {});\n const data = computed(() => props.data ?? {});\n const chunkSize = computed(() => props.chunkSize ?? DEFAULT_CHUNK_SIZE);\n const concurrency = computed(() => props.concurrency ?? DEFAULT_CONCURRENCY);\n const customRequest = computed(() => props.customRequest);\n const instantCheck = computed(() => props.instantCheck);\n const uploadedChunksQuery = computed(() => props.uploadedChunksQuery);\n const mergeChunks = computed(() => props.mergeChunks);\n\n // ─── 文件列表 ────────────────────────────────\n\n const fileList = ref<UploadFileItem[]>([...(props.defaultFileList ?? [])]);\n const pausedSet = reactive(new Set<string>());\n\n // ─── 子 composable ───────────────────────────\n\n const { hashing, hashProgress, calculateHash } = useFileHash(chunkSize);\n\n const chunkUploader = useChunkUpload({\n chunkSize,\n concurrency,\n action,\n headers,\n data,\n customRequest,\n uploadedChunksQuery,\n mergeChunks,\n });\n\n const uploadQueue = useUploadQueue({\n concurrency,\n action,\n headers,\n data,\n customRequest,\n });\n\n // ─── 总进度 ──────────────────────────────────\n\n const totalPercent = computed(() => {\n const list = fileList.value;\n if (list.length === 0) return 0;\n const total = list.reduce((sum, f) => sum + f.percent, 0);\n return Math.round(total / list.length);\n });\n\n // ─── 文件操作 ────────────────────────────────\n\n /** 生成唯一 ID */\n function generateUid(): string {\n return `upload-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n }\n\n /** 创建缩略图 */\n function createThumbnail(file: File): string | undefined {\n if (file.type.startsWith(\"image/\")) {\n return URL.createObjectURL(file);\n }\n return undefined;\n }\n\n /** 添加文件到列表 */\n function addFiles(files: File[]): UploadFileItem[] {\n const items: UploadFileItem[] = [];\n\n for (const file of files) {\n // 数量限制\n if (\n props.maxCount &&\n fileList.value.length + items.length >= props.maxCount\n ) {\n break;\n }\n\n const item: UploadFileItem = {\n uid: generateUid(),\n name: file.name,\n size: file.size,\n type: file.type,\n status: \"pending\",\n percent: 0,\n raw: file,\n thumbUrl: createThumbnail(file),\n };\n\n items.push(item);\n }\n\n fileList.value.push(...items);\n return items;\n }\n\n /** 更新文件状态 */\n function updateFile(uid: string, patch: Partial<UploadFileItem>) {\n const file = fileList.value.find((f) => f.uid === uid);\n if (file) Object.assign(file, patch);\n }\n\n /** 移除文件 */\n function removeFile(uid: string) {\n chunkUploader.abortUpload(uid);\n uploadQueue.abort(uid);\n pausedSet.delete(uid);\n\n const idx = fileList.value.findIndex((f) => f.uid === uid);\n if (idx !== -1) {\n const file = fileList.value[idx];\n if (file.thumbUrl) URL.revokeObjectURL(file.thumbUrl);\n fileList.value.splice(idx, 1);\n }\n }\n\n /** 清空所有 */\n function clearAll() {\n chunkUploader.abortAll();\n uploadQueue.abortAll();\n pausedSet.clear();\n\n fileList.value.forEach((f) => {\n if (f.thumbUrl) URL.revokeObjectURL(f.thumbUrl);\n });\n fileList.value = [];\n }\n\n // ─── 上传流程 ────────────────────────────────\n\n /** 校验单文件 */\n async function validateFile(file: File): Promise<boolean> {\n // 大小校验\n if (props.maxSize && file.size > props.maxSize) {\n return false;\n }\n\n // 自定义 beforeUpload\n if (props.beforeUpload) {\n const result = await props.beforeUpload(file);\n return result !== false;\n }\n\n return true;\n }\n\n /** 上传单个文件 */\n async function processFile(item: UploadFileItem) {\n if (!item.raw) return\n\n // 前置校验\n const valid = await validateFile(item.raw)\n if (!valid) {\n updateFile(item.uid, { status: 'error', error: '文件校验未通过' })\n return\n }\n\n // 秒传检查(所有模式均支持,只要提供了 instantCheck)\n if (instantCheck.value && !item.hash) {\n updateFile(item.uid, { status: 'hashing' })\n try {\n const hash = await calculateHash(item.raw)\n updateFile(item.uid, { hash })\n const result = await instantCheck.value(hash, item.raw.name)\n if (result.exists) {\n updateFile(item.uid, {\n status: 'instant',\n percent: 100,\n url: result.url,\n })\n return\n }\n } catch {\n // 秒传检查失败,继续正常上传\n }\n }\n\n // 分片模式\n if (props.chunked && item.raw.size > chunkSize.value) {\n await processChunkedUpload(item)\n } else {\n processNormalUpload(item)\n }\n }\n\n /** 分片上传流程 */\n async function processChunkedUpload(item: UploadFileItem) {\n const file = item.raw!\n\n // 1. 计算 hash(如果秒传检查已计算则复用)\n let hash = item.hash\n if (!hash) {\n updateFile(item.uid, { status: 'hashing' })\n try {\n hash = await calculateHash(file)\n updateFile(item.uid, { hash })\n } catch {\n updateFile(item.uid, { status: 'error', error: 'Hash 计算失败' })\n return\n }\n }\n\n // 2. 秒传检查(如果 processFile 未提前检查)\n if (instantCheck.value && item.status !== 'instant') {\n try {\n const result = await instantCheck.value(hash, file.name)\n if (result.exists) {\n updateFile(item.uid, {\n status: 'instant',\n percent: 100,\n url: result.url,\n })\n return\n }\n } catch {\n // 秒传检查失败,继续正常上传\n }\n }\n\n // 3. 分片上传\n updateFile(item.uid, { status: 'uploading' })\n\n await chunkUploader.uploadChunks({\n uid: item.uid,\n file,\n hash,\n onProgress: (progress: ChunkProgress) => {\n const percent = Math.round(\n (progress.uploadedBytes / progress.totalBytes) * 100\n )\n updateFile(item.uid, { percent, chunkProgress: progress })\n },\n onSuccess: (response: any) => {\n updateFile(item.uid, { status: 'success', percent: 100, response })\n },\n onError: (error: Error) => {\n updateFile(item.uid, { status: 'error', error: error.message })\n },\n isPaused: () => pausedSet.has(item.uid),\n })\n }\n\n /** 普通上传 */\n function processNormalUpload(item: UploadFileItem) {\n updateFile(item.uid, { status: \"uploading\" });\n\n uploadQueue.enqueue(\n item,\n (uid, percent) => {\n updateFile(uid, { percent });\n },\n (uid, response) => {\n updateFile(uid, { status: \"success\", percent: 100, response });\n },\n (uid, error) => {\n updateFile(uid, { status: \"error\", error: error.message });\n },\n );\n }\n\n /** 开始上传(上传所有 pending 文件) */\n function startUpload() {\n const pending = fileList.value.filter((f) => f.status === \"pending\");\n pending.forEach(processFile);\n }\n\n /** 暂停所有 */\n function pauseAll() {\n fileList.value.forEach((f) => {\n if (f.status === \"uploading\" || f.status === \"hashing\") {\n pausedSet.add(f.uid);\n updateFile(f.uid, { status: \"paused\" });\n }\n });\n }\n\n /** 恢复所有 */\n function resumeAll() {\n fileList.value.forEach((f) => {\n if (f.status === \"paused\") {\n pausedSet.delete(f.uid);\n updateFile(f.uid, { status: \"pending\" });\n }\n });\n startUpload();\n }\n\n /** 重试单个文件 */\n function retryFile(uid: string) {\n const file = fileList.value.find((f) => f.uid === uid);\n if (file && file.status === \"error\") {\n updateFile(uid, { status: \"pending\", percent: 0, error: undefined });\n processFile(file);\n }\n }\n\n /** 获取成功列表 */\n function getSuccessList(): UploadFileItem[] {\n return fileList.value.filter(\n (f) => f.status === \"success\" || f.status === \"instant\",\n );\n }\n\n // 清理 ObjectURL\n onBeforeUnmount(() => {\n fileList.value.forEach((f) => {\n if (f.thumbUrl) URL.revokeObjectURL(f.thumbUrl);\n });\n });\n\n return {\n /** 文件列表 */\n fileList,\n /** 总进度 */\n totalPercent,\n /** 正在计算 hash */\n hashing,\n /** hash 进度 */\n hashProgress,\n\n /** 添加文件 */\n addFiles,\n /** 移除文件 */\n removeFile,\n /** 清空所有 */\n clearAll,\n /** 开始上传 */\n startUpload,\n /** 暂停所有 */\n pauseAll,\n /** 恢复所有 */\n resumeAll,\n /** 重试 */\n retryFile,\n /** 处理单文件 */\n processFile,\n /** 获取成功列表 */\n getSuccessList,\n };\n}\n","/**\r\n * 拖拽 & 粘贴上传\r\n */\r\n\r\nimport { ref, readonly, watch, onMounted, onBeforeUnmount, type Ref } from 'vue'\r\n\r\n/**\r\n * 拖拽 & 粘贴上传\r\n *\r\n * 监听容器的 dragover / drop / paste 事件,\r\n * 提取文件(含目录遍历)后回调给上层。\r\n *\r\n * @param containerRef 容器 DOM\r\n * @param enabled 是否启用拖拽\r\n * @param pasteable 是否启用粘贴\r\n * @param accept 文件类型限制\r\n * @param onFiles 文件回调\r\n */\r\nexport function useDragDrop(\r\n containerRef: Ref<HTMLElement | undefined>,\r\n enabled: Ref<boolean>,\r\n pasteable: Ref<boolean>,\r\n accept: Ref<string>,\r\n onFiles: (files: File[]) => void\r\n) {\r\n /** 是否正在拖拽悬停 */\r\n const isDragOver = ref(false)\r\n\r\n // ─── 拖拽事件 ─────────────────────────────────\r\n\r\n /** 处理拖入事件 */\r\n function handleDragEnter(e: DragEvent) {\r\n if (!enabled.value) return\r\n e.preventDefault()\r\n isDragOver.value = true\r\n }\r\n\r\n /** 处理拖拽经过事件 */\r\n function handleDragOver(e: DragEvent) {\r\n if (!enabled.value) return\r\n e.preventDefault()\r\n isDragOver.value = true\r\n }\r\n\r\n /** 处理拖离事件 */\r\n function handleDragLeave(e: DragEvent) {\r\n if (!enabled.value) return\r\n e.preventDefault()\r\n const container = containerRef.value\r\n if (container && !container.contains(e.relatedTarget as Node)) {\r\n isDragOver.value = false\r\n }\r\n }\r\n\r\n /** 处理放置事件 */\r\n async function handleDrop(e: DragEvent) {\r\n if (!enabled.value) return\r\n e.preventDefault()\r\n isDragOver.value = false\r\n\r\n const items = e.dataTransfer?.items\r\n if (!items) return\r\n\r\n const files = await collectDropFiles(items, e.dataTransfer)\r\n const filtered = filterByAccept(files, accept.value)\r\n if (filtered.length > 0) onFiles(filtered)\r\n }\r\n\r\n // ─── 粘贴事件 ─────────────────────────────────\r\n\r\n /** 粘贴事件 */\r\n function handlePaste(e: ClipboardEvent) {\r\n if (!pasteable.value) return\r\n\r\n const items = e.clipboardData?.items\r\n if (!items) return\r\n\r\n const files: File[] = []\r\n for (let i = 0; i < items.length; i++) {\r\n const item = items[i]\r\n if (item.kind === 'file') {\r\n const file = item.getAsFile()\r\n if (file) files.push(file)\r\n }\r\n }\r\n\r\n const filtered = filterByAccept(files, accept.value)\r\n if (filtered.length > 0) {\r\n e.preventDefault()\r\n onFiles(filtered)\r\n }\r\n }\r\n\r\n // ─── 生命周期绑定 ─────────────────────────────\r\n\r\n /** 绑定事件 */\r\n function bindEvents() {\r\n const el = containerRef.value\r\n if (!el) return\r\n\r\n el.addEventListener('dragenter', handleDragEnter)\r\n el.addEventListener('dragover', handleDragOver)\r\n el.addEventListener('dragleave', handleDragLeave)\r\n el.addEventListener('drop', handleDrop)\r\n\r\n if (pasteable.value) {\r\n document.addEventListener('paste', handlePaste)\r\n }\r\n }\r\n\r\n /** 解绑事件 */\r\n function unbindEvents() {\r\n const el = containerRef.value\r\n if (el) {\r\n el.removeEventListener('dragenter', handleDragEnter)\r\n el.removeEventListener('dragover', handleDragOver)\r\n el.removeEventListener('dragleave', handleDragLeave)\r\n el.removeEventListener('drop', handleDrop)\r\n }\r\n document.removeEventListener('paste', handlePaste)\r\n }\r\n\r\n onMounted(bindEvents)\r\n onBeforeUnmount(unbindEvents)\r\n\r\n watch([containerRef, enabled, pasteable], () => {\r\n unbindEvents()\r\n bindEvents()\r\n })\r\n\r\n return {\r\n /** 是否正在拖拽悬停 */\r\n isDragOver: readonly(isDragOver),\r\n }\r\n}\r\n\r\n// ─── 工具函数 ────────────────────────────────\r\n\r\n/** 从 drop 事件收集文件(含目录遍历) */\r\nasync function collectDropFiles(\r\n items: DataTransferItemList,\r\n dataTransfer: DataTransfer | null\r\n): Promise<File[]> {\r\n const entries: FileSystemEntry[] = []\r\n for (let i = 0; i < items.length; i++) {\r\n const entry = items[i].webkitGetAsEntry?.()\r\n if (entry) entries.push(entry)\r\n }\r\n\r\n if (entries.length > 0) {\r\n const results = await Promise.all(entries.map(readEntry))\r\n return results.flat()\r\n }\r\n\r\n const files: File[] = []\r\n const dtFiles = dataTransfer?.files\r\n if (dtFiles) {\r\n for (let i = 0; i < dtFiles.length; i++) {\r\n files.push(dtFiles[i])\r\n }\r\n }\r\n return files\r\n}\r\n\r\n/** 递归读取 FileSystemEntry */\r\nasync function readEntry(entry: FileSystemEntry): Promise<File[]> {\r\n if (entry.isFile) {\r\n return new Promise(resolve => {\r\n ;(entry as FileSystemFileEntry).file(\r\n file => resolve([file]),\r\n () => resolve([])\r\n )\r\n })\r\n }\r\n\r\n if (entry.isDirectory) {\r\n const reader = (entry as FileSystemDirectoryEntry).createReader()\r\n const entries = await new Promise<FileSystemEntry[]>(resolve => {\r\n reader.readEntries(\r\n result => resolve(result),\r\n () => resolve([])\r\n )\r\n })\r\n const results = await Promise.all(entries.map(readEntry))\r\n return results.flat()\r\n }\r\n\r\n return []\r\n}\r\n\r\n/** 根据 accept 过滤文件 */\r\nfunction filterByAccept(files: File[], accept: string): File[] {\r\n if (!accept) return files\r\n\r\n const acceptTypes = accept.split(',').map(s => s.trim().toLowerCase())\r\n\r\n return files.filter(file => {\r\n return acceptTypes.some(type => {\r\n if (type.startsWith('.')) {\r\n // 扩展名匹配\r\n return file.name.toLowerCase().endsWith(type)\r\n }\r\n if (type.endsWith('/*')) {\r\n // MIME 前缀匹配 (image/*, video/*)\r\n const prefix = type.replace('/*', '')\r\n return file.type.toLowerCase().startsWith(prefix)\r\n }\r\n // 精确 MIME 匹配\r\n return file.type.toLowerCase() === type\r\n })\r\n })\r\n}\r\n","<template>\r\n <div\r\n ref=\"areaRef\"\r\n class=\"upload-area\"\r\n :class=\"{\r\n 'upload-area--drag-over': isDragOver,\r\n 'upload-area--disabled': disabled,\r\n }\"\r\n @click=\"handleClick\"\r\n >\r\n <input\r\n ref=\"inputRef\"\r\n type=\"file\"\r\n class=\"upload-area__input\"\r\n :accept=\"accept\"\r\n :multiple=\"multiple\"\r\n :webkitdirectory=\"directory || undefined\"\r\n @change=\"handleInputChange\"\r\n />\r\n\r\n <slot>\r\n <div class=\"upload-area__default\">\r\n <C_Icon\r\n :name=\"\r\n isDragOver\r\n ? 'mdi:cloud-download-outline'\r\n : 'mdi:cloud-upload-outline'\r\n \"\r\n class=\"upload-area__icon\"\r\n />\r\n <div class=\"upload-area__text\">\r\n <span v-if=\"isDragOver\">释放文件到此处</span>\r\n <span v-else>\r\n 点击或拖拽文件到此区域上传\r\n <template v-if=\"pasteable\">\r\n ,支持 <kbd>Ctrl+V</kbd> 粘贴\r\n </template>\r\n </span>\r\n </div>\r\n <div v-if=\"tip\" class=\"upload-area__tip\">\r\n {{ tip }}\r\n </div>\r\n </div>\r\n </slot>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed } from \"vue\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport { useDragDrop } from \"../composables/useDragDrop\";\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** 接受的文件类型 */\r\n accept?: string;\r\n /** 是否多选 */\r\n multiple?: boolean;\r\n /** 是否目录 */\r\n directory?: boolean;\r\n /** 是否禁用 */\r\n disabled?: boolean;\r\n /** 是否启用拖拽 */\r\n draggable?: boolean;\r\n /** 是否启用粘贴 */\r\n pasteable?: boolean;\r\n /** 提示文本 */\r\n tip?: string;\r\n }>(),\r\n {\r\n accept: \"\",\r\n multiple: false,\r\n directory: false,\r\n disabled: false,\r\n draggable: true,\r\n pasteable: false,\r\n tip: \"\",\r\n },\r\n);\r\n\r\nconst emit = defineEmits<{\r\n files: [files: File[]];\r\n}>();\r\n\r\nconst areaRef = ref<HTMLElement>();\r\nconst inputRef = ref<HTMLInputElement>();\r\n\r\n// 拖拽 & 粘贴\r\nconst { isDragOver } = useDragDrop(\r\n areaRef,\r\n computed(() => props.draggable && !props.disabled),\r\n computed(() => props.pasteable && !props.disabled),\r\n computed(() => props.accept),\r\n (files) => emit(\"files\", files),\r\n);\r\n\r\n/** 点击触发文件选择 */\r\nfunction handleClick() {\r\n if (props.disabled) return;\r\n inputRef.value?.click();\r\n}\r\n\r\n/** input change */\r\nfunction handleInputChange(e: Event) {\r\n const input = e.target as HTMLInputElement;\r\n const files = Array.from(input.files ?? []);\r\n if (files.length > 0) {\r\n emit(\"files\", files);\r\n }\r\n // 清空 input 以允许重复选择同一文件\r\n input.value = \"\";\r\n}\r\n\r\n/** 暴露触发方法 */\r\ndefineExpose({\r\n triggerSelect: handleClick,\r\n});\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.upload-area {\r\n position: relative;\r\n box-sizing: border-box;\r\n padding: 32px 24px;\r\n border: 2px dashed var(--border-color);\r\n border-radius: 10px;\r\n cursor: pointer;\r\n background: var(--card-color);\r\n transition: all 0.25s ease;\r\n text-align: center;\r\n\r\n &:hover:not(.upload-area--disabled) {\r\n border-color: var(--primary-color);\r\n background: color-mix(in srgb, var(--primary-color) 4%, var(--card-color));\r\n }\r\n\r\n &--drag-over {\r\n border-color: var(--primary-color);\r\n border-style: solid;\r\n background: color-mix(in srgb, var(--primary-color) 8%, var(--card-color));\r\n\r\n .upload-area__icon {\r\n color: var(--primary-color);\r\n transform: scale(1.1);\r\n }\r\n }\r\n\r\n &--disabled {\r\n cursor: not-allowed;\r\n opacity: 0.5;\r\n }\r\n\r\n &__input {\r\n display: none;\r\n }\r\n\r\n &__default {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n align-items: center;\r\n }\r\n\r\n &__icon {\r\n font-size: 48px;\r\n color: var(--text-color-3);\r\n transition: all 0.25s ease;\r\n }\r\n\r\n &__text {\r\n font-size: 14px;\r\n color: var(--text-color-2);\r\n line-height: 1.6;\r\n\r\n kbd {\r\n padding: 1px 5px;\r\n font-size: 12px;\r\n border: 1px solid var(--border-color);\r\n border-radius: 3px;\r\n background: var(--body-color);\r\n }\r\n }\r\n\r\n &__tip {\r\n font-size: 12px;\r\n color: var(--text-color-4);\r\n }\r\n}\r\n</style>\r\n","<template>\r\n <div\r\n ref=\"areaRef\"\r\n class=\"upload-area\"\r\n :class=\"{\r\n 'upload-area--drag-over': isDragOver,\r\n 'upload-area--disabled': disabled,\r\n }\"\r\n @click=\"handleClick\"\r\n >\r\n <input\r\n ref=\"inputRef\"\r\n type=\"file\"\r\n class=\"upload-area__input\"\r\n :accept=\"accept\"\r\n :multiple=\"multiple\"\r\n :webkitdirectory=\"directory || undefined\"\r\n @change=\"handleInputChange\"\r\n />\r\n\r\n <slot>\r\n <div class=\"upload-area__default\">\r\n <C_Icon\r\n :name=\"\r\n isDragOver\r\n ? 'mdi:cloud-download-outline'\r\n : 'mdi:cloud-upload-outline'\r\n \"\r\n class=\"upload-area__icon\"\r\n />\r\n <div class=\"upload-area__text\">\r\n <span v-if=\"isDragOver\">释放文件到此处</span>\r\n <span v-else>\r\n 点击或拖拽文件到此区域上传\r\n <template v-if=\"pasteable\">\r\n ,支持 <kbd>Ctrl+V</kbd> 粘贴\r\n </template>\r\n </span>\r\n </div>\r\n <div v-if=\"tip\" class=\"upload-area__tip\">\r\n {{ tip }}\r\n </div>\r\n </div>\r\n </slot>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { ref, computed } from \"vue\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport { useDragDrop } from \"../composables/useDragDrop\";\r\n\r\nconst props = withDefaults(\r\n defineProps<{\r\n /** 接受的文件类型 */\r\n accept?: string;\r\n /** 是否多选 */\r\n multiple?: boolean;\r\n /** 是否目录 */\r\n directory?: boolean;\r\n /** 是否禁用 */\r\n disabled?: boolean;\r\n /** 是否启用拖拽 */\r\n draggable?: boolean;\r\n /** 是否启用粘贴 */\r\n pasteable?: boolean;\r\n /** 提示文本 */\r\n tip?: string;\r\n }>(),\r\n {\r\n accept: \"\",\r\n multiple: false,\r\n directory: false,\r\n disabled: false,\r\n draggable: true,\r\n pasteable: false,\r\n tip: \"\",\r\n },\r\n);\r\n\r\nconst emit = defineEmits<{\r\n files: [files: File[]];\r\n}>();\r\n\r\nconst areaRef = ref<HTMLElement>();\r\nconst inputRef = ref<HTMLInputElement>();\r\n\r\n// 拖拽 & 粘贴\r\nconst { isDragOver } = useDragDrop(\r\n areaRef,\r\n computed(() => props.draggable && !props.disabled),\r\n computed(() => props.pasteable && !props.disabled),\r\n computed(() => props.accept),\r\n (files) => emit(\"files\", files),\r\n);\r\n\r\n/** 点击触发文件选择 */\r\nfunction handleClick() {\r\n if (props.disabled) return;\r\n inputRef.value?.click();\r\n}\r\n\r\n/** input change */\r\nfunction handleInputChange(e: Event) {\r\n const input = e.target as HTMLInputElement;\r\n const files = Array.from(input.files ?? []);\r\n if (files.length > 0) {\r\n emit(\"files\", files);\r\n }\r\n // 清空 input 以允许重复选择同一文件\r\n input.value = \"\";\r\n}\r\n\r\n/** 暴露触发方法 */\r\ndefineExpose({\r\n triggerSelect: handleClick,\r\n});\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.upload-area {\r\n position: relative;\r\n box-sizing: border-box;\r\n padding: 32px 24px;\r\n border: 2px dashed var(--border-color);\r\n border-radius: 10px;\r\n cursor: pointer;\r\n background: var(--card-color);\r\n transition: all 0.25s ease;\r\n text-align: center;\r\n\r\n &:hover:not(.upload-area--disabled) {\r\n border-color: var(--primary-color);\r\n background: color-mix(in srgb, var(--primary-color) 4%, var(--card-color));\r\n }\r\n\r\n &--drag-over {\r\n border-color: var(--primary-color);\r\n border-style: solid;\r\n background: color-mix(in srgb, var(--primary-color) 8%, var(--card-color));\r\n\r\n .upload-area__icon {\r\n color: var(--primary-color);\r\n transform: scale(1.1);\r\n }\r\n }\r\n\r\n &--disabled {\r\n cursor: not-allowed;\r\n opacity: 0.5;\r\n }\r\n\r\n &__input {\r\n display: none;\r\n }\r\n\r\n &__default {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n align-items: center;\r\n }\r\n\r\n &__icon {\r\n font-size: 48px;\r\n color: var(--text-color-3);\r\n transition: all 0.25s ease;\r\n }\r\n\r\n &__text {\r\n font-size: 14px;\r\n color: var(--text-color-2);\r\n line-height: 1.6;\r\n\r\n kbd {\r\n padding: 1px 5px;\r\n font-size: 12px;\r\n border: 1px solid var(--border-color);\r\n border-radius: 3px;\r\n background: var(--body-color);\r\n }\r\n }\r\n\r\n &__tip {\r\n font-size: 12px;\r\n color: var(--text-color-4);\r\n }\r\n}\r\n</style>\r\n","<template>\r\n <TransitionGroup name=\"file-list\" tag=\"div\" class=\"file-list\">\r\n <div\r\n v-for=\"file in fileList\"\r\n :key=\"file.uid\"\r\n class=\"file-list__item\"\r\n :class=\"`file-list__item--${file.status}`\"\r\n >\r\n <!-- 缩略图 / 图标 -->\r\n <div class=\"file-list__thumb\">\r\n <img\r\n v-if=\"showThumbnail && file.thumbUrl\"\r\n :src=\"file.thumbUrl\"\r\n :alt=\"file.name\"\r\n class=\"file-list__thumb-img\"\r\n />\r\n <C_Icon\r\n v-else\r\n :name=\"getFileIcon(file.type)\"\r\n class=\"file-list__thumb-icon\"\r\n />\r\n </div>\r\n\r\n <!-- 文件信息 -->\r\n <div class=\"file-list__info\">\r\n <div class=\"file-list__name\">\r\n <NEllipsis :line-clamp=\"1\">\r\n {{ file.name }}\r\n </NEllipsis>\r\n </div>\r\n <div class=\"file-list__meta\">\r\n <span class=\"file-list__size\">{{ formatFileSize(file.size) }}</span>\r\n <NTag\r\n :type=\"(STATUS_TYPE[file.status] as any) || 'default'\"\r\n size=\"tiny\"\r\n :bordered=\"false\"\r\n >\r\n {{ STATUS_TEXT[file.status] || file.status }}\r\n </NTag>\r\n <span v-if=\"file.status === 'instant'\" class=\"file-list__instant\">\r\n ⚡\r\n </span>\r\n </div>\r\n\r\n <!-- 进度条 -->\r\n <NProgress\r\n v-if=\"file.status === 'uploading' || file.status === 'hashing'\"\r\n :percentage=\"file.status === 'hashing' ? hashProgress : file.percent\"\r\n :height=\"4\"\r\n :border-radius=\"2\"\r\n :show-indicator=\"false\"\r\n :status=\"file.status === 'hashing' ? 'info' : 'success'\"\r\n />\r\n\r\n <!-- 分片进度描述 -->\r\n <div\r\n v-if=\"file.chunkProgress && file.status === 'uploading'\"\r\n class=\"file-list__chunk-info\"\r\n >\r\n 分片 {{ file.chunkProgress.uploadedChunks }}/{{\r\n file.chunkProgress.totalChunks\r\n }}\r\n </div>\r\n\r\n <!-- 错误信息 -->\r\n <div\r\n v-if=\"file.status === 'error' && file.error\"\r\n class=\"file-list__error\"\r\n >\r\n {{ file.error }}\r\n </div>\r\n </div>\r\n\r\n <!-- 操作按钮 -->\r\n <div class=\"file-list__actions\">\r\n <NButton\r\n v-if=\"file.status === 'error'\"\r\n text\r\n type=\"warning\"\r\n size=\"tiny\"\r\n @click=\"emit('retry', file.uid)\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:refresh\" />\r\n </template>\r\n </NButton>\r\n <NButton\r\n text\r\n type=\"error\"\r\n size=\"tiny\"\r\n @click=\"emit('remove', file.uid)\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n </div>\r\n </TransitionGroup>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { NProgress, NButton, NTag, NEllipsis } from \"naive-ui\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport type { UploadFileItem } from \"../types\";\r\nimport {\r\n STATUS_TEXT,\r\n STATUS_TYPE,\r\n getFileIcon,\r\n formatFileSize,\r\n} from \"../constants\";\r\n\r\ndefineProps<{\r\n /** 文件列表 */\r\n fileList: UploadFileItem[];\r\n /** 是否显示缩略图 */\r\n showThumbnail?: boolean;\r\n /** hash 进度(全局共享) */\r\n hashProgress?: number;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n remove: [uid: string];\r\n retry: [uid: string];\r\n}>();\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.file-list {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n margin-top: 12px;\r\n\r\n &__item {\r\n display: flex;\r\n gap: 12px;\r\n align-items: flex-start;\r\n padding: 10px 12px;\r\n border-radius: 8px;\r\n border: 1px solid var(--border-color);\r\n background: var(--card-color);\r\n transition: all 0.25s ease;\r\n\r\n &:hover {\r\n border-color: color-mix(\r\n in srgb,\r\n var(--primary-color) 40%,\r\n var(--border-color)\r\n );\r\n box-shadow: 0 1px 6px rgba(0, 0, 0, 0.04);\r\n }\r\n\r\n &--error {\r\n border-color: var(--error-color);\r\n background: color-mix(in srgb, var(--error-color) 4%, var(--card-color));\r\n }\r\n\r\n &--success,\r\n &--instant {\r\n border-color: color-mix(\r\n in srgb,\r\n var(--success-color) 30%,\r\n var(--border-color)\r\n );\r\n }\r\n }\r\n\r\n &__thumb {\r\n flex-shrink: 0;\r\n width: 40px;\r\n height: 40px;\r\n border-radius: 6px;\r\n overflow: hidden;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: var(--body-color);\r\n }\r\n\r\n &__thumb-img {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n }\r\n\r\n &__thumb-icon {\r\n font-size: 22px;\r\n color: var(--text-color-3);\r\n }\r\n\r\n &__info {\r\n flex: 1;\r\n min-width: 0;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n }\r\n\r\n &__name {\r\n font-size: 13px;\r\n font-weight: 500;\r\n color: var(--text-color-1);\r\n }\r\n\r\n &__meta {\r\n display: flex;\r\n gap: 8px;\r\n align-items: center;\r\n font-size: 12px;\r\n }\r\n\r\n &__size {\r\n color: var(--text-color-3);\r\n }\r\n\r\n &__instant {\r\n font-size: 14px;\r\n }\r\n\r\n &__chunk-info {\r\n font-size: 11px;\r\n color: var(--text-color-4);\r\n }\r\n\r\n &__error {\r\n font-size: 12px;\r\n color: var(--error-color);\r\n }\r\n\r\n &__actions {\r\n flex-shrink: 0;\r\n display: flex;\r\n gap: 4px;\r\n align-items: center;\r\n }\r\n}\r\n\r\n/* ─── 过渡动画 ─────────────────────────────── */\r\n\r\n.file-list-enter-active,\r\n.file-list-leave-active {\r\n transition: all 0.3s ease;\r\n}\r\n\r\n.file-list-enter-from {\r\n opacity: 0;\r\n transform: translateX(-20px);\r\n}\r\n\r\n.file-list-leave-to {\r\n opacity: 0;\r\n transform: translateX(20px);\r\n}\r\n\r\n.file-list-move {\r\n transition: transform 0.3s ease;\r\n}\r\n</style>\r\n","<template>\r\n <TransitionGroup name=\"file-list\" tag=\"div\" class=\"file-list\">\r\n <div\r\n v-for=\"file in fileList\"\r\n :key=\"file.uid\"\r\n class=\"file-list__item\"\r\n :class=\"`file-list__item--${file.status}`\"\r\n >\r\n <!-- 缩略图 / 图标 -->\r\n <div class=\"file-list__thumb\">\r\n <img\r\n v-if=\"showThumbnail && file.thumbUrl\"\r\n :src=\"file.thumbUrl\"\r\n :alt=\"file.name\"\r\n class=\"file-list__thumb-img\"\r\n />\r\n <C_Icon\r\n v-else\r\n :name=\"getFileIcon(file.type)\"\r\n class=\"file-list__thumb-icon\"\r\n />\r\n </div>\r\n\r\n <!-- 文件信息 -->\r\n <div class=\"file-list__info\">\r\n <div class=\"file-list__name\">\r\n <NEllipsis :line-clamp=\"1\">\r\n {{ file.name }}\r\n </NEllipsis>\r\n </div>\r\n <div class=\"file-list__meta\">\r\n <span class=\"file-list__size\">{{ formatFileSize(file.size) }}</span>\r\n <NTag\r\n :type=\"(STATUS_TYPE[file.status] as any) || 'default'\"\r\n size=\"tiny\"\r\n :bordered=\"false\"\r\n >\r\n {{ STATUS_TEXT[file.status] || file.status }}\r\n </NTag>\r\n <span v-if=\"file.status === 'instant'\" class=\"file-list__instant\">\r\n ⚡\r\n </span>\r\n </div>\r\n\r\n <!-- 进度条 -->\r\n <NProgress\r\n v-if=\"file.status === 'uploading' || file.status === 'hashing'\"\r\n :percentage=\"file.status === 'hashing' ? hashProgress : file.percent\"\r\n :height=\"4\"\r\n :border-radius=\"2\"\r\n :show-indicator=\"false\"\r\n :status=\"file.status === 'hashing' ? 'info' : 'success'\"\r\n />\r\n\r\n <!-- 分片进度描述 -->\r\n <div\r\n v-if=\"file.chunkProgress && file.status === 'uploading'\"\r\n class=\"file-list__chunk-info\"\r\n >\r\n 分片 {{ file.chunkProgress.uploadedChunks }}/{{\r\n file.chunkProgress.totalChunks\r\n }}\r\n </div>\r\n\r\n <!-- 错误信息 -->\r\n <div\r\n v-if=\"file.status === 'error' && file.error\"\r\n class=\"file-list__error\"\r\n >\r\n {{ file.error }}\r\n </div>\r\n </div>\r\n\r\n <!-- 操作按钮 -->\r\n <div class=\"file-list__actions\">\r\n <NButton\r\n v-if=\"file.status === 'error'\"\r\n text\r\n type=\"warning\"\r\n size=\"tiny\"\r\n @click=\"emit('retry', file.uid)\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:refresh\" />\r\n </template>\r\n </NButton>\r\n <NButton\r\n text\r\n type=\"error\"\r\n size=\"tiny\"\r\n @click=\"emit('remove', file.uid)\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n </div>\r\n </TransitionGroup>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { NProgress, NButton, NTag, NEllipsis } from \"naive-ui\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport type { UploadFileItem } from \"../types\";\r\nimport {\r\n STATUS_TEXT,\r\n STATUS_TYPE,\r\n getFileIcon,\r\n formatFileSize,\r\n} from \"../constants\";\r\n\r\ndefineProps<{\r\n /** 文件列表 */\r\n fileList: UploadFileItem[];\r\n /** 是否显示缩略图 */\r\n showThumbnail?: boolean;\r\n /** hash 进度(全局共享) */\r\n hashProgress?: number;\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n remove: [uid: string];\r\n retry: [uid: string];\r\n}>();\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.file-list {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n margin-top: 12px;\r\n\r\n &__item {\r\n display: flex;\r\n gap: 12px;\r\n align-items: flex-start;\r\n padding: 10px 12px;\r\n border-radius: 8px;\r\n border: 1px solid var(--border-color);\r\n background: var(--card-color);\r\n transition: all 0.25s ease;\r\n\r\n &:hover {\r\n border-color: color-mix(\r\n in srgb,\r\n var(--primary-color) 40%,\r\n var(--border-color)\r\n );\r\n box-shadow: 0 1px 6px rgba(0, 0, 0, 0.04);\r\n }\r\n\r\n &--error {\r\n border-color: var(--error-color);\r\n background: color-mix(in srgb, var(--error-color) 4%, var(--card-color));\r\n }\r\n\r\n &--success,\r\n &--instant {\r\n border-color: color-mix(\r\n in srgb,\r\n var(--success-color) 30%,\r\n var(--border-color)\r\n );\r\n }\r\n }\r\n\r\n &__thumb {\r\n flex-shrink: 0;\r\n width: 40px;\r\n height: 40px;\r\n border-radius: 6px;\r\n overflow: hidden;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n background: var(--body-color);\r\n }\r\n\r\n &__thumb-img {\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n }\r\n\r\n &__thumb-icon {\r\n font-size: 22px;\r\n color: var(--text-color-3);\r\n }\r\n\r\n &__info {\r\n flex: 1;\r\n min-width: 0;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n }\r\n\r\n &__name {\r\n font-size: 13px;\r\n font-weight: 500;\r\n color: var(--text-color-1);\r\n }\r\n\r\n &__meta {\r\n display: flex;\r\n gap: 8px;\r\n align-items: center;\r\n font-size: 12px;\r\n }\r\n\r\n &__size {\r\n color: var(--text-color-3);\r\n }\r\n\r\n &__instant {\r\n font-size: 14px;\r\n }\r\n\r\n &__chunk-info {\r\n font-size: 11px;\r\n color: var(--text-color-4);\r\n }\r\n\r\n &__error {\r\n font-size: 12px;\r\n color: var(--error-color);\r\n }\r\n\r\n &__actions {\r\n flex-shrink: 0;\r\n display: flex;\r\n gap: 4px;\r\n align-items: center;\r\n }\r\n}\r\n\r\n/* ─── 过渡动画 ─────────────────────────────── */\r\n\r\n.file-list-enter-active,\r\n.file-list-leave-active {\r\n transition: all 0.3s ease;\r\n}\r\n\r\n.file-list-enter-from {\r\n opacity: 0;\r\n transform: translateX(-20px);\r\n}\r\n\r\n.file-list-leave-to {\r\n opacity: 0;\r\n transform: translateX(20px);\r\n}\r\n\r\n.file-list-move {\r\n transition: transform 0.3s ease;\r\n}\r\n</style>\r\n","<template>\r\n <div v-if=\"imageFiles.length > 0\" class=\"image-preview\">\r\n <div\r\n v-for=\"file in imageFiles\"\r\n :key=\"file.uid\"\r\n class=\"image-preview__item\"\r\n :class=\"`image-preview__item--${file.status}`\"\r\n >\r\n <img\r\n v-if=\"file.thumbUrl\"\r\n :src=\"file.thumbUrl\"\r\n :alt=\"file.name\"\r\n class=\"image-preview__img\"\r\n />\r\n\r\n <!-- 遮罩层 -->\r\n <div class=\"image-preview__overlay\">\r\n <!-- 上传中进度 -->\r\n <div\r\n v-if=\"file.status === 'uploading' || file.status === 'hashing'\"\r\n class=\"image-preview__progress\"\r\n >\r\n <NProgress\r\n type=\"circle\"\r\n :percentage=\"file.percent\"\r\n :stroke-width=\"3\"\r\n style=\"width: 40px\"\r\n />\r\n </div>\r\n\r\n <!-- 状态图标 -->\r\n <C_Icon\r\n v-else-if=\"file.status === 'success' || file.status === 'instant'\"\r\n name=\"mdi:check-circle\"\r\n class=\"image-preview__status-icon image-preview__status-icon--success\"\r\n />\r\n <C_Icon\r\n v-else-if=\"file.status === 'error'\"\r\n name=\"mdi:alert-circle\"\r\n class=\"image-preview__status-icon image-preview__status-icon--error\"\r\n />\r\n\r\n <!-- 操作 -->\r\n <div class=\"image-preview__actions\">\r\n <NButton\r\n v-if=\"file.status === 'error'\"\r\n circle\r\n size=\"tiny\"\r\n type=\"warning\"\r\n @click=\"emit('retry', file.uid)\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:refresh\" />\r\n </template>\r\n </NButton>\r\n <NButton\r\n circle\r\n size=\"tiny\"\r\n type=\"error\"\r\n @click=\"emit('remove', file.uid)\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <!-- 文件名 -->\r\n <div class=\"image-preview__name\">\r\n <NEllipsis :line-clamp=\"1\">\r\n {{ file.name }}\r\n </NEllipsis>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport { NProgress, NButton, NEllipsis } from \"naive-ui\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport type { UploadFileItem } from \"../types\";\r\n\r\nconst props = defineProps<{\r\n /** 文件列表 */\r\n fileList: UploadFileItem[];\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n remove: [uid: string];\r\n retry: [uid: string];\r\n}>();\r\n\r\n/** 过滤出图片文件 */\r\nconst imageFiles = computed(() =>\r\n props.fileList.filter((f) => f.type?.startsWith(\"image/\")),\r\n);\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.image-preview {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));\r\n gap: 10px;\r\n margin-top: 12px;\r\n\r\n &__item {\r\n position: relative;\r\n aspect-ratio: 1;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n border: 1px solid var(--border-color);\r\n background: var(--body-color);\r\n\r\n &--error {\r\n border-color: var(--error-color);\r\n }\r\n\r\n &--success,\r\n &--instant {\r\n border-color: color-mix(\r\n in srgb,\r\n var(--success-color) 30%,\r\n var(--border-color)\r\n );\r\n }\r\n\r\n &:hover .image-preview__overlay {\r\n opacity: 1;\r\n }\r\n }\r\n\r\n &__img {\r\n display: block;\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n }\r\n\r\n &__overlay {\r\n position: absolute;\r\n inset: 0;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 8px;\r\n background: rgba(0, 0, 0, 0.45);\r\n opacity: 0;\r\n transition: opacity 0.2s ease;\r\n }\r\n\r\n /* 上传中始终显示遮罩 */\r\n &__item--uploading .image-preview__overlay,\r\n &__item--hashing .image-preview__overlay,\r\n &__item--error .image-preview__overlay {\r\n opacity: 1;\r\n }\r\n\r\n &__progress {\r\n :deep(.n-progress) {\r\n --n-fill-color: #fff;\r\n }\r\n }\r\n\r\n &__status-icon {\r\n font-size: 28px;\r\n\r\n &--success {\r\n color: var(--success-color);\r\n }\r\n\r\n &--error {\r\n color: var(--error-color);\r\n }\r\n }\r\n\r\n &__actions {\r\n display: flex;\r\n gap: 6px;\r\n }\r\n\r\n &__name {\r\n position: absolute;\r\n bottom: 0;\r\n left: 0;\r\n right: 0;\r\n padding: 2px 6px;\r\n font-size: 11px;\r\n color: #fff;\r\n background: linear-gradient(transparent, rgba(0, 0, 0, 0.6));\r\n text-align: center;\r\n }\r\n}\r\n</style>\r\n","<template>\r\n <div v-if=\"imageFiles.length > 0\" class=\"image-preview\">\r\n <div\r\n v-for=\"file in imageFiles\"\r\n :key=\"file.uid\"\r\n class=\"image-preview__item\"\r\n :class=\"`image-preview__item--${file.status}`\"\r\n >\r\n <img\r\n v-if=\"file.thumbUrl\"\r\n :src=\"file.thumbUrl\"\r\n :alt=\"file.name\"\r\n class=\"image-preview__img\"\r\n />\r\n\r\n <!-- 遮罩层 -->\r\n <div class=\"image-preview__overlay\">\r\n <!-- 上传中进度 -->\r\n <div\r\n v-if=\"file.status === 'uploading' || file.status === 'hashing'\"\r\n class=\"image-preview__progress\"\r\n >\r\n <NProgress\r\n type=\"circle\"\r\n :percentage=\"file.percent\"\r\n :stroke-width=\"3\"\r\n style=\"width: 40px\"\r\n />\r\n </div>\r\n\r\n <!-- 状态图标 -->\r\n <C_Icon\r\n v-else-if=\"file.status === 'success' || file.status === 'instant'\"\r\n name=\"mdi:check-circle\"\r\n class=\"image-preview__status-icon image-preview__status-icon--success\"\r\n />\r\n <C_Icon\r\n v-else-if=\"file.status === 'error'\"\r\n name=\"mdi:alert-circle\"\r\n class=\"image-preview__status-icon image-preview__status-icon--error\"\r\n />\r\n\r\n <!-- 操作 -->\r\n <div class=\"image-preview__actions\">\r\n <NButton\r\n v-if=\"file.status === 'error'\"\r\n circle\r\n size=\"tiny\"\r\n type=\"warning\"\r\n @click=\"emit('retry', file.uid)\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:refresh\" />\r\n </template>\r\n </NButton>\r\n <NButton\r\n circle\r\n size=\"tiny\"\r\n type=\"error\"\r\n @click=\"emit('remove', file.uid)\"\r\n >\r\n <template #icon>\r\n <C_Icon name=\"mdi:close\" />\r\n </template>\r\n </NButton>\r\n </div>\r\n </div>\r\n\r\n <!-- 文件名 -->\r\n <div class=\"image-preview__name\">\r\n <NEllipsis :line-clamp=\"1\">\r\n {{ file.name }}\r\n </NEllipsis>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed } from \"vue\";\r\nimport { NProgress, NButton, NEllipsis } from \"naive-ui\";\r\nimport C_Icon from \"../../C_Icon/index.vue\";\r\nimport type { UploadFileItem } from \"../types\";\r\n\r\nconst props = defineProps<{\r\n /** 文件列表 */\r\n fileList: UploadFileItem[];\r\n}>();\r\n\r\nconst emit = defineEmits<{\r\n remove: [uid: string];\r\n retry: [uid: string];\r\n}>();\r\n\r\n/** 过滤出图片文件 */\r\nconst imageFiles = computed(() =>\r\n props.fileList.filter((f) => f.type?.startsWith(\"image/\")),\r\n);\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.image-preview {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));\r\n gap: 10px;\r\n margin-top: 12px;\r\n\r\n &__item {\r\n position: relative;\r\n aspect-ratio: 1;\r\n border-radius: 8px;\r\n overflow: hidden;\r\n border: 1px solid var(--border-color);\r\n background: var(--body-color);\r\n\r\n &--error {\r\n border-color: var(--error-color);\r\n }\r\n\r\n &--success,\r\n &--instant {\r\n border-color: color-mix(\r\n in srgb,\r\n var(--success-color) 30%,\r\n var(--border-color)\r\n );\r\n }\r\n\r\n &:hover .image-preview__overlay {\r\n opacity: 1;\r\n }\r\n }\r\n\r\n &__img {\r\n display: block;\r\n width: 100%;\r\n height: 100%;\r\n object-fit: cover;\r\n }\r\n\r\n &__overlay {\r\n position: absolute;\r\n inset: 0;\r\n display: flex;\r\n flex-direction: column;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 8px;\r\n background: rgba(0, 0, 0, 0.45);\r\n opacity: 0;\r\n transition: opacity 0.2s ease;\r\n }\r\n\r\n /* 上传中始终显示遮罩 */\r\n &__item--uploading .image-preview__overlay,\r\n &__item--hashing .image-preview__overlay,\r\n &__item--error .image-preview__overlay {\r\n opacity: 1;\r\n }\r\n\r\n &__progress {\r\n :deep(.n-progress) {\r\n --n-fill-color: #fff;\r\n }\r\n }\r\n\r\n &__status-icon {\r\n font-size: 28px;\r\n\r\n &--success {\r\n color: var(--success-color);\r\n }\r\n\r\n &--error {\r\n color: var(--error-color);\r\n }\r\n }\r\n\r\n &__actions {\r\n display: flex;\r\n gap: 6px;\r\n }\r\n\r\n &__name {\r\n position: absolute;\r\n bottom: 0;\r\n left: 0;\r\n right: 0;\r\n padding: 2px 6px;\r\n font-size: 11px;\r\n color: #fff;\r\n background: linear-gradient(transparent, rgba(0, 0, 0, 0.6));\r\n text-align: center;\r\n }\r\n}\r\n</style>\r\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2026-02-27\n * @Description: 增强型上传组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-upload\">\n <!-- 上传区域 -->\n <UploadArea\n ref=\"uploadAreaRef\"\n :accept=\"props.accept\"\n :multiple=\"props.multiple\"\n :directory=\"props.directory\"\n :disabled=\"props.disabled\"\n :draggable=\"props.draggable\"\n :pasteable=\"props.pasteable\"\n :tip=\"props.tip\"\n @files=\"handleFiles\"\n >\n <slot name=\"area\" />\n </UploadArea>\n\n <!-- 总进度条 -->\n <div v-if=\"showTotalProgress\" class=\"c-upload__total-progress\">\n <div class=\"c-upload__total-label\">\n <span>总进度</span>\n <span>{{ totalPercent }}%</span>\n </div>\n <NProgress\n :percentage=\"totalPercent\"\n :height=\"6\"\n :border-radius=\"3\"\n :show-indicator=\"false\"\n status=\"success\"\n />\n </div>\n\n <!-- 图片预览模式 -->\n <ImagePreview\n v-if=\"props.listType === 'image'\"\n :file-list=\"fileList\"\n @remove=\"handleRemove\"\n @retry=\"handleRetry\"\n />\n\n <!-- 文件列表模式 -->\n <FileList\n v-else-if=\"props.showFileList && fileList.length > 0\"\n :file-list=\"fileList\"\n :show-thumbnail=\"props.showThumbnail\"\n :hash-progress=\"hashProgress\"\n @remove=\"handleRemove\"\n @retry=\"handleRetry\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch } from \"vue\";\nimport { NProgress } from \"naive-ui\";\nimport type { UploadProps, UploadExpose, UploadFileItem } from \"./types\";\nimport { DEFAULT_CHUNK_SIZE, DEFAULT_CONCURRENCY } from \"./constants\";\nimport { useUploadCore } from \"./composables/useUploadCore\";\nimport UploadArea from \"./components/UploadArea.vue\";\nimport FileList from \"./components/FileList.vue\";\nimport ImagePreview from \"./components/ImagePreview.vue\";\n\nconst props = withDefaults(defineProps<UploadProps>(), {\n action: \"\",\n headers: () => ({}),\n data: () => ({}),\n multiple: false,\n directory: false,\n accept: \"\",\n maxSize: 0,\n maxCount: 0,\n disabled: false,\n draggable: true,\n pasteable: false,\n chunked: false,\n chunkSize: DEFAULT_CHUNK_SIZE,\n concurrency: DEFAULT_CONCURRENCY,\n showFileList: true,\n listType: \"text\",\n showThumbnail: true,\n tip: \"\",\n defaultFileList: () => [],\n});\n\nconst emit = defineEmits<{\n change: [fileList: UploadFileItem[]];\n success: [file: UploadFileItem, response: any];\n error: [file: UploadFileItem, error: Error];\n progress: [file: UploadFileItem, percent: number];\n remove: [file: UploadFileItem];\n finish: [fileList: UploadFileItem[]];\n exceed: [files: File[], fileList: UploadFileItem[]];\n}>();\n\nconst uploadAreaRef = ref<InstanceType<typeof UploadArea>>();\n\nconst {\n fileList,\n totalPercent,\n hashProgress,\n addFiles,\n removeFile,\n clearAll,\n startUpload,\n pauseAll,\n resumeAll,\n retryFile,\n processFile,\n getSuccessList,\n} = useUploadCore(props);\n\n// ─── 是否显示总进度 ──────────────────────────\n\nconst showTotalProgress = computed(() => {\n return fileList.value.some(\n (f) => f.status === \"uploading\" || f.status === \"hashing\",\n );\n});\n\n// ─── 文件接收处理 ─────────────────────────────\n\n/** 处理接收到的文件 */\nfunction handleFiles(files: File[]) {\n // 数量超限\n if (props.maxCount && fileList.value.length + files.length > props.maxCount) {\n emit(\"exceed\", files, fileList.value);\n const available = props.maxCount - fileList.value.length;\n if (available <= 0) return;\n files = files.slice(0, available);\n }\n\n // 大小校验过滤\n if (props.maxSize) {\n files = files.filter((f) => f.size <= props.maxSize!);\n }\n\n const items = addFiles(files);\n emit(\"change\", fileList.value);\n\n // 自动上传\n items.forEach(processFile);\n}\n\n// ─── 移除 & 重试 ─────────────────────────────\n\n/** 移除文件 */\nfunction handleRemove(uid: string) {\n const file = fileList.value.find((f) => f.uid === uid);\n if (file) {\n emit(\"remove\", file);\n removeFile(uid);\n emit(\"change\", fileList.value);\n }\n}\n\n/** 重试上传 */\nfunction handleRetry(uid: string) {\n retryFile(uid);\n}\n\n// ─── 监听文件状态变化触发事件 ─────────────────\n\nwatch(\n fileList,\n (list) => {\n // 检查是否全部完成\n const allDone =\n list.length > 0 &&\n list.every(\n (f) =>\n f.status === \"success\" ||\n f.status === \"instant\" ||\n f.status === \"error\",\n );\n if (allDone) {\n emit(\"finish\", list);\n }\n },\n { deep: true },\n);\n\n// ─── Expose ──────────────────────────────────\n\ndefineExpose<UploadExpose>({\n selectFiles: () => uploadAreaRef.value?.triggerSelect(),\n startUpload,\n pauseAll,\n resumeAll,\n clearAll: () => {\n clearAll();\n emit(\"change\", fileList.value);\n },\n removeFile: (uid: string) => {\n handleRemove(uid);\n },\n retryFile,\n getFileList: () => fileList.value,\n getSuccessList,\n /** 总体进度 */\n get totalPercent() {\n return totalPercent.value;\n },\n});\n</script>\n\n<style scoped lang=\"scss\">\n.c-upload {\n --c-upload-progress-bg: var(--body-color, #fff);\n --c-upload-progress-border: var(--border-color, #e5e7eb);\n --c-upload-progress-radius: 8px;\n --c-upload-label-color: var(--text-color-3, #999);\n --c-upload-label-size: 12px;\n\n width: 100%;\n\n &__total-progress {\n margin-top: 12px;\n padding: 8px 12px;\n border-radius: var(--c-upload-progress-radius);\n background: var(--c-upload-progress-bg);\n border: 1px solid var(--c-upload-progress-border);\n }\n\n &__total-label {\n display: flex;\n justify-content: space-between;\n margin-bottom: 6px;\n font-size: var(--c-upload-label-size);\n color: var(--c-upload-label-color);\n }\n}\n</style>\n","<!--\n * @Author: ChenYu ycyplus@gmail.com\n * @Date: 2026-02-27\n * @Description: 增强型上传组件\n * @Migration: naive-ui-components 组件库迁移版本\n * Copyright (c) 2026 by CHENY, All Rights Reserved.\n-->\n<template>\n <div class=\"c-upload\">\n <!-- 上传区域 -->\n <UploadArea\n ref=\"uploadAreaRef\"\n :accept=\"props.accept\"\n :multiple=\"props.multiple\"\n :directory=\"props.directory\"\n :disabled=\"props.disabled\"\n :draggable=\"props.draggable\"\n :pasteable=\"props.pasteable\"\n :tip=\"props.tip\"\n @files=\"handleFiles\"\n >\n <slot name=\"area\" />\n </UploadArea>\n\n <!-- 总进度条 -->\n <div v-if=\"showTotalProgress\" class=\"c-upload__total-progress\">\n <div class=\"c-upload__total-label\">\n <span>总进度</span>\n <span>{{ totalPercent }}%</span>\n </div>\n <NProgress\n :percentage=\"totalPercent\"\n :height=\"6\"\n :border-radius=\"3\"\n :show-indicator=\"false\"\n status=\"success\"\n />\n </div>\n\n <!-- 图片预览模式 -->\n <ImagePreview\n v-if=\"props.listType === 'image'\"\n :file-list=\"fileList\"\n @remove=\"handleRemove\"\n @retry=\"handleRetry\"\n />\n\n <!-- 文件列表模式 -->\n <FileList\n v-else-if=\"props.showFileList && fileList.length > 0\"\n :file-list=\"fileList\"\n :show-thumbnail=\"props.showThumbnail\"\n :hash-progress=\"hashProgress\"\n @remove=\"handleRemove\"\n @retry=\"handleRetry\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, computed, watch } from \"vue\";\nimport { NProgress } from \"naive-ui\";\nimport type { UploadProps, UploadExpose, UploadFileItem } from \"./types\";\nimport { DEFAULT_CHUNK_SIZE, DEFAULT_CONCURRENCY } from \"./constants\";\nimport { useUploadCore } from \"./composables/useUploadCore\";\nimport UploadArea from \"./components/UploadArea.vue\";\nimport FileList from \"./components/FileList.vue\";\nimport ImagePreview from \"./components/ImagePreview.vue\";\n\nconst props = withDefaults(defineProps<UploadProps>(), {\n action: \"\",\n headers: () => ({}),\n data: () => ({}),\n multiple: false,\n directory: false,\n accept: \"\",\n maxSize: 0,\n maxCount: 0,\n disabled: false,\n draggable: true,\n pasteable: false,\n chunked: false,\n chunkSize: DEFAULT_CHUNK_SIZE,\n concurrency: DEFAULT_CONCURRENCY,\n showFileList: true,\n listType: \"text\",\n showThumbnail: true,\n tip: \"\",\n defaultFileList: () => [],\n});\n\nconst emit = defineEmits<{\n change: [fileList: UploadFileItem[]];\n success: [file: UploadFileItem, response: any];\n error: [file: UploadFileItem, error: Error];\n progress: [file: UploadFileItem, percent: number];\n remove: [file: UploadFileItem];\n finish: [fileList: UploadFileItem[]];\n exceed: [files: File[], fileList: UploadFileItem[]];\n}>();\n\nconst uploadAreaRef = ref<InstanceType<typeof UploadArea>>();\n\nconst {\n fileList,\n totalPercent,\n hashProgress,\n addFiles,\n removeFile,\n clearAll,\n startUpload,\n pauseAll,\n resumeAll,\n retryFile,\n processFile,\n getSuccessList,\n} = useUploadCore(props);\n\n// ─── 是否显示总进度 ──────────────────────────\n\nconst showTotalProgress = computed(() => {\n return fileList.value.some(\n (f) => f.status === \"uploading\" || f.status === \"hashing\",\n );\n});\n\n// ─── 文件接收处理 ─────────────────────────────\n\n/** 处理接收到的文件 */\nfunction handleFiles(files: File[]) {\n // 数量超限\n if (props.maxCount && fileList.value.length + files.length > props.maxCount) {\n emit(\"exceed\", files, fileList.value);\n const available = props.maxCount - fileList.value.length;\n if (available <= 0) return;\n files = files.slice(0, available);\n }\n\n // 大小校验过滤\n if (props.maxSize) {\n files = files.filter((f) => f.size <= props.maxSize!);\n }\n\n const items = addFiles(files);\n emit(\"change\", fileList.value);\n\n // 自动上传\n items.forEach(processFile);\n}\n\n// ─── 移除 & 重试 ─────────────────────────────\n\n/** 移除文件 */\nfunction handleRemove(uid: string) {\n const file = fileList.value.find((f) => f.uid === uid);\n if (file) {\n emit(\"remove\", file);\n removeFile(uid);\n emit(\"change\", fileList.value);\n }\n}\n\n/** 重试上传 */\nfunction handleRetry(uid: string) {\n retryFile(uid);\n}\n\n// ─── 监听文件状态变化触发事件 ─────────────────\n\nwatch(\n fileList,\n (list) => {\n // 检查是否全部完成\n const allDone =\n list.length > 0 &&\n list.every(\n (f) =>\n f.status === \"success\" ||\n f.status === \"instant\" ||\n f.status === \"error\",\n );\n if (allDone) {\n emit(\"finish\", list);\n }\n },\n { deep: true },\n);\n\n// ─── Expose ──────────────────────────────────\n\ndefineExpose<UploadExpose>({\n selectFiles: () => uploadAreaRef.value?.triggerSelect(),\n startUpload,\n pauseAll,\n resumeAll,\n clearAll: () => {\n clearAll();\n emit(\"change\", fileList.value);\n },\n removeFile: (uid: string) => {\n handleRemove(uid);\n },\n retryFile,\n getFileList: () => fileList.value,\n getSuccessList,\n /** 总体进度 */\n get totalPercent() {\n return totalPercent.value;\n },\n});\n</script>\n\n<style scoped lang=\"scss\">\n.c-upload {\n --c-upload-progress-bg: var(--body-color, #fff);\n --c-upload-progress-border: var(--border-color, #e5e7eb);\n --c-upload-progress-radius: 8px;\n --c-upload-label-color: var(--text-color-3, #999);\n --c-upload-label-size: 12px;\n\n width: 100%;\n\n &__total-progress {\n margin-top: 12px;\n padding: 8px 12px;\n border-radius: var(--c-upload-progress-radius);\n background: var(--c-upload-progress-bg);\n border: 1px solid var(--c-upload-progress-border);\n }\n\n &__total-label {\n display: flex;\n justify-content: space-between;\n margin-bottom: 6px;\n font-size: var(--c-upload-label-size);\n color: var(--c-upload-label-color);\n }\n}\n</style>\n"],"mappings":"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"}
|