@fudanda/admin-antd 2.20261109.1-beta.1 → 2.20261109.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/chunks/ApiTokensPage-w3JGRNQC.js +2 -0
  2. package/dist/chunks/ApiTokensPage-w3JGRNQC.js.map +1 -0
  3. package/dist/chunks/{App-CfQx7jWq.js → App-nh6_qOo8.js} +5 -5
  4. package/dist/chunks/{App-CfQx7jWq.js.map → App-nh6_qOo8.js.map} +1 -1
  5. package/dist/chunks/{AppModal-CVoUC2t3.js → AppModal-CEouaU03.js} +2 -2
  6. package/dist/chunks/{AppModal-CVoUC2t3.js.map → AppModal-CEouaU03.js.map} +1 -1
  7. package/dist/chunks/{ArcBaseAdmin-DQyddbFC.js → ArcBaseAdmin-CX7fQz4x.js} +2 -2
  8. package/dist/chunks/{ArcBaseAdmin-DQyddbFC.js.map → ArcBaseAdmin-CX7fQz4x.js.map} +1 -1
  9. package/dist/chunks/{DepartmentsPage-CQx53QGE.js → DepartmentsPage-BdD9VfDI.js} +2 -2
  10. package/dist/chunks/{DepartmentsPage-CQx53QGE.js.map → DepartmentsPage-BdD9VfDI.js.map} +1 -1
  11. package/dist/chunks/{DictsPage-BGnamkWk.js → DictsPage-fxBdV1-3.js} +2 -2
  12. package/dist/chunks/{DictsPage-BGnamkWk.js.map → DictsPage-fxBdV1-3.js.map} +1 -1
  13. package/dist/chunks/{EntityContextRuntime-CbzeMNXR.js → EntityContextRuntime-CMhoj6qe.js} +2 -2
  14. package/dist/chunks/{EntityContextRuntime-CbzeMNXR.js.map → EntityContextRuntime-CMhoj6qe.js.map} +1 -1
  15. package/dist/chunks/EntityContextRuntime-Dc89ZSnM.js +1 -0
  16. package/dist/chunks/{ExportButton-sMPAV2hd.js → ExportButton-DGxB2tcu.js} +2 -2
  17. package/dist/chunks/{ExportButton-sMPAV2hd.js.map → ExportButton-DGxB2tcu.js.map} +1 -1
  18. package/dist/chunks/{FileViewerPreviewPanel-Rti7pUhU.js → FileViewerPreviewPanel-BMZ7FdKV.js} +3 -3
  19. package/dist/chunks/{FileViewerPreviewPanel-Rti7pUhU.js.map → FileViewerPreviewPanel-BMZ7FdKV.js.map} +1 -1
  20. package/dist/chunks/{FilesPage-D_iYsUwl.js → FilesPage-DrgWELKy.js} +3 -3
  21. package/dist/chunks/{FilesPage-D_iYsUwl.js.map → FilesPage-DrgWELKy.js.map} +1 -1
  22. package/dist/chunks/{IdentitySecurityPage-rDYX5ksX.js → IdentitySecurityPage-BjTKvr-O.js} +2 -2
  23. package/dist/chunks/{IdentitySecurityPage-rDYX5ksX.js.map → IdentitySecurityPage-BjTKvr-O.js.map} +1 -1
  24. package/dist/chunks/{LogStatsScaffold-CMIz4z0a.js → LogStatsScaffold-DaB0JeAN.js} +2 -2
  25. package/dist/chunks/{LogStatsScaffold-CMIz4z0a.js.map → LogStatsScaffold-DaB0JeAN.js.map} +1 -1
  26. package/dist/chunks/{LoginLogsPage-Bv5pPJb2.js → LoginLogsPage-VmYC7i6q.js} +2 -2
  27. package/dist/chunks/{LoginLogsPage-Bv5pPJb2.js.map → LoginLogsPage-VmYC7i6q.js.map} +1 -1
  28. package/dist/chunks/{LoginLogsTable-DYcTx2oR.js → LoginLogsTable-CULcc_e8.js} +2 -2
  29. package/dist/chunks/{LoginLogsTable-DYcTx2oR.js.map → LoginLogsTable-CULcc_e8.js.map} +1 -1
  30. package/dist/chunks/{MarkdownPreviewPanel-lqs6AsoN.js → MarkdownPreviewPanel-CBcPc7Wv.js} +2 -2
  31. package/dist/chunks/{MarkdownPreviewPanel-lqs6AsoN.js.map → MarkdownPreviewPanel-CBcPc7Wv.js.map} +1 -1
  32. package/dist/chunks/{MasterDetailLayout-B5uqvh98.js → MasterDetailLayout-ByIBpmkZ.js} +2 -2
  33. package/dist/chunks/{MasterDetailLayout-B5uqvh98.js.map → MasterDetailLayout-ByIBpmkZ.js.map} +1 -1
  34. package/dist/chunks/{McpSettingsPage-Cdej9Evi.js → McpSettingsPage-C5V_rj2V.js} +2 -2
  35. package/dist/chunks/{McpSettingsPage-Cdej9Evi.js.map → McpSettingsPage-C5V_rj2V.js.map} +1 -1
  36. package/dist/chunks/{MemberAssignmentSheet-AKvLbbYW.js → MemberAssignmentSheet-DL0fg1Yf.js} +2 -2
  37. package/dist/chunks/{MemberAssignmentSheet-AKvLbbYW.js.map → MemberAssignmentSheet-DL0fg1Yf.js.map} +1 -1
  38. package/dist/chunks/{MenuCommandPalette-ia0pSIFg.js → MenuCommandPalette-C2PvK3Gh.js} +2 -2
  39. package/dist/chunks/{MenuCommandPalette-ia0pSIFg.js.map → MenuCommandPalette-C2PvK3Gh.js.map} +1 -1
  40. package/dist/chunks/{MenusPage-Bh55bvKH.js → MenusPage-Bytd1HlW.js} +2 -2
  41. package/dist/chunks/{MenusPage-Bh55bvKH.js.map → MenusPage-Bytd1HlW.js.map} +1 -1
  42. package/dist/chunks/{MonacoPreviewPanel-CT_sjf7Z.js → MonacoPreviewPanel-BZrNIcFZ.js} +2 -2
  43. package/dist/chunks/{MonacoPreviewPanel-CT_sjf7Z.js.map → MonacoPreviewPanel-BZrNIcFZ.js.map} +1 -1
  44. package/dist/chunks/{NavListPanel-DMIUMh7q.js → NavListPanel-zwUDIYSP.js} +2 -2
  45. package/dist/chunks/{NavListPanel-DMIUMh7q.js.map → NavListPanel-zwUDIYSP.js.map} +1 -1
  46. package/dist/chunks/{OperationLogsPage-7XVfmhxP.js → OperationLogsPage-D2BQdGNp.js} +2 -2
  47. package/dist/chunks/{OperationLogsPage-7XVfmhxP.js.map → OperationLogsPage-D2BQdGNp.js.map} +1 -1
  48. package/dist/chunks/{OperationLogsTable-gp-WZxiF.js → OperationLogsTable-DaaON4QR.js} +3 -3
  49. package/dist/chunks/{OperationLogsTable-gp-WZxiF.js.map → OperationLogsTable-DaaON4QR.js.map} +1 -1
  50. package/dist/chunks/{PDFPreviewPanel-CyfhyWav.js → PDFPreviewPanel-CMmnWBKW.js} +3 -3
  51. package/dist/chunks/{PDFPreviewPanel-CyfhyWav.js.map → PDFPreviewPanel-CMmnWBKW.js.map} +1 -1
  52. package/dist/chunks/{PositionsPage-Bwua1vNV.js → PositionsPage-CBZITssT.js} +2 -2
  53. package/dist/chunks/{PositionsPage-Bwua1vNV.js.map → PositionsPage-CBZITssT.js.map} +1 -1
  54. package/dist/chunks/{PreferencesSheetBody-C80D3dOM.js → PreferencesSheetBody-B0poTPEA.js} +2 -2
  55. package/dist/chunks/{PreferencesSheetBody-C80D3dOM.js.map → PreferencesSheetBody-B0poTPEA.js.map} +1 -1
  56. package/dist/chunks/{ProfilePage-DeTw-59a.js → ProfilePage-DLhacdWt.js} +2 -2
  57. package/dist/chunks/{ProfilePage-DeTw-59a.js.map → ProfilePage-DLhacdWt.js.map} +1 -1
  58. package/dist/chunks/{ProjectInfoPage-Bqn3gfXa.js → ProjectInfoPage-CJpGxpu5.js} +2 -2
  59. package/dist/chunks/{ProjectInfoPage-Bqn3gfXa.js.map → ProjectInfoPage-CJpGxpu5.js.map} +1 -1
  60. package/dist/chunks/{RelationPanel-Bio-ig-l.js → RelationPanel-BsbfEDo2.js} +2 -2
  61. package/dist/chunks/{RelationPanel-Bio-ig-l.js.map → RelationPanel-BsbfEDo2.js.map} +1 -1
  62. package/dist/chunks/{RolesPage-fwUSs2HR.js → RolesPage-46dzdiah.js} +2 -2
  63. package/dist/chunks/{RolesPage-fwUSs2HR.js.map → RolesPage-46dzdiah.js.map} +1 -1
  64. package/dist/chunks/{SessionConflictModal-Dw4opoNP.js → SessionConflictModal-Bm38eExL.js} +2 -2
  65. package/dist/chunks/{SessionConflictModal-Dw4opoNP.js.map → SessionConflictModal-Bm38eExL.js.map} +1 -1
  66. package/dist/chunks/SettingModuleDetail-BjxUJeH5.js +17 -0
  67. package/dist/chunks/SettingModuleDetail-BjxUJeH5.js.map +1 -0
  68. package/dist/chunks/{SettingsPage-Cy5-q-P6.js → SettingsPage-C32Vuizm.js} +2 -2
  69. package/dist/chunks/{SettingsPage-Cy5-q-P6.js.map → SettingsPage-C32Vuizm.js.map} +1 -1
  70. package/dist/chunks/{StatCard-BROG4NSp.js → StatCard-CRPfkSBY.js} +2 -2
  71. package/dist/chunks/{StatCard-BROG4NSp.js.map → StatCard-CRPfkSBY.js.map} +1 -1
  72. package/dist/chunks/{UserGroupsPage-CsTBkUPy.js → UserGroupsPage-08G5MdGm.js} +2 -2
  73. package/dist/chunks/{UserGroupsPage-CsTBkUPy.js.map → UserGroupsPage-08G5MdGm.js.map} +1 -1
  74. package/dist/chunks/{UserLogsPanels-3u8Z93YU.js → UserLogsPanels-nbIrMym9.js} +2 -2
  75. package/dist/chunks/{UserLogsPanels-3u8Z93YU.js.map → UserLogsPanels-nbIrMym9.js.map} +1 -1
  76. package/dist/chunks/{UserTransferSelect-ChLj4EkS.js → UserTransferSelect-C8betaWG.js} +2 -2
  77. package/dist/chunks/{UserTransferSelect-ChLj4EkS.js.map → UserTransferSelect-C8betaWG.js.map} +1 -1
  78. package/dist/chunks/{UsersPage-CfNnQJi4.js → UsersPage-BjTiNEqF.js} +2 -2
  79. package/dist/chunks/{UsersPage-CfNnQJi4.js.map → UsersPage-BjTiNEqF.js.map} +1 -1
  80. package/dist/chunks/{browser-BKLM0ThC-D0TgzKTC.js → browser-BKLM0ThC-Cp7Gi4-F.js} +1 -1
  81. package/dist/chunks/{code-BkkeN2fd.js → code-LswxAr98.js} +1 -1
  82. package/dist/chunks/{direct-engine-m32m22nx.js → direct-engine-Dv_6v4sx.js} +1 -1
  83. package/dist/chunks/{html-BGNWR_Bp.js → html-BokjxlLb.js} +1 -1
  84. package/dist/chunks/{line-CsTeuEcK.js → line-Bqh6atHH.js} +1 -1
  85. package/dist/chunks/{lrc-D6gjnGZ5.js → lrc-T5e1svZG.js} +1 -1
  86. package/dist/chunks/{markdown-BglUe9j8.js → markdown-BpWokN1s.js} +1 -1
  87. package/dist/chunks/{patch-C97Yl3jr.js → patch-BYVX5C9G.js} +1 -1
  88. package/dist/chunks/{worker-engine-B7_3qQVS.js → worker-engine-CtUAeI4G.js} +1 -1
  89. package/dist/index.js +1 -1
  90. package/dist/{src-DAkFn9SG.js → src-D6UlLiVN.js} +2 -2
  91. package/dist/{src-DAkFn9SG.js.map → src-D6UlLiVN.js.map} +1 -1
  92. package/package.json +9 -9
  93. package/dist/chunks/ApiTokensPage-DiRMgViD.js +0 -2
  94. package/dist/chunks/ApiTokensPage-DiRMgViD.js.map +0 -1
  95. package/dist/chunks/EntityContextRuntime-C9q95WDf.js +0 -1
  96. package/dist/chunks/SettingModuleDetail-I9CSJ-fh.js +0 -17
  97. package/dist/chunks/SettingModuleDetail-I9CSJ-fh.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"FilesPage-D_iYsUwl.js","mappings":"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","names":["__iconData","__iconData$8","createLucideIcon","__iconData$7","__iconData$6","__iconData$5","File","__iconData$4","__iconData$3","__iconData$2","__iconData$1","getFileIcon","lower","fileName","NAME_MAP","dot","ext","EXT_MAP","DEFAULT_FILE_ICON","getFileFullUrl","url","config","getIconIdForMime","mime","mimeType","isOfdFile","isArchiveFile","getFileTypeIcon","iconId","byName","FileTypeIcon","iconSize","canPreviewFile","resolvedMimeType","resolveFileMimeType","isEmailFile","isMindMapFile","isDrawingFile","isDataAssetFile","isGeoFile","isSpreadsheetFile","isWordFile","isPresentationFile","isMarkdownFile","isPlainTextFile","isJsonFile","isSvgFile","isCodeFile","EMAIL_MIME_TYPES","DRAWING_MIME_TYPES","DATA_ASSET_MIME_TYPES","isGalleryImageFile","GEO_MIME_TYPES","isZipFile","ARCHIVE_MIME_TYPES","fetchProtectedFile","id","extractManagedFileId","operation","filesPrivateContent","filesContent","goTransport","response","MANAGED_FILE_CONTENT_URL","fetchManagedFileBlob","fileId","__vitePreload","access","getFileAccessUrl","prefix","escapeRegExp","buildManagedFileActions","record","preview","onDetail","onCopyUrl","canDelete","confirmDelete","onDelete","revokePreviewUrl","data","PreviewModalShell","AppModal","fullscreen","viewportHeight","Suspense","Spin","FilePreviewModal","setFullscreen","useState","toggleFullscreen","useCallback","f","useThemeController","useMemo","previewKind","isImage","isMpegTsAsCode","previewQuery","useQuery","contractKey","fileContract","visible","fileUrl","blob","previewData","useEffect","onFallback","onClose","Toast","handleClose","previewTitle","Typography","Modal","PDFPreviewPanel","isPresentation","isWide","FileViewerPreviewPanel","isRawText","displayContent","MarkdownPreviewPanel","JsonPreviewPanel","MonacoPreviewPanel","createPortal","AudioPlayer","isDark","X","VideoPlayer","lazy","n","m","PreviewWatermark","lines","content","key","style","setStyle","tile","generateWatermarkTile","FilePreviewLayer","_Fragment","ImagePreview","v","watermark","FileDetailModal","Space","Button","file","loading","Descriptions","formatBytes","extraRows","row","Text","resolveUrl","formatDateTime","Text$3","useFilePreview","previewVisible","setPreviewVisible","previewSrcList","setPreviewSrcList","previewCurrentIndex","setPreviewCurrentIndex","previewLoadingId","setPreviewLoadingId","filePreview","setFilePreview","downloadLoadingId","setDownloadLoadingId","previewBlobUrlsRef","useRef","previewSessionRef","cleanupPreviewBlobs","objectUrl","error","imageFiles","getImageFiles","clickedIndex","mySession","initialUrls","clickedFile","clickedBlob","clickedUrl","createDisplayableImageUrl","i","imgFile","updated","prev","link","withViewportMenuStyle","cloneElement","viewportMenuStyle","getPopupContainer","CursorContextDropdown","rePosKey","contextKey","point","closedKeyRef","closeOnce","Dropdown","render","FileGridCard","ctxPos","setCtxPos","isPreviewable","selected","canSelect","e","onSelect","onPreview","Checkbox","previewLoading","Tooltip","onDownload","confirmAndDelete","FileNameCell","parts","highlight","name","icon","part","index","onClick","StorageFileBrowser","usePermission","viewMode","setViewMode","currentPath","setCurrentPath","usePagination","browseQuery","useStorageBrowse","browseData","deleteMutation","useDeleteFiles","historyStack","setHistoryStack","historyIndex","setHistoryIndex","canGoBack","canGoForward","detailFile","setDetailFile","detailQuery","useFileDetail","displayedDetailFile","detailFileLoading","setPage","navigateTo","path","pushHistory","next","navigateToFolder","folder","navigateToPath","goBack","newIndex","goForward","breadcrumbSegments","handleDelete","handleOpenDetail","handleCopyUrl","copyTextWithToast","listColumns","Folder","previewable","DateTimeText","createOperationColumn","hasPermission","allItems","totalCount","pagedItems","page","SideSheet","ChevronLeft","ChevronRight","Breadcrumb","segPath","idx","isLast","seg","ListIcon","LayoutGrid","ConfigurableTable","pagedFolders","r","pagedFiles","Pagination","Text$2","normalizeOptional","value","buildPayload","fileStorageConfigContract","values","PRESIGNED_EXPIRY_DEFAULT_SECONDS","provider","getStorageSummary","EMPTY_PLACEHOLDER","FileStorageConfigsPanel","useListPage","useFileStorageConfigList","formProvider","setFormProvider","formIsDefault","setFormIsDefault","browsingConfig","setBrowsingConfig","saveMutation","useSaveFileStorageConfig","statusMutation","modal","useEditModal","formSaveMutation","useFileStorageConfigDetail","isEdit","useDeleteFileStorageConfigs","setDefaultMutation","useSetDefaultFileStorageConfig","testMutation","useTestFileStorageConfig","modalTestLoading","testingConfigId","openCreate","openEdit","handleModalTest","payload","handleSetDefault","handleTestSaved","status","useStatusToggle","enabled","abortSubmit","operationColumn","useCrudOperationColumn","columns","renderEllipsis","Tag","FILE_STORAGE_PROVIDER_LABELS","isDefault","label","summary","OBJECT_ACL_PROVIDERS","colorMap","\"default\"","acl","FILE_OBJECT_ACL_LABELS","strategy","FILE_URL_STRATEGY_LABELS","dateTimeColumn","buildTableSearch","CreateButton","ExportButton","filterQuery","tableProps","EditFormSheet","PlugZap","runSubmitAction","Form","Row","Col","FILE_STORAGE_PROVIDER_OPTIONS","option","currentAcl","FILE_OBJECT_ACL_SUPPORT","Select","Switch","FormPasswordInput","Radio","IS_GO_FOUNDATION","FILE_URL_STRATEGY_OPTIONS","opt","PRESIGNED_EXPIRY_MIN_SECONDS","PRESIGNED_EXPIRY_MAX_SECONDS","Text$1","getProgressStroke","uploadSingleFile","updateItem","setItems","item","uid","updater","onDone","onFail","signal","err","chunkThreshold","chunkedUpload","percent","uploadFile","loadImageResolution","src","external","img","cleanup","resolve","FileListPanel","queryClient","useQueryClient","usePreferences","defaultSearchParams","fileInputRef","uploadItems","setUploadItems","uploadProgressVisible","setUploadProgressVisible","uploadControllersRef","useListSearch","fileKeys","preferences","listPagination","gridPagination","FILE_GRID_PAGE_SIZE","FILE_GRID_PAGE_SIZE_OPTIONS","selectedRowKeys","setSelectedRowKeys","batchDownloadLoading","setBatchDownloadLoading","imageResolution","setImageResolution","defaultConfig","useDefaultFileStorageConfig","startDate","endDate","formatDateTimeRangeValuesForApi","submittedParams","useFilterQuery","filesList","listQuery","useFileList","active","uploadFileMutation","useUploadFile","useChunkUploadThreshold","batchDeleteMutation","toggleViewMode","canEditPreference","setPreferences","mode","handleGridSelect","checked","k","handleGridSelectAll","currentPageIds","isUploadFinished","successCount","timer","invalidateAfterFilesAdded","handlePickFile","handleUpload","files","event","items","controller","handleCancelUpload","CHUNKED_UPLOAD_CANCELLED","handleBatchDelete","handleBatchDownload","filesBatchDownload","downloadBlob","size","fileTableSearch","KeywordInput","bindKeyword","FilterSelect","bind","FILE_TYPE_FILTER_OPTIONS","DateRangeFilter","handleSearch","handleReset","Plus","FolderDown","BatchDeleteButton","TableSearchPanel","CheckCircle2","XCircle","Progress","listTableProps","buildPagination","keys","selectedOnPage","allSelected","someSelected","List","p","setPageSize","UPLOAD_ACTIVE_STATUSES","CartesianChartSpecTransformer","seriesSpec","spec","setDefaultCrosshairForCartesianChart","LineChart","BaseChart","LineChartSpecTransformer","SeriesTypeEnum","getCartesianDimensionInfo","getDimensionInfoByValue","getCartesianCrosshairRect","mixin","StackChartMixin","registerDimensionTooltipProcessor","registerMarkTooltipProcessor","registerGroupTooltipProcessor","registerDimensionEvents","registerDimensionHover","registerLineSeries","Factory","createChart","VChart","registerLineChart","registerLabel","registers","DataBar","ratio","max","minPercent","classes","className","barStyle","height","color","fillOpacity","track","radius","FileStatsPanel","palette","useChartPalette","statsQuery","useFileStats","stats","totalFiles","providerData","PROVIDER_LABELS","PROVIDER_COLORS","uploaderData","u","providerSpec","makeBarSpec","d","monthlySpec","makeLineSpec","sizeRangeSpec","uploaderSpec","StatGrid","StatCard","FILE_TYPE_CONFIG","stat","type","count","bgColor","Icon","sectionTitleStyle","BarChart","chartOptions","FileImage","Video","Music","FileText","FilesPage","canConfigure","canList","sections","SECTIONS","section","navigation","useSectionSelection","activeSection","Empty","MasterDetailLayout","NavListPanel","SectionNavigation","HardDriveUpload","Files","BarChart3"],"ignoreList":[0,1,2,3,4,5,6,7,8,23,24,25],"sources":["../../../../node_modules/lucide-react/dist/esm/icons/eighth-notes.mjs","../../../../node_modules/lucide-react/dist/esm/icons/file-image.mjs","../../../../node_modules/lucide-react/dist/esm/icons/file-text.mjs","../../../../node_modules/lucide-react/dist/esm/icons/file.mjs","../../../../node_modules/lucide-react/dist/esm/icons/files.mjs","../../../../node_modules/lucide-react/dist/esm/icons/folder-down.mjs","../../../../node_modules/lucide-react/dist/esm/icons/folder.mjs","../../../../node_modules/lucide-react/dist/esm/icons/hard-drive-upload.mjs","../../../../node_modules/lucide-react/dist/esm/icons/video.mjs","../../src/utils/fileIcons.ts","../../src/utils/file-utils.tsx","../../src/utils/managed-file-actions.ts","../../src/components/FilePreviewModal/index.tsx","../../src/components/PreviewWatermark.tsx","../../src/components/FilePreviewLayer.tsx","../../src/components/FileDetailModal.tsx","../../src/hooks/useFilePreview.ts","../../src/components/CursorContextDropdown.tsx","../../src/pages/system/files/components/FileGridCard.tsx","../../src/components/FileNameCell.tsx","../../src/pages/system/file-configs/StorageFileBrowser.tsx","../../src/pages/system/file-configs/FileStorageConfigsPanel.tsx","../../src/pages/system/files/FileListPanel.tsx","../../../../node_modules/@visactor/vchart/esm/chart/line/line-transformer.js","../../../../node_modules/@visactor/vchart/esm/chart/line/line.js","../../../../node_modules/@visactor/react-vchart/esm/charts/LineChart.js","../../src/components/data-viz/DataBar.tsx","../../src/pages/system/files/FileStatsPanel.tsx","../../src/pages/system/files/FilesPage.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"eighth-notes\",\n size: 24,\n node: [\n [\"path\", { d: \"M21 16V3a1 1 0 0 0-1.28-.96l-10 2.917a1 1 0 0 0-.72.96V19\", key: \"uy6ho8\" }],\n [\"circle\", { cx: \"18\", cy: \"16\", r: \"3\", key: \"1hluhg\" }],\n [\"circle\", { cx: \"6\", cy: \"19\", r: \"3\", key: \"1kj8tv\" }]\n ],\n aliases: [\"music\"]\n};\n__iconData.node;\nconst EighthNotes = createLucideIcon(__iconData);\n\nexport { __iconData, EighthNotes as default };\n//# sourceMappingURL=eighth-notes.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"file-image\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\",\n key: \"1oefj6\"\n }\n ],\n [\"path\", { d: \"M14 2v5a1 1 0 0 0 1 1h5\", key: \"wfsgrz\" }],\n [\"circle\", { cx: \"10\", cy: \"12\", r: \"2\", key: \"737tya\" }],\n [\"path\", { d: \"m20 17-1.296-1.296a2.41 2.41 0 0 0-3.408 0L9 22\", key: \"wt3hpn\" }]\n ]\n};\n__iconData.node;\nconst FileImage = createLucideIcon(__iconData);\n\nexport { __iconData, FileImage as default };\n//# sourceMappingURL=file-image.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"file-text\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\",\n key: \"1oefj6\"\n }\n ],\n [\"path\", { d: \"M14 2v5a1 1 0 0 0 1 1h5\", key: \"wfsgrz\" }],\n [\"path\", { d: \"M10 9H8\", key: \"b1mrlr\" }],\n [\"path\", { d: \"M16 13H8\", key: \"t4e002\" }],\n [\"path\", { d: \"M16 17H8\", key: \"z1uh3a\" }]\n ]\n};\n__iconData.node;\nconst FileText = createLucideIcon(__iconData);\n\nexport { __iconData, FileText as default };\n//# sourceMappingURL=file-text.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"file\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\",\n key: \"1oefj6\"\n }\n ],\n [\"path\", { d: \"M14 2v5a1 1 0 0 0 1 1h5\", key: \"wfsgrz\" }]\n ]\n};\n__iconData.node;\nconst File = createLucideIcon(__iconData);\n\nexport { __iconData, File as default };\n//# sourceMappingURL=file.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"files\",\n size: 24,\n node: [\n [\"path\", { d: \"M15 2h-4a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V8\", key: \"14sh0y\" }],\n [\n \"path\",\n {\n d: \"M16.706 2.706A2.4 2.4 0 0 0 15 2v5a1 1 0 0 0 1 1h5a2.4 2.4 0 0 0-.706-1.706z\",\n key: \"1970lx\"\n }\n ],\n [\"path\", { d: \"M5 7a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h8a2 2 0 0 0 1.732-1\", key: \"l4dndm\" }]\n ]\n};\n__iconData.node;\nconst Files = createLucideIcon(__iconData);\n\nexport { __iconData, Files as default };\n//# sourceMappingURL=files.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"folder-down\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\",\n key: \"1kt360\"\n }\n ],\n [\"path\", { d: \"M12 10v6\", key: \"1bos4e\" }],\n [\"path\", { d: \"m15 13-3 3-3-3\", key: \"6j2sf0\" }]\n ]\n};\n__iconData.node;\nconst FolderDown = createLucideIcon(__iconData);\n\nexport { __iconData, FolderDown as default };\n//# sourceMappingURL=folder-down.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"folder\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\",\n key: \"1kt360\"\n }\n ]\n ]\n};\n__iconData.node;\nconst Folder = createLucideIcon(__iconData);\n\nexport { __iconData, Folder as default };\n//# sourceMappingURL=folder.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"hard-drive-upload\",\n size: 24,\n node: [\n [\"path\", { d: \"m16 6-4-4-4 4\", key: \"13yo43\" }],\n [\"path\", { d: \"M12 2v8\", key: \"1q4o3n\" }],\n [\"rect\", { width: \"20\", height: \"8\", x: \"2\", y: \"14\", rx: \"2\", key: \"w68u3i\" }],\n [\"path\", { d: \"M6 18h.01\", key: \"uhywen\" }],\n [\"path\", { d: \"M10 18h.01\", key: \"h775k\" }]\n ]\n};\n__iconData.node;\nconst HardDriveUpload = createLucideIcon(__iconData);\n\nexport { __iconData, HardDriveUpload as default };\n//# sourceMappingURL=hard-drive-upload.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"video\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5\",\n key: \"ftymec\"\n }\n ],\n [\"rect\", { x: \"2\", y: \"6\", width: \"14\", height: \"12\", rx: \"2\", key: \"158x01\" }]\n ]\n};\n__iconData.node;\nconst Video = createLucideIcon(__iconData);\n\nexport { __iconData, Video as default };\n//# sourceMappingURL=video.mjs.map\n","/**\n * 文件 / 文件夹图标映射表\n *\n * 使用 Iconify vscode-icons 图标集的图标 id,视觉风格与 VS Code material-icon-theme 高度一致。\n * 图标本体不再由运行时从公网 Iconify API 拉取:`scripts/gen-iconify-assets.mjs` 在构建期扫描这里的字面量,\n * 从 @iconify-json/vscode-icons 抽成 `assets/file-icons/*.svg`(文件夹图标连同 `-opened` 展开态),\n * 由 `components/FileTypeIcon` 按需渲染;新增映射后运行 `npm run icons:iconify -w @fudanda/web`。\n */\n/** 文件扩展名 → iconify 图标 ID */\nconst EXT_MAP: Readonly<Record<string, string>> = {\n // TypeScript\n ts: 'vscode-icons:file-type-typescript',\n tsx: 'vscode-icons:file-type-typescript',\n // JavaScript\n js: 'vscode-icons:file-type-js-official',\n jsx: 'vscode-icons:file-type-js-official',\n mjs: 'vscode-icons:file-type-js-official',\n cjs: 'vscode-icons:file-type-js-official',\n // JSON\n json: 'vscode-icons:file-type-json',\n jsonc: 'vscode-icons:file-type-json',\n json5: 'vscode-icons:file-type-json',\n // Markdown\n md: 'vscode-icons:file-type-markdown',\n mdx: 'vscode-icons:file-type-markdown',\n markdown: 'vscode-icons:file-type-markdown',\n // HTML\n html: 'vscode-icons:file-type-html',\n htm: 'vscode-icons:file-type-html',\n xhtml: 'vscode-icons:file-type-html',\n // CSS\n css: 'vscode-icons:file-type-css',\n scss: 'vscode-icons:file-type-scss',\n sass: 'vscode-icons:file-type-sass',\n less: 'vscode-icons:file-type-less',\n styl: 'vscode-icons:file-type-stylus',\n stylus: 'vscode-icons:file-type-stylus',\n // Python\n py: 'vscode-icons:file-type-python',\n pyi: 'vscode-icons:file-type-python',\n // Go\n go: 'vscode-icons:file-type-go',\n // Rust\n rs: 'vscode-icons:file-type-rust',\n // Java\n java: 'vscode-icons:file-type-java',\n // C / C++\n c: 'vscode-icons:file-type-c',\n h: 'vscode-icons:file-type-c',\n cpp: 'vscode-icons:file-type-cpp',\n cc: 'vscode-icons:file-type-cpp',\n cxx: 'vscode-icons:file-type-cpp',\n hpp: 'vscode-icons:file-type-cpp',\n hxx: 'vscode-icons:file-type-cpp',\n // C#\n cs: 'vscode-icons:file-type-csharp',\n // PHP\n php: 'vscode-icons:file-type-php',\n // Ruby\n rb: 'vscode-icons:file-type-ruby',\n // Swift\n swift: 'vscode-icons:file-type-swift',\n // Kotlin\n kt: 'vscode-icons:file-type-kotlin',\n kts: 'vscode-icons:file-type-kotlin',\n // Dart\n dart: 'vscode-icons:file-type-dartlang',\n // Scala\n scala: 'vscode-icons:file-type-scala',\n // Lua\n lua: 'vscode-icons:file-type-lua',\n // R\n r: 'vscode-icons:file-type-r',\n // YAML\n yml: 'vscode-icons:file-type-yaml',\n yaml: 'vscode-icons:file-type-yaml',\n // XML\n xml: 'vscode-icons:file-type-xml',\n xsl: 'vscode-icons:file-type-xml',\n xslt: 'vscode-icons:file-type-xml',\n // SQL\n sql: 'vscode-icons:file-type-sql',\n // Shell\n sh: 'vscode-icons:file-type-shell',\n bash: 'vscode-icons:file-type-shell',\n zsh: 'vscode-icons:file-type-shell',\n fish: 'vscode-icons:file-type-shell',\n // PowerShell\n ps1: 'vscode-icons:file-type-powershell',\n psm1: 'vscode-icons:file-type-powershell',\n // Batch\n bat: 'vscode-icons:file-type-bat',\n cmd: 'vscode-icons:file-type-bat',\n // Config / env\n env: 'vscode-icons:file-type-dotenv',\n ini: 'vscode-icons:file-type-config',\n conf: 'vscode-icons:file-type-config',\n cfg: 'vscode-icons:file-type-config',\n toml: 'vscode-icons:file-type-toml',\n // Text\n txt: 'vscode-icons:file-type-text',\n log: 'vscode-icons:file-type-log',\n // Lock\n lock: 'vscode-icons:default-file',\n // Images\n png: 'vscode-icons:file-type-image',\n jpg: 'vscode-icons:file-type-image',\n jpeg: 'vscode-icons:file-type-image',\n gif: 'vscode-icons:file-type-image',\n bmp: 'vscode-icons:file-type-image',\n webp: 'vscode-icons:file-type-image',\n ico: 'vscode-icons:file-type-image',\n tiff: 'vscode-icons:file-type-image',\n svg: 'vscode-icons:file-type-svg',\n // Video\n mp4: 'vscode-icons:file-type-video',\n mov: 'vscode-icons:file-type-video',\n avi: 'vscode-icons:file-type-video',\n mkv: 'vscode-icons:file-type-video',\n webm: 'vscode-icons:file-type-video',\n // Audio\n mp3: 'vscode-icons:file-type-audio',\n wav: 'vscode-icons:file-type-audio',\n ogg: 'vscode-icons:file-type-audio',\n flac: 'vscode-icons:file-type-audio',\n // Archive\n zip: 'vscode-icons:file-type-zip',\n tar: 'vscode-icons:file-type-zip',\n gz: 'vscode-icons:file-type-zip',\n rar: 'vscode-icons:file-type-zip',\n '7z': 'vscode-icons:file-type-zip',\n // PDF\n pdf: 'vscode-icons:file-type-pdf2',\n // Vue / Svelte\n vue: 'vscode-icons:file-type-vue',\n svelte: 'vscode-icons:file-type-svelte',\n // GraphQL\n graphql: 'vscode-icons:file-type-graphql',\n gql: 'vscode-icons:file-type-graphql',\n // Prisma\n prisma: 'vscode-icons:file-type-prisma',\n // Protobuf\n proto: 'vscode-icons:file-type-protobuf',\n // WASM\n wasm: 'vscode-icons:file-type-wasm',\n // Certificate / Key\n pem: 'vscode-icons:file-type-cert',\n crt: 'vscode-icons:file-type-cert',\n cer: 'vscode-icons:file-type-cert',\n key: 'vscode-icons:file-type-key',\n // Binary\n exe: 'vscode-icons:file-type-binary',\n dll: 'vscode-icons:file-type-binary',\n so: 'vscode-icons:file-type-binary',\n dylib: 'vscode-icons:file-type-binary',\n // 字体\n woff: 'vscode-icons:file-type-font',\n woff2: 'vscode-icons:file-type-font',\n ttf: 'vscode-icons:file-type-font',\n eot: 'vscode-icons:file-type-font',\n otf: 'vscode-icons:file-type-font',\n // Jupyter\n ipynb: 'vscode-icons:file-type-jupyter',\n // 数据库\n db: 'vscode-icons:file-type-db',\n sqlite: 'vscode-icons:file-type-db',\n sqlite3: 'vscode-icons:file-type-db',\n // Office 文档\n doc: 'vscode-icons:file-type-word',\n docx: 'vscode-icons:file-type-word',\n xls: 'vscode-icons:file-type-excel',\n xlsx: 'vscode-icons:file-type-excel',\n csv: 'vscode-icons:file-type-excel2',\n tsv: 'vscode-icons:file-type-excel2',\n ppt: 'vscode-icons:file-type-powerpoint',\n pptx: 'vscode-icons:file-type-powerpoint',\n // 模板语言\n erb: 'vscode-icons:file-type-erb',\n haml: 'vscode-icons:file-type-haml',\n slim: 'vscode-icons:file-type-slim',\n jinja: 'vscode-icons:file-type-jinja',\n j2: 'vscode-icons:file-type-jinja',\n twig: 'vscode-icons:file-type-twig',\n astro: 'vscode-icons:file-type-astro',\n // 函数式语言\n ex: 'vscode-icons:file-type-elixir',\n exs: 'vscode-icons:file-type-elixir',\n hs: 'vscode-icons:file-type-haskell',\n lhs: 'vscode-icons:file-type-haskell',\n erl: 'vscode-icons:file-type-erlang',\n hrl: 'vscode-icons:file-type-erlang',\n clj: 'vscode-icons:file-type-clojure',\n cljs: 'vscode-icons:file-type-clojure',\n cljc: 'vscode-icons:file-type-clojure',\n edn: 'vscode-icons:file-type-clojure',\n fs: 'vscode-icons:file-type-fsharp',\n fsi: 'vscode-icons:file-type-fsharp',\n fsx: 'vscode-icons:file-type-fsharp',\n ml: 'vscode-icons:file-type-ocaml',\n mli: 'vscode-icons:file-type-ocaml',\n // 新兴语言\n nim: 'vscode-icons:file-type-nim',\n zig: 'vscode-icons:file-type-zig',\n cr: 'vscode-icons:file-type-crystal',\n d: 'vscode-icons:file-type-dlang',\n coffee: 'vscode-icons:file-type-coffeescript',\n // Terraform / HCL\n tf: 'vscode-icons:file-type-terraform',\n tfvars: 'vscode-icons:file-type-terraform',\n hcl: 'vscode-icons:file-type-terraform',\n // Nix\n nix: 'vscode-icons:file-type-nix',\n // JVM 编译产物\n jar: 'vscode-icons:file-type-java',\n class: 'vscode-icons:file-type-java',\n};\n/** 特殊文件名(完整,小写)→ iconify 图标 ID */\nconst NAME_MAP: Readonly<Record<string, string>> = {\n dockerfile: 'vscode-icons:file-type-docker',\n 'docker-compose.yml': 'vscode-icons:file-type-docker',\n 'docker-compose.yaml': 'vscode-icons:file-type-docker',\n 'docker-compose.override.yml': 'vscode-icons:file-type-docker',\n makefile: 'vscode-icons:file-type-makefile',\n '.gitignore': 'vscode-icons:file-type-git',\n '.gitattributes': 'vscode-icons:file-type-git',\n '.gitmodules': 'vscode-icons:file-type-git',\n '.env': 'vscode-icons:file-type-dotenv',\n '.env.local': 'vscode-icons:file-type-dotenv',\n '.env.example': 'vscode-icons:file-type-dotenv',\n '.env.development': 'vscode-icons:file-type-dotenv',\n '.env.production': 'vscode-icons:file-type-dotenv',\n '.env.test': 'vscode-icons:file-type-dotenv',\n 'package.json': 'vscode-icons:file-type-npm',\n 'package-lock.json': 'vscode-icons:file-type-npm',\n 'yarn.lock': 'vscode-icons:file-type-yarn',\n '.yarnrc': 'vscode-icons:file-type-yarn',\n '.yarnrc.yml': 'vscode-icons:file-type-yarn',\n 'pnpm-lock.yaml': 'vscode-icons:file-type-pnpm',\n '.pnpmfile.cjs': 'vscode-icons:file-type-pnpm',\n 'bun.lockb': 'vscode-icons:file-type-bun',\n 'bunfig.toml': 'vscode-icons:file-type-bun',\n 'tsconfig.json': 'vscode-icons:file-type-tsconfig',\n 'tsconfig.node.json': 'vscode-icons:file-type-tsconfig',\n 'tsconfig.base.json': 'vscode-icons:file-type-tsconfig',\n 'jsconfig.json': 'vscode-icons:file-type-jsconfig',\n 'vite.config.ts': 'vscode-icons:file-type-vite',\n 'vite.config.js': 'vscode-icons:file-type-vite',\n 'vite.config.mts': 'vscode-icons:file-type-vite',\n 'vite.config.mjs': 'vscode-icons:file-type-vite',\n 'vitest.config.ts': 'vscode-icons:file-type-vitest',\n 'vitest.config.js': 'vscode-icons:file-type-vitest',\n 'webpack.config.js': 'vscode-icons:file-type-webpack',\n 'webpack.config.ts': 'vscode-icons:file-type-webpack',\n 'rollup.config.js': 'vscode-icons:file-type-rollup',\n 'rollup.config.ts': 'vscode-icons:file-type-rollup',\n 'eslint.config.js': 'vscode-icons:file-type-eslint',\n 'eslint.config.ts': 'vscode-icons:file-type-eslint',\n 'eslint.config.mjs': 'vscode-icons:file-type-eslint',\n '.eslintrc': 'vscode-icons:file-type-eslint',\n '.eslintrc.js': 'vscode-icons:file-type-eslint',\n '.eslintrc.json': 'vscode-icons:file-type-eslint',\n '.eslintrc.yml': 'vscode-icons:file-type-eslint',\n '.eslintignore': 'vscode-icons:file-type-eslint',\n '.prettierrc': 'vscode-icons:file-type-prettier',\n '.prettierrc.js': 'vscode-icons:file-type-prettier',\n '.prettierrc.json': 'vscode-icons:file-type-prettier',\n '.prettierrc.yml': 'vscode-icons:file-type-prettier',\n '.prettierignore': 'vscode-icons:file-type-prettier',\n 'readme.md': 'vscode-icons:file-type-markdown',\n readme: 'vscode-icons:file-type-markdown',\n license: 'vscode-icons:file-type-license',\n 'license.md': 'vscode-icons:file-type-license',\n licence: 'vscode-icons:file-type-license',\n 'changelog.md': 'vscode-icons:file-type-log',\n changelog: 'vscode-icons:file-type-log',\n '.editorconfig': 'vscode-icons:file-type-editorconfig',\n '.nvmrc': 'vscode-icons:file-type-node',\n '.node-version': 'vscode-icons:file-type-node',\n 'jest.config.js': 'vscode-icons:file-type-jest',\n 'jest.config.ts': 'vscode-icons:file-type-jest',\n 'babel.config.js': 'vscode-icons:file-type-babel',\n 'babel.config.json': 'vscode-icons:file-type-babel',\n '.babelrc': 'vscode-icons:file-type-babel',\n 'tailwind.config.js': 'vscode-icons:file-type-tailwind',\n 'tailwind.config.ts': 'vscode-icons:file-type-tailwind',\n 'postcss.config.js': 'vscode-icons:file-type-postcss',\n 'next.config.js': 'vscode-icons:file-type-next',\n 'next.config.ts': 'vscode-icons:file-type-next',\n 'nuxt.config.ts': 'vscode-icons:file-type-nuxt',\n 'nuxt.config.js': 'vscode-icons:file-type-nuxt',\n 'astro.config.mjs': 'vscode-icons:file-type-astro',\n 'drizzle.config.ts': 'vscode-icons:file-type-drizzle-orm',\n 'drizzle.config.js': 'vscode-icons:file-type-drizzle-orm',\n // Python 项目文件\n 'requirements.txt': 'vscode-icons:file-type-python',\n 'requirements-dev.txt': 'vscode-icons:file-type-python',\n 'setup.py': 'vscode-icons:file-type-python',\n 'setup.cfg': 'vscode-icons:file-type-python',\n 'pyproject.toml': 'vscode-icons:file-type-python',\n pipfile: 'vscode-icons:file-type-python',\n 'pipfile.lock': 'vscode-icons:file-type-python',\n 'tox.ini': 'vscode-icons:file-type-python',\n '.python-version': 'vscode-icons:file-type-python',\n 'conda.yml': 'vscode-icons:file-type-python',\n 'environment.yml': 'vscode-icons:file-type-python',\n // Rust\n 'cargo.toml': 'vscode-icons:file-type-rust',\n 'cargo.lock': 'vscode-icons:file-type-rust',\n 'rust-toolchain': 'vscode-icons:file-type-rust',\n 'rust-toolchain.toml': 'vscode-icons:file-type-rust',\n // Go\n 'go.mod': 'vscode-icons:file-type-go',\n 'go.sum': 'vscode-icons:file-type-go',\n 'go.work': 'vscode-icons:file-type-go',\n // Java / Maven / Gradle\n 'pom.xml': 'vscode-icons:file-type-maven',\n 'build.gradle': 'vscode-icons:file-type-gradle',\n 'build.gradle.kts': 'vscode-icons:file-type-gradle',\n 'settings.gradle': 'vscode-icons:file-type-gradle',\n 'settings.gradle.kts': 'vscode-icons:file-type-gradle',\n 'gradle.properties': 'vscode-icons:file-type-gradle',\n gradlew: 'vscode-icons:file-type-gradle',\n // Ruby\n gemfile: 'vscode-icons:file-type-ruby',\n 'gemfile.lock': 'vscode-icons:file-type-ruby',\n rakefile: 'vscode-icons:file-type-ruby',\n '.ruby-version': 'vscode-icons:file-type-ruby',\n // PHP\n 'composer.json': 'vscode-icons:file-type-composer',\n 'composer.lock': 'vscode-icons:file-type-composer',\n '.htaccess': 'vscode-icons:file-type-apache',\n // npm / Node\n '.npmrc': 'vscode-icons:file-type-npm',\n '.npmignore': 'vscode-icons:file-type-npm',\n // 代码规范\n 'commitlint.config.js': 'vscode-icons:file-type-commitlint',\n 'commitlint.config.ts': 'vscode-icons:file-type-commitlint',\n 'commitlint.config.cjs': 'vscode-icons:file-type-commitlint',\n '.commitlintrc': 'vscode-icons:file-type-commitlint',\n '.commitlintrc.json': 'vscode-icons:file-type-commitlint',\n '.czrc': 'vscode-icons:file-type-commitlint',\n '.cz.json': 'vscode-icons:file-type-commitlint',\n '.stylelintrc': 'vscode-icons:file-type-stylelint',\n '.stylelintrc.json': 'vscode-icons:file-type-stylelint',\n '.stylelintrc.yml': 'vscode-icons:file-type-stylelint',\n '.stylelintignore': 'vscode-icons:file-type-stylelint',\n 'stylelint.config.js': 'vscode-icons:file-type-stylelint',\n // Monorepo / 工具类\n 'nx.json': 'vscode-icons:file-type-nx',\n 'turbo.json': 'vscode-icons:file-type-turbo',\n 'lerna.json': 'vscode-icons:file-type-lerna',\n 'rush.json': 'vscode-icons:file-type-npm',\n // CI / 依赖更新\n 'renovate.json': 'vscode-icons:file-type-renovate',\n '.renovaterc': 'vscode-icons:file-type-renovate',\n '.renovaterc.json': 'vscode-icons:file-type-renovate',\n '.travis.yml': 'vscode-icons:file-type-travis',\n 'codecov.yml': 'vscode-icons:file-type-yaml',\n // Web manifest\n 'robots.txt': 'vscode-icons:file-type-robots',\n 'manifest.json': 'vscode-icons:file-type-manifest',\n '.webmanifest': 'vscode-icons:file-type-manifest',\n // 部署 / Hosting\n 'firebase.json': 'vscode-icons:file-type-firebase',\n '.firebaserc': 'vscode-icons:file-type-firebase',\n 'vercel.json': 'vscode-icons:file-type-vercel',\n 'netlify.toml': 'vscode-icons:file-type-netlify',\n 'fly.toml': 'vscode-icons:file-type-toml',\n // Capacitor / Electron\n 'capacitor.config.ts': 'vscode-icons:file-type-capacitor',\n 'capacitor.config.json': 'vscode-icons:file-type-capacitor',\n 'electron-builder.config.js': 'vscode-icons:file-type-electron',\n 'electron-builder.yml': 'vscode-icons:file-type-electron',\n // Terraform\n 'main.tf': 'vscode-icons:file-type-terraform',\n 'variables.tf': 'vscode-icons:file-type-terraform',\n 'outputs.tf': 'vscode-icons:file-type-terraform',\n 'terraform.tfvars': 'vscode-icons:file-type-terraform',\n};\n/** 文件夹名(小写)→ iconify 图标 ID 基础值(不含 -open 后缀) */\nconst FOLDER_MAP: Readonly<Record<string, string>> = {\n src: 'vscode-icons:folder-type-src',\n source: 'vscode-icons:folder-type-src',\n node_modules: 'vscode-icons:folder-type-node',\n public: 'vscode-icons:folder-type-public',\n dist: 'vscode-icons:folder-type-dist',\n build: 'vscode-icons:folder-type-dist',\n out: 'vscode-icons:folder-type-dist',\n output: 'vscode-icons:folder-type-dist',\n '.git': 'vscode-icons:folder-type-git',\n '.github': 'vscode-icons:folder-type-github',\n components: 'vscode-icons:folder-type-component',\n pages: 'vscode-icons:folder-type-view',\n views: 'vscode-icons:folder-type-view',\n screens: 'vscode-icons:folder-type-view',\n hooks: 'vscode-icons:folder-type-hook',\n utils: 'vscode-icons:folder-type-tools',\n helpers: 'vscode-icons:folder-type-helper',\n styles: 'vscode-icons:folder-type-style',\n css: 'vscode-icons:folder-type-style',\n assets: 'vscode-icons:folder-type-asset',\n images: 'vscode-icons:folder-type-images',\n img: 'vscode-icons:folder-type-images',\n icons: 'vscode-icons:folder-type-images',\n fonts: 'vscode-icons:folder-type-fonts',\n tests: 'vscode-icons:folder-type-test',\n test: 'vscode-icons:folder-type-test',\n __tests__: 'vscode-icons:folder-type-test',\n spec: 'vscode-icons:folder-type-test',\n __mocks__: 'vscode-icons:folder-type-mock',\n mocks: 'vscode-icons:folder-type-mock',\n mock: 'vscode-icons:folder-type-mock',\n types: 'vscode-icons:folder-type-typings',\n typings: 'vscode-icons:folder-type-typings',\n interfaces: 'vscode-icons:folder-type-typings',\n api: 'vscode-icons:folder-type-api',\n routes: 'vscode-icons:folder-type-route',\n middleware: 'vscode-icons:folder-type-middleware',\n services: 'vscode-icons:folder-type-services',\n config: 'vscode-icons:folder-type-config',\n configs: 'vscode-icons:folder-type-config',\n docs: 'vscode-icons:folder-type-docs',\n doc: 'vscode-icons:folder-type-docs',\n documentation: 'vscode-icons:folder-type-docs',\n scripts: 'vscode-icons:folder-type-script',\n script: 'vscode-icons:folder-type-script',\n lib: 'vscode-icons:folder-type-library',\n library: 'vscode-icons:folder-type-library',\n packages: 'vscode-icons:folder-type-package',\n logs: 'vscode-icons:folder-type-log',\n log: 'vscode-icons:folder-type-log',\n cache: 'vscode-icons:folder-type-temp',\n tmp: 'vscode-icons:folder-type-temp',\n temp: 'vscode-icons:folder-type-temp',\n models: 'vscode-icons:folder-type-model',\n model: 'vscode-icons:folder-type-model',\n db: 'vscode-icons:folder-type-db',\n database: 'vscode-icons:folder-type-db',\n docker: 'vscode-icons:folder-type-docker',\n '.docker': 'vscode-icons:folder-type-docker',\n '.vscode': 'vscode-icons:folder-type-vscode',\n '.idea': 'vscode-icons:folder-type-idea',\n store: 'vscode-icons:default-folder',\n redux: 'vscode-icons:folder-type-redux',\n context: 'vscode-icons:default-folder',\n providers: 'vscode-icons:default-folder',\n layouts: 'vscode-icons:folder-type-view',\n layout: 'vscode-icons:folder-type-view',\n drizzle: 'vscode-icons:folder-type-db',\n migrations: 'vscode-icons:folder-type-db',\n storage: 'vscode-icons:default-folder',\n // NestJS 各层\n controllers: 'vscode-icons:folder-type-route',\n controller: 'vscode-icons:folder-type-route',\n guards: 'vscode-icons:default-folder',\n guard: 'vscode-icons:default-folder',\n filters: 'vscode-icons:folder-type-middleware',\n filter: 'vscode-icons:folder-type-middleware',\n interceptors: 'vscode-icons:folder-type-middleware',\n interceptor: 'vscode-icons:folder-type-middleware',\n pipes: 'vscode-icons:folder-type-middleware',\n pipe: 'vscode-icons:folder-type-middleware',\n decorators: 'vscode-icons:folder-type-tools',\n decorator: 'vscode-icons:folder-type-tools',\n validators: 'vscode-icons:folder-type-tools',\n validator: 'vscode-icons:folder-type-tools',\n exceptions: 'vscode-icons:folder-type-tools',\n exception: 'vscode-icons:folder-type-tools',\n modules: 'vscode-icons:default-folder',\n module: 'vscode-icons:default-folder',\n // ORM 层\n entities: 'vscode-icons:folder-type-db',\n entity: 'vscode-icons:folder-type-db',\n repositories: 'vscode-icons:folder-type-db',\n repository: 'vscode-icons:folder-type-db',\n // Redux / 状态管理\n actions: 'vscode-icons:folder-type-redux',\n action: 'vscode-icons:folder-type-redux',\n reducers: 'vscode-icons:folder-type-redux',\n reducer: 'vscode-icons:folder-type-redux',\n selectors: 'vscode-icons:folder-type-redux',\n selector: 'vscode-icons:folder-type-redux',\n sagas: 'vscode-icons:folder-type-redux',\n saga: 'vscode-icons:folder-type-redux',\n effects: 'vscode-icons:folder-type-redux',\n effect: 'vscode-icons:folder-type-redux',\n epics: 'vscode-icons:folder-type-redux',\n // 异步任务\n workers: 'vscode-icons:default-folder',\n worker: 'vscode-icons:default-folder',\n jobs: 'vscode-icons:default-folder',\n job: 'vscode-icons:default-folder',\n tasks: 'vscode-icons:default-folder',\n task: 'vscode-icons:default-folder',\n queues: 'vscode-icons:default-folder',\n queue: 'vscode-icons:default-folder',\n // 事件\n events: 'vscode-icons:default-folder',\n event: 'vscode-icons:default-folder',\n listeners: 'vscode-icons:default-folder',\n listener: 'vscode-icons:default-folder',\n subscribers: 'vscode-icons:default-folder',\n subscriber: 'vscode-icons:default-folder',\n handlers: 'vscode-icons:default-folder',\n handler: 'vscode-icons:default-folder',\n // 国际化\n locales: 'vscode-icons:folder-type-locale',\n locale: 'vscode-icons:folder-type-locale',\n i18n: 'vscode-icons:folder-type-locale',\n intl: 'vscode-icons:folder-type-locale',\n translations: 'vscode-icons:folder-type-locale',\n translation: 'vscode-icons:folder-type-locale',\n l10n: 'vscode-icons:folder-type-locale',\n lang: 'vscode-icons:folder-type-locale',\n // 应用架构\n features: 'vscode-icons:default-folder',\n feature: 'vscode-icons:default-folder',\n server: 'vscode-icons:folder-type-server',\n client: 'vscode-icons:folder-type-client',\n shared: 'vscode-icons:folder-type-shared',\n common: 'vscode-icons:folder-type-common',\n core: 'vscode-icons:default-folder',\n app: 'vscode-icons:folder-type-app',\n // 插件 / 主题\n plugins: 'vscode-icons:folder-type-plugin',\n plugin: 'vscode-icons:folder-type-plugin',\n theme: 'vscode-icons:folder-type-theme',\n themes: 'vscode-icons:folder-type-theme',\n // CI / 基础设施\n '.circleci': 'vscode-icons:default-folder',\n '.gitlab': 'vscode-icons:default-folder',\n workflows: 'vscode-icons:default-folder',\n devops: 'vscode-icons:default-folder',\n terraform: 'vscode-icons:default-folder',\n infra: 'vscode-icons:default-folder',\n infrastructure: 'vscode-icons:default-folder',\n k8s: 'vscode-icons:folder-type-kubernetes',\n kubernetes: 'vscode-icons:folder-type-kubernetes',\n // 测试覆盖率 / 供应商\n coverage: 'vscode-icons:folder-type-coverage',\n vendor: 'vscode-icons:folder-type-library',\n // 工具类\n bin: 'vscode-icons:folder-type-binary',\n tools: 'vscode-icons:folder-type-tools',\n tooling: 'vscode-icons:folder-type-tools',\n patches: 'vscode-icons:default-folder',\n // 示例 / 演示\n sandbox: 'vscode-icons:default-folder',\n demo: 'vscode-icons:default-folder',\n demos: 'vscode-icons:default-folder',\n examples: 'vscode-icons:default-folder',\n example: 'vscode-icons:default-folder',\n playground: 'vscode-icons:default-folder',\n // 数据 / 内容\n data: 'vscode-icons:folder-type-asset',\n content: 'vscode-icons:folder-type-asset',\n // 上传 / 媒体\n uploads: 'vscode-icons:default-folder',\n upload: 'vscode-icons:default-folder',\n media: 'vscode-icons:default-folder',\n // 导出\n reports: 'vscode-icons:folder-type-dist',\n exports: 'vscode-icons:folder-type-dist',\n};\nconst DEFAULT_FILE_ICON = 'vscode-icons:default-file';\nconst DEFAULT_FOLDER_ICON = 'vscode-icons:default-folder';\nconst DEFAULT_FOLDER_OPEN_ICON = 'vscode-icons:default-folder-opened';\n/**\n * 根据文件名获取 Iconify 图标 ID。\n * 优先完整文件名匹配(大小写不敏感),再匹配扩展名,兜底通用文件图标。\n */\nexport function getFileIcon(fileName: string): string {\n const lower = fileName.toLowerCase();\n if (NAME_MAP[lower])\n return NAME_MAP[lower];\n const dot = lower.lastIndexOf('.');\n const ext = dot >= 0 ? lower.slice(dot + 1) : '';\n return EXT_MAP[ext] ?? DEFAULT_FILE_ICON;\n}\n/**\n * 根据文件夹名获取 Iconify 图标 ID。\n * isOpen 为 true 时返回展开状态图标(vscode-icons 集合的展开态为 -opened 后缀)。\n */\nexport function getFolderIcon(folderName: string, isOpen = false): string {\n const lower = folderName.toLowerCase();\n const base = FOLDER_MAP[lower];\n if (base) {\n return isOpen ? `${base}-opened` : base;\n }\n return isOpen ? DEFAULT_FOLDER_OPEN_ICON : DEFAULT_FOLDER_ICON;\n}\n/** Shell 类型 ID → iconify 图标 ID(用于终端标签,与 listShells() 返回的 id 对齐) */\nconst SHELL_ICON_MAP: Readonly<Record<string, string>> = {\n powershell: 'vscode-icons:file-type-powershell',\n cmd: 'codicon:terminal-cmd',\n bash: 'vscode-icons:file-type-shell',\n zsh: 'vscode-icons:file-type-shell',\n fish: 'vscode-icons:file-type-shell',\n sh: 'vscode-icons:file-type-shell',\n};\nconst DEFAULT_SHELL_ICON = 'vscode-icons:file-type-shell';\n/**\n * 根据 shell 类型 ID 获取 Iconify 图标 ID。\n * 未知类型兜底为通用 shell 图标。\n */\nexport function getShellIcon(shellId: string | undefined): string {\n if (!shellId)\n return DEFAULT_SHELL_ICON;\n return SHELL_ICON_MAP[shellId.toLowerCase()] ?? DEFAULT_SHELL_ICON;\n}\n","import { FileTypeIcon } from \"@/components/FileTypeIcon\";\nimport { escapeRegExp } from '@fudanda/shared/core';\nimport { filesContent, filesPrivateContent } from '@fudanda/shared/api/file_content';\nimport { goTransport } from '@fudanda/react-core/lib/go-transport';\nimport { config } from \"@fudanda/react-core/config\";\nimport { getFileIcon } from \"@/utils/fileIcons\";\nimport { resolveFileMimeType } from \"@fudanda/react-core/utils/file-mime\";\nexport { guessMimeTypeFromName, resolveFileMimeType } from \"@fudanda/react-core/utils/file-mime\";\n/** 将相对路径拼接为完整可访问 URL */\nexport function getFileFullUrl(url: string): string {\n if (/^https?:\\/\\//.test(url))\n return url;\n const base = config.apiBaseUrl || globalThis.location.origin;\n return `${base}${url}`;\n}\n/** MIME 类型 → vscode-icons 图标 ID(无法识别时返回 null,由调用方兜底) */\nfunction getIconIdForMime(mimeType: string): string | null {\n const mime = mimeType.toLowerCase();\n if (mime === 'image/svg+xml')\n return 'vscode-icons:file-type-svg';\n if (mime.startsWith('image/'))\n return 'vscode-icons:file-type-image';\n if (mime.startsWith('video/'))\n return 'vscode-icons:file-type-video';\n if (mime.startsWith('audio/'))\n return 'vscode-icons:file-type-audio';\n if (mime === 'application/pdf')\n return 'vscode-icons:file-type-pdf2';\n if (isOfdFile(mime))\n return 'vscode-icons:file-type-pdf2';\n if (mime.includes('msword') || mime.includes('wordprocessingml') ||\n mime.includes('vnd.ms-word') || mime.includes('opendocument.text') || mime.includes('rtf'))\n return 'vscode-icons:file-type-word';\n if (mime.includes('presentationml') || mime.includes('powerpoint') ||\n mime.includes('opendocument.presentation'))\n return 'vscode-icons:file-type-powerpoint';\n if (mime === 'text/csv' || mime === 'application/csv' || mime === 'text/tab-separated-values')\n return 'vscode-icons:file-type-excel2';\n if (mime.includes('spreadsheetml') || mime.includes('excel') ||\n mime.includes('opendocument.spreadsheet') || mime.includes('apple.numbers') ||\n mime.includes('iwork-numbers'))\n return 'vscode-icons:file-type-excel';\n if (isArchiveFile(mime))\n return 'vscode-icons:file-type-zip';\n if (mime.startsWith('font/') || mime.includes('ttf') || mime.includes('woff') || mime.includes('opentype'))\n return 'vscode-icons:file-type-font';\n if (mime.includes('json'))\n return 'vscode-icons:file-type-json';\n if (mime.includes('javascript'))\n return 'vscode-icons:file-type-js-official';\n if (mime.includes('typescript'))\n return 'vscode-icons:file-type-typescript';\n if (mime.includes('html'))\n return 'vscode-icons:file-type-html';\n if (mime.includes('css'))\n return 'vscode-icons:file-type-css';\n if (mime.includes('xml'))\n return 'vscode-icons:file-type-xml';\n if (mime.includes('yaml'))\n return 'vscode-icons:file-type-yaml';\n if (mime === 'application/x-sh' || mime === 'text/x-shellscript')\n return 'vscode-icons:file-type-shell';\n if (mime.includes('sql'))\n return 'vscode-icons:file-type-sql';\n if (mime === 'text/markdown' || mime === 'text/x-markdown')\n return 'vscode-icons:file-type-markdown';\n if (mime === 'text/x-python' || mime === 'application/x-python-code')\n return 'vscode-icons:file-type-python';\n if (mime === 'application/x-msdownload' ||\n mime === 'application/vnd.microsoft.portable-executable' ||\n mime === 'application/x-executable' ||\n mime === 'application/x-msdos-program')\n return 'vscode-icons:file-type-binary';\n if (mime.startsWith('text/') || mime.includes('document'))\n return 'vscode-icons:file-type-text';\n return null;\n}\n/**\n * 根据 MIME 类型(及可选文件名)返回文件类型图标(vscode-icons 彩色图标,全站统一)。\n * 提供 fileName 时优先按扩展名精确匹配(覆盖数百种类型),MIME 作兜底。\n */\nexport function getFileTypeIcon(mimeType?: string | null, iconSize = 15, fileName?: string | null) {\n let iconId: string | null = null;\n if (fileName) {\n const byName = getFileIcon(fileName);\n if (byName !== 'vscode-icons:default-file')\n iconId = byName;\n }\n if (!iconId && mimeType)\n iconId = getIconIdForMime(mimeType);\n return (<FileTypeIcon icon={iconId ?? 'vscode-icons:default-file'} size={iconSize}/>);\n}\n/** 判断文件是否支持预览;MIME 缺失或为通用二进制类型时可按文件名回退。 */\nexport function canPreviewFile(mimeType: string | null | undefined, fileName?: string | null): boolean {\n const resolvedMimeType = resolveFileMimeType(mimeType, fileName);\n if (!resolvedMimeType)\n return false;\n return (resolvedMimeType.startsWith('image/') ||\n resolvedMimeType.startsWith('audio/') ||\n resolvedMimeType.startsWith('video/') ||\n resolvedMimeType === 'application/pdf' ||\n isOfdFile(resolvedMimeType) ||\n isEmailFile(resolvedMimeType) ||\n isMindMapFile(resolvedMimeType) ||\n isDrawingFile(resolvedMimeType) ||\n isDataAssetFile(resolvedMimeType) ||\n isGeoFile(resolvedMimeType) ||\n isSpreadsheetFile(resolvedMimeType) ||\n isWordFile(resolvedMimeType) ||\n isPresentationFile(resolvedMimeType) ||\n isMarkdownFile(resolvedMimeType) ||\n isPlainTextFile(resolvedMimeType) ||\n isArchiveFile(resolvedMimeType) ||\n isJsonFile(resolvedMimeType) ||\n isSvgFile(resolvedMimeType) ||\n isCodeFile(resolvedMimeType));\n}\n/** 判断是否为 File Viewer Spreadsheet renderer 支持的表格。 */\nexport function isSpreadsheetFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return (mime === 'application/vnd.ms-excel' ||\n mime.startsWith('application/vnd.ms-excel.') ||\n mime.includes('spreadsheetml') ||\n mime === 'text/csv' ||\n mime === 'application/csv' ||\n mime === 'text/tab-separated-values' ||\n mime === 'application/vnd.oasis.opendocument.spreadsheet' ||\n mime === 'application/vnd.oasis.opendocument.spreadsheet-flat-xml' ||\n mime === 'application/vnd.apple.numbers' ||\n mime === 'application/x-iwork-numbers-sffnumbers');\n}\n/** 判断是否为 File Viewer Word renderer 支持的文本文档。 */\nexport function isWordFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return (mime === 'application/msword' ||\n mime.startsWith('application/vnd.ms-word.') ||\n mime.includes('wordprocessingml') ||\n mime === 'application/vnd.oasis.opendocument.text' ||\n mime === 'application/rtf' ||\n mime === 'application/x-rtf' ||\n mime === 'text/rtf');\n}\n/** 判断是否为 File Viewer Presentation/OpenDocument renderer 支持的演示文稿。 */\nexport function isPresentationFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return (mime === 'application/vnd.ms-powerpoint' ||\n mime.startsWith('application/vnd.ms-powerpoint.') ||\n mime.includes('presentationml') ||\n mime === 'application/vnd.oasis.opendocument.presentation');\n}\n/** 判断是否为 File Viewer OFD renderer 支持的国产版式文档。 */\nexport function isOfdFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return mime === 'application/ofd' || mime === 'application/vnd.ofd';\n}\nconst EMAIL_MIME_TYPES = new Set([\n 'message/rfc822',\n 'application/vnd.ms-outlook',\n 'application/mbox',\n 'application/x-mbox',\n]);\n/** 判断是否为 File Viewer Email renderer 支持的邮件文件。 */\nexport function isEmailFile(mimeType?: string | null): boolean {\n return !!mimeType && EMAIL_MIME_TYPES.has(mimeType.toLowerCase());\n}\n/** 判断是否为 File Viewer Mind Map renderer 支持的 XMind 文件。 */\nexport function isMindMapFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return mime === 'application/vnd.xmind.workbook' || mime === 'application/x-xmind';\n}\nconst DRAWING_MIME_TYPES = new Set([\n 'text/x-mermaid',\n 'text/mermaid',\n 'text/vnd.mermaid',\n 'application/x-mermaid',\n 'application/vnd.mermaid',\n 'application/vnd.jgraph.mxfile',\n 'application/x-drawio',\n 'application/vnd.excalidraw+json',\n 'text/vnd.plantuml',\n 'text/x-plantuml',\n]);\n/**\n * 判断是否为 File Viewer Drawing renderer 支持的图形文件。\n * 覆盖 Mermaid、draw.io、Excalidraw 与 PlantUML 四类,均在浏览器本地解析。\n */\nexport function isDrawingFile(mimeType?: string | null): boolean {\n return !!mimeType && DRAWING_MIME_TYPES.has(mimeType.toLowerCase());\n}\nconst DATA_ASSET_MIME_TYPES = new Set([\n // 字体\n 'font/ttf', 'font/otf', 'font/woff', 'font/woff2',\n 'application/x-font-ttf', 'application/x-font-otf', 'application/font-woff',\n 'application/font-woff2', 'application/vnd.ms-opentype',\n // 设计资产(PSD 的规范 MIME 以 image/ 开头,必须显式识别,否则会被当成普通图片)\n 'image/vnd.adobe.photoshop', 'image/x-photoshop', 'application/x-photoshop', 'application/photoshop',\n // 结构化数据\n // 不含 Avro:avsc 的浏览器入口是为 browserify 写的,模块加载期就执行\n // util.inherits(BlobReader, stream.Readable),而 Vite 会把 stream / util 外部化为空模块,\n // 导致 import 阶段直接抛错。要支持需给整个 web 包引入 Node 内置 polyfill,代价与收益不匹配。\n 'application/vnd.sqlite3', 'application/x-sqlite3',\n 'application/vnd.apache.parquet', 'application/x-parquet',\n 'application/wasm',\n]);\n/**\n * 判断是否为 File Viewer Data renderer 支持的数据 / 设计 / 字体资产。\n * 覆盖字体样张、PSD 图层、SQLite 表结构、Parquet 记录与 WASM 导入导出表(不含 Avro,原因见上方注释)。\n */\nexport function isDataAssetFile(mimeType?: string | null): boolean {\n return !!mimeType && DATA_ASSET_MIME_TYPES.has(mimeType.toLowerCase());\n}\n/**\n * 判断是否为「交给 Semi `ImagePreview` 展示」的图片。\n *\n * 不能直接用 `image/*` 前缀:PSD 的规范 MIME 是 `image/vnd.adobe.photoshop`,\n * 但它需要 Data renderer 解析图层,塞进 `<img>` 只会得到裂图。\n * HEIC/HEIF/TIFF 仍属于本类——它们由 `@/utils/image-decode` 转码后照常进图集。\n */\nexport function isGalleryImageFile(mimeType?: string | null, fileName?: string | null): boolean {\n const mime = resolveFileMimeType(mimeType, fileName);\n return !!mime && mime.startsWith('image/') && !isDataAssetFile(mime);\n}\nconst GEO_MIME_TYPES = new Set([\n 'application/geo+json',\n 'application/vnd.geo+json',\n 'application/vnd.google-earth.kml+xml',\n 'application/gpx+xml',\n 'application/x-gpx+xml',\n 'application/vnd.shp',\n 'application/x-esri-shape',\n]);\n/**\n * 判断是否为 File Viewer Geo renderer 支持的地理数据文件。\n * 覆盖 GeoJSON、KML、GPX 与 Shapefile,使用离线空底图渲染,不请求外部瓦片服务。\n */\nexport function isGeoFile(mimeType?: string | null): boolean {\n return !!mimeType && GEO_MIME_TYPES.has(mimeType.toLowerCase());\n}\n/** 判断是否为可预览的 Markdown 文件 */\nexport function isMarkdownFile(mimeType?: string | null): boolean {\n return mimeType === 'text/markdown' || mimeType === 'text/x-markdown';\n}\n/** 判断是否为可预览的纯文本文件(.txt) */\nexport function isPlainTextFile(mimeType?: string | null): boolean {\n return mimeType === 'text/plain';\n}\n/** 判断是否为可预览的 JSON 文件 */\nexport function isJsonFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return mime === 'application/json' || mime === 'text/json';\n}\n/** 判断是否为 SVG 图形文件 */\nexport function isSvgFile(mimeType?: string | null): boolean {\n return mimeType === 'image/svg+xml';\n}\n/**\n * 判断是否为可预览的代码/配置文件。\n * 概履: JS/TS、Python、CSS、HTML、XML、YAML、Shell、SQL 等常见文本格式。\n * JSON 并不包含在此(由 isJsonFile 单独处理)。\n */\nexport function isCodeFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return (mime === 'application/javascript' ||\n mime === 'text/javascript' ||\n mime === 'application/typescript' ||\n mime === 'text/typescript' ||\n mime === 'text/x-python' ||\n mime === 'application/x-python-code' ||\n mime === 'text/css' ||\n mime === 'text/html' ||\n mime === 'text/xml' ||\n mime === 'application/xml' ||\n mime === 'application/x-yaml' ||\n mime === 'text/yaml' ||\n mime === 'text/x-yaml' ||\n mime === 'application/x-sh' ||\n mime === 'text/x-shellscript' ||\n mime === 'application/sql' ||\n mime === 'text/x-sql');\n}\n/** 判断是否为可预览的 ZIP 压缩包 */\nexport function isZipFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return (mime === 'application/zip' ||\n mime === 'application/x-zip-compressed' ||\n mime === 'application/x-zip');\n}\nconst ARCHIVE_MIME_TYPES = new Set([\n 'application/vnd.rar',\n 'application/x-rar-compressed',\n 'application/x-7z-compressed',\n 'application/x-tar',\n 'application/x-gtar',\n 'application/gzip',\n 'application/x-gzip',\n 'application/x-bzip2',\n 'application/x-bzip-compressed-tar',\n 'application/x-xz',\n 'application/x-xz-compressed-tar',\n 'application/x-lzma',\n 'application/zstd',\n 'application/x-zstd-compressed-tar',\n 'application/vnd.ms-cab-compressed',\n 'application/x-archive',\n 'application/x-cpio',\n 'application/x-iso9660-image',\n 'application/x-xar',\n 'application/x-lzh-compressed',\n 'application/java-archive',\n 'application/vnd.android.package-archive',\n 'application/vnd.comicbook+zip',\n 'application/vnd.comicbook-rar',\n]);\n/** 判断是否为 File Viewer Archive renderer 支持的压缩包。 */\nexport function isArchiveFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return isZipFile(mime) || ARCHIVE_MIME_TYPES.has(mime);\n}\n/** Fetch managed bytes through their generated operation; object-storage links carry no credentials. */\nexport async function fetchProtectedFile(url: string): Promise<Blob> {\n const id = extractManagedFileId(url);\n if (id) {\n const operation = /\\/private-content(?:[?#]|$)/.test(url) ? filesPrivateContent : filesContent;\n return (await goTransport.download(operation, { params: { id } })).blob();\n }\n if (!/^https?:\\/\\//.test(url)) throw new Error('文件地址不是受支持的托管文件或存储直链');\n const response = await fetch(url, { credentials: 'omit' });\n if (!response.ok) throw new Error('文件读取失败');\n return response.blob();\n}\nconst MANAGED_FILE_CONTENT_URL = (() => {\n const [prefix] = filesContent.path.split('{id}');\n return new RegExp(`${escapeRegExp(prefix)}([0-9a-f-]{36})/(?:private-content|content)(?:[?#]|$)`, 'i');\n})();\nexport function extractManagedFileId(url: string): string | null {\n return MANAGED_FILE_CONTENT_URL.exec(url)?.[1] ?? null;\n}\n/** Prefer an authorized object-store URL and fall back to the native managed-file operation. */\nexport async function fetchManagedFileBlob(url: string): Promise<Blob> {\n const fileId = extractManagedFileId(url);\n if (fileId) {\n try {\n const { getFileAccessUrl } = await import('@fudanda/react-core/hooks/queries/files');\n const access = await getFileAccessUrl(fileId);\n if (access.url !== url) return await fetchProtectedFile(access.url);\n } catch { /* The same authorization applies to the managed-content fallback. */ }\n }\n return fetchProtectedFile(url);\n}\n","import type { ManagedFile } from '@fudanda/shared/platform';\nimport type { ResponsiveTableAction } from \"@/components/ResponsiveTableActions\";\nimport type { FilePreviewController } from \"@/hooks/useFilePreview\";\nimport { canPreviewFile } from \"@/utils/file-utils\";\nimport { confirmDelete } from \"@/utils/confirm\";\ninterface ManagedFileActionsOptions {\n preview: FilePreviewController;\n onDetail: (record: ManagedFile) => void;\n onCopyUrl: (record: ManagedFile) => void;\n onDelete: (record: ManagedFile) => void | Promise<unknown>;\n canDelete: boolean;\n}\n/** 文件表格操作列公共动作:预览 / 下载 / 详情 / 复制链接 / 删除 */\nexport function buildManagedFileActions(record: ManagedFile, { preview, onDetail, onCopyUrl, onDelete, canDelete }: ManagedFileActionsOptions): ResponsiveTableAction[] {\n const isPreviewable = canPreviewFile(record.mimeType, record.originalName);\n return [\n {\n key: 'preview',\n label: '预览',\n disabled: !isPreviewable,\n loading: preview.previewLoadingId === record.id,\n onClick: () => preview.handlePreview(record),\n },\n {\n key: 'download',\n label: '下载',\n loading: preview.downloadLoadingId === record.id,\n onClick: () => preview.handleDownload(record),\n },\n {\n key: 'detail',\n label: '详情',\n onClick: () => onDetail(record),\n },\n {\n key: 'copy-url',\n label: '复制链接',\n onClick: () => onCopyUrl(record),\n },\n {\n key: 'delete',\n label: '删除',\n danger: true,\n dividerBefore: true,\n hidden: !canDelete,\n onClick: () => {\n confirmDelete({\n title: '确认删除此文件?',\n content: '删除文件记录后,将同步尝试删除实际存储对象。',\n onOk: () => onDelete(record),\n });\n },\n },\n ];\n}\n","import { useState, useEffect, useCallback, lazy, Suspense, useMemo } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useQuery } from '@tanstack/react-query';\nimport { Modal, Spin, Toast, AudioPlayer, VideoPlayer, Typography } from \"@/ui\";\nimport { X } from 'lucide-react';\nimport { fileContract } from '@fudanda/react-core/lib/native-operations';\nimport { contractKey } from \"@fudanda/react-core/lib/contract-query\";\nimport { useThemeController } from \"@/providers/theme-controller\";\nimport { fetchManagedFileBlob, resolveFileMimeType, isSpreadsheetFile, isWordFile, isPresentationFile, isOfdFile, isEmailFile, isMindMapFile, isDrawingFile, isDataAssetFile, isGeoFile, isMarkdownFile, isPlainTextFile, isArchiveFile, isJsonFile, isSvgFile, isCodeFile, getFileTypeIcon } from \"@/utils/file-utils\";\nimport AppModal from \"@/components/AppModal\";\nimport type { CSSProperties, ReactNode } from 'react';\nimport \"@/components/FilePreviewModal/filePreview.css\";\n// @embedpdf 引擎 ~1MB,懒加载,避免经 FileAttachment 链进入首屏\nconst PDFPreviewPanel = lazy(() => import(\"@/components/PDFPreviewPanel\").then((m) => ({ default: m.PDFPreviewPanel })));\n// File Viewer 重型渲染器懒加载,避免影响首屏\nconst FileViewerPreviewPanel = lazy(() => import(\"@/components/FileViewerPreviewPanel\"));\n// react-markdown 懒加载\nconst MarkdownPreviewPanel = lazy(() => import(\"@/components/MarkdownPreviewPanel\"));\n// Semi JsonViewer 懒加载\nconst JsonPreviewPanel = lazy(() => import(\"@/components/JsonPreviewPanel\"));\n// Monaco Editor 懒加载(代码/纯文本文件预览)\nconst MonacoPreviewPanel = lazy(() => import(\"@/components/MonacoPreviewPanel\"));\ninterface FilePreviewModalProps {\n fileUrl: string;\n fileName?: string;\n mimeType?: string | null;\n visible: boolean;\n onClose: () => void;\n /** 遇到不支持预览的格式时触发,不传则组件内部静默关闭 */\n onFallback?: (fileUrl: string, fileName: string, mimeType: string) => void;\n style?: CSSProperties;\n}\ntype PreviewKind = 'spreadsheet' | 'word' | 'presentation' | 'ofd' | 'email' | 'mindmap' | 'drawing' | 'dataAsset' | 'geo' | 'archive' | 'markdown' | 'plainText' | 'json' | 'svg' | 'code' | 'pdf' | 'audio' | 'video';\ntype PreviewData = {\n kind: 'spreadsheet' | 'word' | 'presentation' | 'ofd' | 'email' | 'mindmap' | 'drawing' | 'dataAsset' | 'geo' | 'archive';\n file: File;\n} | {\n kind: 'markdown';\n text: string;\n} | {\n kind: 'plainText';\n text: string;\n} | {\n kind: 'json';\n text: string;\n} | {\n kind: 'svg';\n url: string;\n} | {\n kind: 'code';\n text: string;\n} | {\n kind: 'pdf';\n file: File;\n} | {\n kind: 'audio';\n url: string;\n} | {\n kind: 'video';\n url: string;\n};\nfunction revokePreviewUrl(data: PreviewData | undefined) {\n if (data?.kind === 'audio' || data?.kind === 'video' || data?.kind === 'svg') {\n URL.revokeObjectURL(data.url);\n }\n}\n/** 懒加载预览面板的统一弹窗外壳:AppModal + Suspense 加载兜底(各分支仅宽高不同) */\nfunction PreviewModalShell({ title, onCancel, fullscreen, onToggleFullscreen, width, viewportHeight, children }: Readonly<{\n title: ReactNode;\n onCancel: () => void;\n fullscreen: boolean;\n onToggleFullscreen: () => void;\n width: string;\n /** 非全屏时弹窗高度基准(如 '90vh') */\n viewportHeight: string;\n children: ReactNode;\n}>) {\n return (<AppModal visible onCancel={onCancel} title={title} footer={null} fullscreenable fullscreen={fullscreen} onToggleFullscreen={onToggleFullscreen} width={width} centered bodyStyle={{ padding: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden', height: fullscreen ? 'calc(100vh - 40px)' : `calc(${viewportHeight} - 40px)` }} keepDOM={false}>\n <Suspense fallback={<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }}>\n <Spin size=\"large\" tip=\"加载预览组件...\"/>\n </div>}>\n {children}\n </Suspense>\n </AppModal>);\n}\nexport default function FilePreviewModal({ fileUrl, fileName = '文件', mimeType, visible, onClose, onFallback, }: Readonly<FilePreviewModalProps>) {\n const [fullscreen, setFullscreen] = useState(false);\n const toggleFullscreen = useCallback(() => {\n setFullscreen(f => !f);\n }, []);\n const { isDark } = useThemeController();\n const resolvedMimeType = useMemo(() => resolveFileMimeType(mimeType, fileName), [fileName, mimeType]);\n const previewKind = useMemo<PreviewKind | 'unsupported' | 'image' | null>(() => {\n if (!resolvedMimeType)\n return null;\n const isImage = resolvedMimeType.startsWith('image/');\n const tsExtPattern = /\\.(ts|tsx)$/i;\n const isMpegTsAsCode = resolvedMimeType === 'video/mp2t' && tsExtPattern.test(fileName);\n if (isSpreadsheetFile(resolvedMimeType))\n return 'spreadsheet';\n if (isWordFile(resolvedMimeType))\n return 'word';\n if (isPresentationFile(resolvedMimeType))\n return 'presentation';\n if (isOfdFile(resolvedMimeType))\n return 'ofd';\n if (isEmailFile(resolvedMimeType))\n return 'email';\n if (isMindMapFile(resolvedMimeType))\n return 'mindmap';\n if (isDrawingFile(resolvedMimeType))\n return 'drawing';\n // 必须早于下方的 image/* 判定:PSD 的规范 MIME 是 image/vnd.adobe.photoshop\n if (isDataAssetFile(resolvedMimeType))\n return 'dataAsset';\n if (isGeoFile(resolvedMimeType))\n return 'geo';\n if (isMarkdownFile(resolvedMimeType))\n return 'markdown';\n if (isPlainTextFile(resolvedMimeType))\n return 'plainText';\n if (isArchiveFile(resolvedMimeType))\n return 'archive';\n if (isJsonFile(resolvedMimeType))\n return 'json';\n if (isSvgFile(resolvedMimeType))\n return 'svg';\n if (isCodeFile(resolvedMimeType) || isMpegTsAsCode)\n return 'code';\n if (resolvedMimeType === 'application/pdf')\n return 'pdf';\n if (resolvedMimeType.startsWith('audio/'))\n return 'audio';\n if (resolvedMimeType.startsWith('video/') && !isMpegTsAsCode)\n return 'video';\n if (isImage)\n return 'image';\n return 'unsupported';\n }, [fileName, resolvedMimeType]);\n // 非契约 blob 通道(任意文件地址经 fetchManagedFileBlob 取二进制并按类型加工),保留手写 useQuery,key 取文件内容契约 key 追加预览段\n const previewQuery = useQuery({\n queryKey: [...contractKey(fileContract.content), 'preview', visible, fileUrl, fileName, resolvedMimeType, previewKind],\n queryFn: async (): Promise<PreviewData> => {\n const blob = await fetchManagedFileBlob(fileUrl);\n if (previewKind === 'spreadsheet' || previewKind === 'word' || previewKind === 'presentation' || previewKind === 'ofd' || previewKind === 'email' || previewKind === 'mindmap' || previewKind === 'drawing' || previewKind === 'dataAsset' || previewKind === 'geo' || previewKind === 'archive') {\n return {\n kind: previewKind,\n file: new File([blob], fileName, { type: resolvedMimeType || blob.type }),\n };\n }\n if (previewKind === 'markdown')\n return { kind: 'markdown', text: await blob.text() };\n if (previewKind === 'plainText')\n return { kind: 'plainText', text: await blob.text() };\n if (previewKind === 'json')\n return { kind: 'json', text: await blob.text() };\n if (previewKind === 'svg')\n return { kind: 'svg', url: URL.createObjectURL(blob) };\n if (previewKind === 'code')\n return { kind: 'code', text: await blob.text() };\n if (previewKind === 'pdf')\n return { kind: 'pdf', file: new File([blob], fileName, { type: 'application/pdf' }) };\n if (previewKind === 'audio')\n return { kind: 'audio', url: URL.createObjectURL(blob) };\n if (previewKind === 'video')\n return { kind: 'video', url: URL.createObjectURL(blob) };\n throw new Error('文件加载失败');\n },\n enabled: visible && !!previewKind && previewKind !== 'unsupported' && previewKind !== 'image',\n staleTime: 0,\n gcTime: 0,\n });\n const previewData = previewQuery.data;\n useEffect(() => {\n return () => revokePreviewUrl(previewData);\n }, [previewData]);\n useEffect(() => {\n if (!visible) {\n setFullscreen(false);\n return;\n }\n if (!resolvedMimeType) {\n onClose();\n return;\n }\n if (previewKind === 'unsupported') {\n onFallback?.(fileUrl, fileName, resolvedMimeType);\n onClose();\n return;\n }\n if (previewKind === 'image') {\n onClose();\n }\n }, [fileName, fileUrl, onClose, onFallback, previewKind, resolvedMimeType, visible]);\n useEffect(() => {\n if (previewQuery.error) {\n Toast.error(previewQuery.error instanceof Error ? previewQuery.error.message : '文件加载失败');\n onClose();\n }\n }, [onClose, previewQuery.error]);\n const handleClose = () => {\n onClose();\n };\n /**\n * 预览弹窗标题:文件类型图标 + 文件名。\n * 由 AppModal 的 title prop 承载,统一在弹窗顶部展示。\n * PDF 除外(PDFPreviewPanel 有自己的完整标题栏,不使用 AppModal)。\n */\n const previewTitle: ReactNode = (<div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>\n {getFileTypeIcon(mimeType, 15, fileName)}\n <Typography.Text ellipsis={{ showTooltip: true }} style={{ fontSize: 13, fontWeight: 500, minWidth: 0 }}>\n {fileName}\n </Typography.Text>\n </div>);\n if (!visible)\n return null;\n if (previewQuery.isLoading) {\n return (<AppModal visible onCancel={handleClose} title={previewTitle} footer={null} fullscreenable={false} centered keepDOM={false} bodyStyle={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 40 }}>\n <Spin size=\"large\" tip=\"加载中...\"/>\n </AppModal>);\n }\n if (previewData?.kind === 'pdf') {\n return (<Modal visible onCancel={handleClose} title={null} footer={null} fullScreen={fullscreen} width=\"min(1100px, 92vw)\" centered bodyStyle={{ padding: 0, display: 'flex', overflow: 'hidden', height: fullscreen ? '100vh' : '88vh' }} closable={false} keepDOM={false}>\n <Suspense fallback={<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%' }}>\n <Spin size=\"large\"/>\n </div>}>\n <PDFPreviewPanel file={previewData.file} onClose={handleClose} fullscreen={fullscreen} onToggleFullscreen={toggleFullscreen} style={{ width: '100%', borderLeft: 'none' }}/>\n </Suspense>\n </Modal>);\n }\n if (previewData?.kind === 'spreadsheet' || previewData?.kind === 'word' || previewData?.kind === 'presentation' || previewData?.kind === 'ofd' || previewData?.kind === 'email' || previewData?.kind === 'mindmap' || previewData?.kind === 'drawing' || previewData?.kind === 'dataAsset' || previewData?.kind === 'geo' || previewData?.kind === 'archive') {\n const isPresentation = previewData.kind === 'presentation';\n const isWide = previewData.kind === 'spreadsheet' || previewData.kind === 'archive' || previewData.kind === 'email' || previewData.kind === 'mindmap' || previewData.kind === 'drawing' || previewData.kind === 'dataAsset' || previewData.kind === 'geo' || isPresentation;\n return (<PreviewModalShell title={previewTitle} onCancel={handleClose} fullscreen={fullscreen} onToggleFullscreen={toggleFullscreen} width={isWide ? 'min(1200px, 94vw)' : 'min(960px, 92vw)'} viewportHeight=\"90vh\">\n <FileViewerPreviewPanel file={previewData.file} style={{ flex: 1 }}/>\n </PreviewModalShell>);\n }\n if (previewData?.kind === 'markdown') {\n const isRawText = previewData.text.startsWith('\\u0000PLAINTEXT\\u0000');\n const displayContent = isRawText ? previewData.text.slice('\\u0000PLAINTEXT\\u0000'.length) : previewData.text;\n return (<PreviewModalShell title={previewTitle} onCancel={handleClose} fullscreen={fullscreen} onToggleFullscreen={toggleFullscreen} width=\"min(900px, 92vw)\" viewportHeight=\"90vh\">\n <MarkdownPreviewPanel content={displayContent} rawText={isRawText} style={{ flex: 1, minHeight: 0 }}/>\n </PreviewModalShell>);\n }\n if (previewData?.kind === 'json') {\n return (<PreviewModalShell title={previewTitle} onCancel={handleClose} fullscreen={fullscreen} onToggleFullscreen={toggleFullscreen} width=\"min(900px, 92vw)\" viewportHeight=\"88vh\">\n <JsonPreviewPanel content={previewData.text} style={{ flex: 1, minHeight: 0 }}/>\n </PreviewModalShell>);\n }\n if (previewData?.kind === 'code' || previewData?.kind === 'plainText') {\n return (<PreviewModalShell title={previewTitle} onCancel={handleClose} fullscreen={fullscreen} onToggleFullscreen={toggleFullscreen} width=\"min(1100px, 92vw)\" viewportHeight=\"90vh\">\n <MonacoPreviewPanel content={previewData.text} fileName={fileName} style={{ flex: 1, minHeight: 0 }}/>\n </PreviewModalShell>);\n }\n if (previewData?.kind === 'svg') {\n return (<AppModal visible onCancel={handleClose} title={previewTitle} footer={null} fullscreenable fullscreen={fullscreen} onToggleFullscreen={toggleFullscreen} width=\"min(900px, 92vw)\" centered bodyStyle={{\n padding: 16,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'auto',\n height: fullscreen ? 'calc(100vh - 40px)' : 'calc(80vh - 40px)',\n }} keepDOM={false}>\n <img src={previewData.url} alt={fileName} style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain' }}/>\n </AppModal>);\n }\n if (previewData?.kind === 'audio') {\n // 音频固定在页面底部以播放条形式呈现,避免在窄弹窗内控件(播放键)被裁切\n return createPortal(<div className=\"arcbase-audio-dock\" role=\"region\" aria-label=\"音频播放器\">\n <div className=\"arcbase-audio-dock__inner\">\n <div className=\"arcbase-audio-dock__player\">\n <AudioPlayer className=\"arcbase-audio-preview\" audioUrl={{ src: previewData.url, title: fileName }} theme={isDark ? 'dark' : 'light'} autoPlay style={{ width: '100%' }}/>\n </div>\n <button type=\"button\" className=\"arcbase-audio-dock__close\" onClick={handleClose} aria-label=\"关闭音频播放器\">\n <X size={18}/>\n </button>\n </div>\n </div>, document.body);\n }\n if (previewData?.kind === 'video') {\n return (<AppModal visible onCancel={handleClose} title={previewTitle} footer={null} fullscreenable={false} width=\"min(960px, 92vw)\" centered bodyStyle={{ padding: 0, overflow: 'hidden', borderRadius: 'var(--arc-antd-border-radius-medium)' }} keepDOM={false}>\n <VideoPlayer src={previewData.url} theme={isDark ? 'dark' : 'light'} width=\"100%\" autoPlay={false} muted={false} volume={100} clickToPlay={true} defaultPlaybackRate={1} playbackRateList={[\n { label: '0.5x', value: 0.5 },\n { label: '1x', value: 1 },\n { label: '1.5x', value: 1.5 },\n { label: '2x', value: 2 },\n ]} style={{ borderRadius: 'var(--arc-antd-border-radius-medium)' }}/>\n </AppModal>);\n }\n return null;\n}\n","import { useEffect, useState } from 'react';\nimport { generateWatermarkTile } from \"@fudanda/react-core/components/watermark-tile\";\ninterface PreviewWatermarkProps {\n readonly content: string | string[] | null | undefined;\n readonly visible: boolean;\n}\n/**\n * 预览水印:覆盖整个视口且位于预览弹层之上(Semi Modal / ImagePreview 通过 portal 挂到 body,\n * 页面内的 Watermark 覆盖不到),只在预览打开时渲染,不拦截任何交互。\n */\nexport function PreviewWatermark({ content, visible }: PreviewWatermarkProps) {\n const lines = (Array.isArray(content) ? content : content ? [content] : []).filter(Boolean);\n const key = JSON.stringify(lines);\n const [style, setStyle] = useState<React.CSSProperties | null>(null);\n useEffect(() => {\n const lines = JSON.parse(key) as string[];\n if (!visible || lines.length === 0) {\n setStyle(null);\n return;\n }\n const isDark = document.body.getAttribute('theme-mode') === 'dark';\n const tile = generateWatermarkTile(lines, 13, 0.22, -22, 180, 110, isDark);\n if (!tile)\n return;\n setStyle({\n position: 'fixed', inset: 0, pointerEvents: 'none', userSelect: 'none', zIndex: 2100,\n backgroundImage: `url(${tile.dataUrl})`, backgroundRepeat: 'repeat', backgroundSize: `${tile.cssWidth}px ${tile.cssHeight}px`,\n });\n }, [key, visible]);\n if (!style)\n return null;\n return <div aria-hidden style={style} data-testid=\"preview-watermark\"/>;\n}\n","import { ImagePreview } from \"@/ui\";\nimport FilePreviewModal from \"@/components/FilePreviewModal/index\";\nimport { PreviewWatermark } from \"@/components/PreviewWatermark\";\nimport type { FilePreviewController } from \"@/hooks/useFilePreview\";\ninterface FilePreviewLayerProps {\n readonly preview: FilePreviewController;\n /** 预览打开时叠加的水印文本(空 = 不加水印) */\n readonly watermark?: string | string[] | null;\n}\n/** 图集预览 + 非图片文件预览弹层(与 useFilePreview 配套使用) */\nexport function FilePreviewLayer({ preview, watermark }: FilePreviewLayerProps) {\n return (<>\n <ImagePreview src={preview.previewSrcList} visible={preview.previewVisible} currentIndex={preview.previewCurrentIndex} onChange={preview.setPreviewCurrentIndex} onVisibleChange={(v) => {\n if (!v)\n preview.closeImagePreview();\n }} infinite/>\n <FilePreviewModal fileUrl={preview.filePreview?.url ?? ''} fileName={preview.filePreview?.name} mimeType={preview.filePreview?.mimeType} visible={!!preview.filePreview} onClose={preview.closeFilePreview}/>\n <PreviewWatermark content={watermark} visible={preview.previewVisible || !!preview.filePreview}/>\n </>);\n}\n","import type { ReactNode } from 'react';\nimport { Button, Descriptions, Space, Spin, Typography } from \"@/ui\";\nimport type { ManagedFile } from '@fudanda/shared/platform';\nimport { formatBytes } from '@fudanda/shared/core';\nimport { AppModal } from \"@/components/AppModal\";\nimport { formatDateTime } from \"@fudanda/react-core/utils/date\";\nimport '@/components/FileDetailModal.css';\nconst { Text } = Typography;\ninterface FileDetailModalProps {\n visible: boolean;\n /** 当前展示的文件(详情查询结果优先,缺省回退列表行) */\n file: ManagedFile | null;\n /** 详情查询进行中 */\n loading: boolean;\n /** 访问链接口径(文件中心 directUrl 优先;存储浏览器统一走代理地址),与页面「复制链接」保持一致 */\n resolveUrl: (file: ManagedFile) => string;\n onCopyUrl: (file: ManagedFile) => void;\n onClose: () => void;\n /** 基础信息之后的附加整行(如图片分辨率) */\n extraRows?: Array<{\n key: string;\n value: ReactNode;\n }>;\n}\n/** 托管文件详情弹窗:文件中心与存储服务文件浏览器共用 */\nexport function FileDetailModal({ visible, file, loading, resolveUrl, onCopyUrl, onClose, extraRows = [] }: Readonly<FileDetailModalProps>) {\n return (<AppModal title=\"文件详情\" visible={visible} onCancel={onClose} footer={<Space>\n <Button onClick={() => file && onCopyUrl(file)}>复制链接</Button>\n <Button type=\"primary\" onClick={onClose}>关闭</Button>\n </Space>} width={960}>\n <Spin spinning={loading} tip=\"加载中...\" size=\"small\">\n {file && (<Descriptions className=\"file-detail-descriptions\" column={{ xs: 1, sm: 1, md: 2 }} align=\"left\" size=\"medium\" data={[\n { key: '文件名', value: file.originalName, span: 'filled' },\n { key: '存储服务', value: file.storageName },\n { key: 'MIME 类型', value: file.mimeType || '—' },\n { key: '文件大小', value: formatBytes(file.size) },\n { key: '上传人', value: file.uploaderName || '—' },\n ...extraRows.map(row => ({ ...row, span: 'filled' as const })),\n { key: '对象键', value: <Text copyable style={{ fontSize: 12 }}>{file.objectKey}</Text>, span: 'filled' },\n { key: '访问链接', value: <Text copyable style={{ fontSize: 12 }}>{resolveUrl(file)}</Text>, span: 'filled' },\n { key: '上传时间', value: formatDateTime(file.createdAt), span: 'filled' },\n ]}/>)}\n </Spin>\n </AppModal>);\n}\nexport default FileDetailModal;\n","import { useRef, useState } from 'react';\nimport { Toast } from \"@/ui\";\nimport { canPreviewFile, fetchManagedFileBlob, isGalleryImageFile, resolveFileMimeType } from \"@/utils/file-utils\";\nimport { createDisplayableImageUrl } from \"@fudanda/react-core/utils/image-decode\";\n/** 预览/下载所需的最小文件形状:ManagedFile 天然满足;素材等业务实体映射这四个字段即可复用预览设施 */\nexport interface PreviewableFile {\n id: string;\n url: string;\n originalName: string;\n mimeType?: string | null;\n}\ninterface FilePreviewTarget {\n id: string;\n url: string;\n name: string;\n mimeType: string;\n}\n/**\n * 受保护文件的预览/下载共享逻辑(文件管理页与存储空间浏览器共用)。\n *\n * - 图片:进入图集预览(先加载点击图,其余后台渐进加载,会话号防止过期加载写入)\n * - 可预览的非图片(PDF 等):打开 FilePreviewModal\n * - 其他类型:拉取 blob 后新窗口打开\n *\n * 与 `<FilePreviewLayer preview={...} />` 配套使用。\n *\n * @param getImageFiles 返回当前列表中的图片文件(点击图片时构建图集用)\n */\nexport function useFilePreview(getImageFiles: () => PreviewableFile[]) {\n const [previewVisible, setPreviewVisible] = useState(false);\n const [previewSrcList, setPreviewSrcList] = useState<string[]>([]);\n const [previewCurrentIndex, setPreviewCurrentIndex] = useState(0);\n const [previewLoadingId, setPreviewLoadingId] = useState<string | null>(null);\n const [filePreview, setFilePreview] = useState<FilePreviewTarget | null>(null);\n const [downloadLoadingId, setDownloadLoadingId] = useState<string | null>(null);\n // previewBlobUrlsRef: 与图片列表索引对齐,记录已创建的 blob URL 便于统一释放\n const previewBlobUrlsRef = useRef<string[]>([]);\n // previewSessionRef: 每次新预览会话自增,用于取消过期的后台加载\n const previewSessionRef = useRef(0);\n const cleanupPreviewBlobs = () => {\n previewBlobUrlsRef.current.forEach((url) => globalThis.URL.revokeObjectURL(url));\n previewBlobUrlsRef.current = [];\n };\n const handlePreview = async (file: PreviewableFile) => {\n const resolvedMimeType = resolveFileMimeType(file.mimeType, file.originalName);\n const isImage = isGalleryImageFile(file.mimeType, file.originalName);\n const isPreviewable = canPreviewFile(file.mimeType, file.originalName);\n if (!isPreviewable && !isImage) {\n try {\n const blob = await fetchManagedFileBlob(file.url);\n const objectUrl = globalThis.URL.createObjectURL(blob);\n globalThis.open(objectUrl, '_blank', 'noopener,noreferrer');\n globalThis.setTimeout(() => globalThis.URL.revokeObjectURL(objectUrl), 60000);\n }\n catch (error) {\n Toast.error(error instanceof Error ? error.message : '预览文件失败');\n }\n return;\n }\n if (!isImage) {\n setPreviewLoadingId(file.id);\n try {\n setFilePreview({\n id: file.id,\n url: file.url,\n name: file.originalName,\n mimeType: resolvedMimeType ?? 'application/octet-stream',\n });\n }\n catch (error) {\n Toast.error(error instanceof Error ? error.message : '预览文件失败');\n }\n finally {\n setPreviewLoadingId(null);\n }\n return;\n }\n const imageFiles = getImageFiles().filter((f) => isGalleryImageFile(f.mimeType, f.originalName));\n const clickedIndex = imageFiles.findIndex((f) => f.id === file.id);\n setPreviewLoadingId(file.id);\n // 开启新预览会话\n previewSessionRef.current += 1;\n const mySession = previewSessionRef.current;\n try {\n cleanupPreviewBlobs();\n // 先用空占位初始化,保证数组索引稳定\n const initialUrls = imageFiles.map(() => '');\n previewBlobUrlsRef.current = [...initialUrls];\n // 优先加载被点击的图片 → 立即展示预览\n const clickedFile = imageFiles[clickedIndex];\n const clickedBlob = await fetchManagedFileBlob(clickedFile.url);\n if (previewSessionRef.current !== mySession)\n return; // 用户在加载完成前已关闭预览\n const clickedUrl = await createDisplayableImageUrl(clickedBlob, clickedFile.mimeType, clickedFile.originalName);\n if (previewSessionRef.current !== mySession) {\n globalThis.URL.revokeObjectURL(clickedUrl);\n return;\n }\n initialUrls[clickedIndex] = clickedUrl;\n previewBlobUrlsRef.current[clickedIndex] = clickedUrl;\n setPreviewSrcList([...initialUrls]);\n setPreviewCurrentIndex(Math.max(0, clickedIndex));\n setPreviewVisible(true);\n // 其余图片后台渐进加载(不阻塞)\n imageFiles.forEach(async (imgFile, i) => {\n if (i === clickedIndex)\n return;\n try {\n const blob = await fetchManagedFileBlob(imgFile.url);\n if (previewSessionRef.current !== mySession)\n return;\n const url = await createDisplayableImageUrl(blob, imgFile.mimeType, imgFile.originalName);\n if (previewSessionRef.current !== mySession) {\n globalThis.URL.revokeObjectURL(url);\n return;\n }\n previewBlobUrlsRef.current[i] = url;\n setPreviewSrcList((prev) => {\n const updated = [...prev];\n updated[i] = url;\n return updated;\n });\n }\n catch { /* 忽略单张失败 */ }\n });\n }\n catch (error) {\n Toast.error(error instanceof Error ? error.message : '预览图片失败');\n }\n finally {\n setPreviewLoadingId(null);\n }\n };\n const handleDownload = async (file: PreviewableFile) => {\n setDownloadLoadingId(file.id);\n try {\n const blob = await fetchManagedFileBlob(file.url);\n const objectUrl = globalThis.URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = objectUrl;\n link.download = file.originalName;\n link.click();\n globalThis.setTimeout(() => globalThis.URL.revokeObjectURL(objectUrl), 60000);\n }\n catch (error) {\n Toast.error(error instanceof Error ? error.message : '下载文件失败');\n }\n finally {\n setDownloadLoadingId(null);\n }\n };\n /** 关闭图集预览:使在途后台加载失效并释放 blob URL */\n const closeImagePreview = () => {\n previewSessionRef.current += 1;\n setPreviewVisible(false);\n cleanupPreviewBlobs();\n setPreviewSrcList([]);\n };\n const closeFilePreview = () => setFilePreview(null);\n /** 数据源切换时强制关闭预览(不清空图集列表,与原行为一致) */\n const resetPreview = () => {\n setFilePreview(null);\n setPreviewVisible(false);\n previewSessionRef.current += 1;\n };\n return {\n previewVisible,\n previewSrcList,\n previewCurrentIndex,\n setPreviewCurrentIndex,\n previewLoadingId,\n downloadLoadingId,\n filePreview,\n handlePreview,\n handleDownload,\n closeImagePreview,\n closeFilePreview,\n resetPreview,\n };\n}\nexport type FilePreviewController = ReturnType<typeof useFilePreview>;\n","import { cloneElement, useCallback, useRef } from 'react';\nimport type { CSSProperties, ReactElement } from 'react';\nimport { Dropdown } from \"@/ui\";\nexport interface CursorPoint {\n readonly x: number;\n readonly y: number;\n}\ninterface CursorContextDropdownProps {\n readonly point: CursorPoint;\n readonly contextKey: string | number;\n readonly render: ReactElement<{\n style?: CSSProperties;\n }>;\n readonly onClose: () => void;\n}\nconst viewportMenuStyle: CSSProperties = {\n maxHeight: 'calc(100vh - 16px)',\n overflowY: 'auto',\n};\nfunction withViewportMenuStyle(content: ReactElement<{\n style?: CSSProperties;\n}>): ReactElement<{\n style?: CSSProperties;\n}> {\n return cloneElement(content, {\n style: { ...content.props.style, ...viewportMenuStyle },\n });\n}\nfunction getPopupContainer(): HTMLElement {\n return document.body;\n}\n/** Semi Dropdown adapter for menus that must open at the pointer coordinates. */\nexport function CursorContextDropdown({ point, contextKey, render, onClose, }: Readonly<CursorContextDropdownProps>) {\n const rePosKey = `${contextKey}:${point.x}:${point.y}`;\n const closedKeyRef = useRef<string | null>(null);\n const closeOnce = useCallback(() => {\n if (closedKeyRef.current === rePosKey)\n return;\n closedKeyRef.current = rePosKey;\n onClose();\n }, [onClose, rePosKey]);\n return (<Dropdown visible trigger=\"click\" position=\"bottomLeft\" autoAdjustOverflow rePosKey={rePosKey} render={withViewportMenuStyle(render)} getPopupContainer={getPopupContainer} clickToHide closeOnEsc onClickOutSide={closeOnce} onVisibleChange={(visible) => {\n if (!visible)\n closeOnce();\n }}>\n <span aria-hidden=\"true\" style={{\n position: 'fixed',\n left: point.x,\n top: point.y,\n width: 1,\n height: 1,\n pointerEvents: 'none',\n }}/>\n </Dropdown>);\n}\n","import { useState } from 'react';\nimport { Checkbox, Dropdown, Spin, Tooltip } from \"@/ui\";\nimport type { ManagedFile } from '@fudanda/shared/platform';\nimport { getFileTypeIcon, canPreviewFile } from \"@/utils/file-utils\";\nimport { confirmAndDelete } from \"@/components/list-page/index\";\nimport { CursorContextDropdown } from \"@/components/CursorContextDropdown\";\nimport { formatBytes } from '@fudanda/shared/core';\nimport \"@/pages/system/files/FilesPage.css\";\nexport interface FileGridCardProps {\n file: ManagedFile;\n selected: boolean;\n canSelect: boolean;\n onSelect: (id: string, checked: boolean) => void;\n onPreview: (file: ManagedFile) => void;\n onDownload: (file: ManagedFile) => void;\n onDelete: (file: ManagedFile) => void;\n onDetail: (file: ManagedFile) => void;\n onCopyUrl: (file: ManagedFile) => void;\n canDelete: boolean;\n previewLoading: boolean;\n}\nexport function FileGridCard({ file, selected, canSelect, onSelect, onPreview, onDownload, onDelete, onDetail, onCopyUrl, canDelete, previewLoading, }: Readonly<FileGridCardProps>) {\n const [ctxPos, setCtxPos] = useState<{\n x: number;\n y: number;\n } | null>(null);\n const isPreviewable = canPreviewFile(file.mimeType, file.originalName);\n const ext = file.originalName.includes('.') ? file.originalName.split('.').pop()?.toUpperCase() : '';\n return (<>\n <div className={`files-grid-card${selected ? ' files-grid-card--selected' : ''}`} onContextMenu={(e) => {\n e.preventDefault();\n setCtxPos({ x: e.clientX, y: e.clientY });\n }}>\n <button type=\"button\" className=\"files-grid-card__preview-btn\" aria-label={isPreviewable ? `预览 ${file.originalName}` : file.originalName} style={{ cursor: isPreviewable ? 'pointer' : 'default' }} onClick={(e) => {\n if (canSelect && (e.ctrlKey || e.metaKey || e.shiftKey)) {\n onSelect(file.id, !selected);\n return;\n }\n if (isPreviewable)\n onPreview(file);\n }}/>\n {canSelect && (<div className=\"files-grid-card__checkbox\">\n <Checkbox checked={selected} onChange={() => onSelect(file.id, !selected)}/>\n </div>)}\n <div className=\"files-grid-card__media-wrap\">\n <div className=\"files-grid-card__media\">\n <span className=\"files-grid-card__icon\">\n {getFileTypeIcon(file.mimeType, 34, file.originalName)}\n </span>\n {ext && <span className=\"files-grid-card__type-badge\">{ext}</span>}\n {previewLoading && (<div className=\"files-grid-card__media-overlay\">\n <Spin />\n </div>)}\n </div>\n </div>\n <div className=\"files-grid-card__info\">\n <Tooltip content={file.originalName} position=\"top\">\n <div className=\"files-grid-card__name\">{file.originalName}</div>\n </Tooltip>\n <div className=\"files-grid-card__meta\">\n <span>{formatBytes(file.size)}</span>\n </div>\n </div>\n </div>\n {ctxPos && (<CursorContextDropdown point={ctxPos} contextKey={file.id} onClose={() => setCtxPos(null)} render={(<Dropdown.Menu>\n {isPreviewable && <Dropdown.Item onClick={() => onPreview(file)}>预览</Dropdown.Item>}\n <Dropdown.Item onClick={() => onDownload(file)}>下载</Dropdown.Item>\n <Dropdown.Item onClick={() => onDetail(file)}>详情</Dropdown.Item>\n <Dropdown.Item onClick={() => onCopyUrl(file)}>复制链接</Dropdown.Item>\n {canDelete && (<>\n <Dropdown.Divider />\n <Dropdown.Item type=\"danger\" onClick={() => {\n confirmAndDelete({\n title: '确认删除此文件?',\n content: '删除文件记录后,将同步尝试删除实际存储对象。',\n run: () => Promise.resolve(onDelete(file)),\n successMessage: null,\n });\n }}>删除</Dropdown.Item>\n </>)}\n </Dropdown.Menu>)}/>)}\n </>);\n}\n","/**\n * 表格「文件名」单元格统一组件\n *\n * 图标固定宽度、名称占满剩余列宽并在列边界省略,悬停 Tooltip 展示完整名称。\n * 需配合列定义 `ellipsis: { showTitle: false }` 使用(表格切为 fixed 布局并关闭原生 title)。\n *\n * - 默认按 mimeType/文件名推断托管文件图标;\n * - `icon` 可传入自定义图标(如 iconify 扩展名图标、文件夹图标);\n * - `onClick` 存在时整体渲染为可点击按钮(如文件夹进入目录)。\n */\nimport type { ReactNode } from 'react';\nimport { Tooltip } from \"@/ui\";\nimport { getFileTypeIcon } from \"@/utils/file-utils\";\nimport \"@/components/FileNameCell.css\";\nimport { escapeRegExp } from '@fudanda/shared/core';\ninterface FileNameCellProps {\n name: string;\n /** 自定义图标;缺省时按 mimeType/文件名推断 */\n icon?: ReactNode;\n mimeType?: string | null;\n /** 提供时整行渲染为可点击按钮 */\n onClick?: () => void;\n highlight?: string;\n}\nexport function FileNameCell({ name, icon, mimeType, onClick, highlight }: Readonly<FileNameCellProps>) {\n const parts = highlight ? name.split(new RegExp(`(${escapeRegExp(highlight)})`, 'gi')) : [name];\n const content = (<>\n <span className=\"file-name-cell__icon\">{icon ?? getFileTypeIcon(mimeType, 16, name)}</span>\n <Tooltip content={name}>\n <span className=\"file-name-cell__text\">{parts.map((part, index) => highlight && part.toLowerCase() === highlight.toLowerCase() ? <mark key={index}>{part}</mark> : part)}</span>\n </Tooltip>\n </>);\n if (onClick) {\n return (<button type=\"button\" className=\"file-name-cell file-name-cell--button\" onClick={onClick}>\n {content}\n </button>);\n }\n return <div className=\"file-name-cell\">{content}</div>;\n}\n","import { useEffect, useState } from 'react';\nimport { Button, Breadcrumb, Pagination, SideSheet, Space, Spin, Toast, Tooltip, Typography } from \"@/ui\";\nimport { Folder, ChevronLeft, ChevronRight, LayoutGrid, List as ListIcon } from 'lucide-react';\nimport type { FileStorageConfig, FolderEntry, ManagedFile } from '@fudanda/shared/platform';\nimport type { ColumnProps } from \"@/ui\";\nimport DateTimeText from \"@/components/DateTimeText\";\nimport { getFileFullUrl, canPreviewFile } from \"@/utils/file-utils\";\nimport { buildManagedFileActions } from \"@/utils/managed-file-actions\";\nimport { copyTextWithToast } from \"@/utils/clipboard\";\nimport { usePermission } from \"@fudanda/react-core/hooks/usePermission\";\nimport { usePagination } from \"@fudanda/react-core/hooks/usePagination\";\nimport { FilePreviewLayer } from \"@/components/FilePreviewLayer\";\nimport { FileDetailModal } from \"@/components/FileDetailModal\";\nimport { useFilePreview } from \"@/hooks/useFilePreview\";\nimport ConfigurableTable from \"@/components/ConfigurableTable\";\nimport { createOperationColumn } from \"@/components/ResponsiveTableActions\";\nimport { FileGridCard } from \"@/pages/system/files/components/FileGridCard\";\nimport { FileNameCell } from \"@/components/FileNameCell\";\nimport { useDeleteFiles, useFileDetail } from \"@fudanda/react-core/hooks/queries/files\";\nimport { useStorageBrowse } from \"@fudanda/react-core/hooks/queries/file-storage-configs\";\nimport { formatBytes } from '@fudanda/shared/core';\nimport \"@/pages/system/file-configs/StorageFileBrowser.css\";\nconst { Text } = Typography;\ninterface StorageFileBrowserProps {\n config: FileStorageConfig | null;\n onClose: () => void;\n}\nexport default function StorageFileBrowser({ config, onClose }: Readonly<StorageFileBrowserProps>) {\n const { hasPermission } = usePermission();\n const [viewMode, setViewMode] = useState<'list' | 'grid'>('list');\n const [currentPath, setCurrentPath] = useState('');\n const { page, setPage } = usePagination();\n const PAGE_SIZE = 50;\n const browseQuery = useStorageBrowse(config?.id, currentPath, !!config);\n const browseData = browseQuery.data ?? null;\n const loading = browseQuery.isFetching;\n const deleteMutation = useDeleteFiles();\n // Navigation history for back/forward\n const [historyStack, setHistoryStack] = useState<string[]>(['']);\n const [historyIndex, setHistoryIndex] = useState(0);\n const canGoBack = historyIndex > 0;\n const canGoForward = historyIndex < historyStack.length - 1;\n // Preview state\n const preview = useFilePreview(() => browseData?.files ?? []);\n const [detailFile, setDetailFile] = useState<ManagedFile | null>(null);\n const detailQuery = useFileDetail(detailFile?.id, !!detailFile);\n const displayedDetailFile = detailQuery.data ?? detailFile;\n const detailFileLoading = detailQuery.isFetching;\n // Reset state whenever the config changes (different storage or re-open)\n useEffect(() => {\n if (config) {\n setCurrentPath('');\n setViewMode('list');\n setPage(1);\n setHistoryStack(['']);\n setHistoryIndex(0);\n setDetailFile(null);\n preview.resetPreview();\n }\n // preview is recreated per render; reset only when the storage configuration changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [config, setPage]);\n const navigateTo = (path: string, pushHistory = true) => {\n setCurrentPath(path);\n setPage(1);\n if (pushHistory) {\n setHistoryStack((prev) => {\n const next = prev.slice(0, historyIndex + 1);\n next.push(path);\n return next;\n });\n setHistoryIndex((i) => i + 1);\n }\n };\n const navigateToFolder = (folder: FolderEntry) => navigateTo(folder.path);\n const navigateToPath = (path: string) => navigateTo(path);\n const goBack = () => {\n const newIndex = historyIndex - 1;\n const path = historyStack[newIndex] ?? '';\n setHistoryIndex(newIndex);\n setCurrentPath(path);\n setPage(1);\n };\n const goForward = () => {\n const newIndex = historyIndex + 1;\n const path = historyStack[newIndex] ?? '';\n setHistoryIndex(newIndex);\n setCurrentPath(path);\n setPage(1);\n };\n // Build breadcrumb segments from currentPath\n const breadcrumbSegments = currentPath ? currentPath.split('/') : [];\n const handleDelete = async (file: ManagedFile) => {\n await deleteMutation.mutateAsync([file.id]);\n Toast.success('文件已删除');\n void browseQuery.refetch();\n };\n const handleOpenDetail = (file: ManagedFile) => {\n setDetailFile(file);\n };\n const handleCopyUrl = async (file: ManagedFile) => {\n await copyTextWithToast(getFileFullUrl(file.url), { success: '链接已复制', error: '复制失败,请手动复制' });\n };\n const listColumns: ColumnProps<ManagedFile | FolderEntry>[] = [\n {\n title: '名称',\n key: 'name',\n ellipsis: { showTitle: false },\n render: (_: unknown, record: ManagedFile | FolderEntry) => {\n if (!('id' in record)) {\n // Folder row\n return (<FileNameCell name={record.name} icon={<Folder size={15} className=\"storage-browser__folder-icon\"/>} onClick={() => navigateToFolder(record)}/>);\n }\n // 与操作列“预览”一致:仅可预览类型渲染为可点击\n const previewable = canPreviewFile(record.mimeType, record.originalName);\n return <FileNameCell name={record.originalName} mimeType={record.mimeType} onClick={previewable ? () => void preview.handlePreview(record) : undefined}/>;\n },\n },\n {\n title: '大小',\n key: 'size',\n width: 110,\n align: 'right' as const,\n render: (_: unknown, record: ManagedFile | FolderEntry) => {\n if (!('id' in record))\n return <span className=\"table-cell-placeholder\">—</span>;\n return formatBytes(record.size);\n },\n },\n {\n title: '上传时间',\n key: 'createdAt',\n width: 180,\n render: (_: unknown, record: ManagedFile | FolderEntry) => {\n if (!('id' in record))\n return <span className=\"table-cell-placeholder\">—</span>;\n return <DateTimeText value={record.createdAt}/>;\n },\n },\n createOperationColumn<ManagedFile | FolderEntry>({\n width: 180,\n desktopInlineKeys: ['open', 'preview', 'download'],\n actions: (record) => {\n if (!('id' in record)) {\n return [\n {\n key: 'open',\n label: '打开',\n onClick: () => navigateToFolder(record),\n },\n ];\n }\n return buildManagedFileActions(record, {\n preview,\n onDetail: (file) => { void handleOpenDetail(file); },\n onCopyUrl: handleCopyUrl,\n onDelete: handleDelete,\n canDelete: hasPermission('system:file:delete'),\n });\n },\n }),\n ];\n const allItems: (ManagedFile | FolderEntry)[] = [\n ...(browseData?.folders ?? []),\n ...(browseData?.files ?? []),\n ];\n const totalCount = allItems.length;\n const pagedItems = allItems.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);\n return (<>\n <SideSheet title={config ? `文件浏览 · ${config.name}` : '文件浏览'} visible={!!config} onCancel={onClose} size=\"large\" bodyStyle={{ padding: 0, display: 'flex', flexDirection: 'column' }} headerStyle={{ borderBottom: '1px solid var(--arc-antd-color-border)' }}>\n {/* Toolbar: back/forward + breadcrumb + view mode toggle */}\n <div className=\"storage-browser__toolbar\">\n <Space spacing={4} style={{ flexShrink: 0 }}>\n <Button size=\"small\" theme=\"borderless\" type=\"tertiary\" icon={<ChevronLeft size={15}/>} disabled={!canGoBack} onClick={goBack}/>\n <Button size=\"small\" theme=\"borderless\" type=\"tertiary\" icon={<ChevronRight size={15}/>} disabled={!canGoForward} onClick={goForward}/>\n </Space>\n <Breadcrumb className=\"storage-browser__breadcrumb\">\n <Breadcrumb.Item onClick={() => navigateToPath('')} style={{ cursor: currentPath ? 'pointer' : 'default', color: currentPath ? 'var(--arc-antd-color-primary)' : undefined }}>\n 根目录\n </Breadcrumb.Item>\n {breadcrumbSegments.map((seg, idx) => {\n const segPath = breadcrumbSegments.slice(0, idx + 1).join('/');\n const isLast = idx === breadcrumbSegments.length - 1;\n return (<Breadcrumb.Item key={segPath} onClick={isLast ? undefined : () => navigateToPath(segPath)} style={{ cursor: isLast ? 'default' : 'pointer', color: isLast ? undefined : 'var(--arc-antd-color-primary)' }}>\n {seg}\n </Breadcrumb.Item>);\n })}\n </Breadcrumb>\n <Space spacing={0}>\n <Button size=\"small\" theme={viewMode === 'list' ? 'solid' : 'light'} type={viewMode === 'list' ? 'primary' : 'tertiary'} icon={<ListIcon size={14}/>} style={{ borderRadius: '4px 0 0 4px' }} onClick={() => setViewMode('list')}/>\n <Button size=\"small\" theme={viewMode === 'grid' ? 'solid' : 'light'} type={viewMode === 'grid' ? 'primary' : 'tertiary'} icon={<LayoutGrid size={14}/>} style={{ borderRadius: '0 4px 4px 0' }} onClick={() => setViewMode('grid')}/>\n </Space>\n </div>\n\n {/* Content area */}\n <div className=\"storage-browser__content\">\n <Spin spinning={loading} size=\"large\" wrapperClassName=\"storage-browser__spin\">\n {!loading && totalCount === 0 && (<div className=\"storage-browser__empty\">当前目录为空</div>)}\n\n {viewMode === 'list' && totalCount > 0 && (<ConfigurableTable bordered columns={listColumns} dataSource={pagedItems} rowKey={(record) => {\n if (!record)\n return '';\n return 'id' in record ? `file-${record.id}` : `folder-${record.path}`;\n }} loading={false} onRefresh={() => void browseQuery.refetch()} refreshLoading={loading} size=\"small\" pagination={false} onRow={(record) => {\n if (record && !('id' in record)) {\n return {\n onClick: () => navigateToFolder(record),\n style: { cursor: 'pointer' },\n };\n }\n return {};\n }}/>)}\n\n {viewMode === 'grid' && totalCount > 0 && (() => {\n const pagedFolders = pagedItems.filter((r): r is FolderEntry => !('id' in r));\n const pagedFiles = pagedItems.filter((r): r is ManagedFile => 'id' in r);\n return (<div className=\"storage-browser__grid-wrap\">\n {pagedFolders.length > 0 && (<div className=\"storage-browser__grid-section\">\n <div className=\"storage-browser__grid-section-label\">文件夹</div>\n <div className=\"storage-browser__folder-grid\">\n {pagedFolders.map((folder) => (<button key={folder.path} type=\"button\" className=\"storage-browser__folder-card\" onClick={() => navigateToFolder(folder)}>\n <Folder size={32} className=\"storage-browser__folder-card-icon\"/>\n <Tooltip content={folder.name} position=\"top\">\n <span className=\"storage-browser__folder-card-name\">{folder.name}</span>\n </Tooltip>\n </button>))}\n </div>\n </div>)}\n {pagedFiles.length > 0 && (<div className=\"storage-browser__grid-section\">\n {pagedFolders.length > 0 && (<div className=\"storage-browser__grid-section-label\">文件</div>)}\n <div className=\"storage-browser__file-grid\">\n {pagedFiles.map((file) => (<FileGridCard key={file.id} file={file} selected={false} canSelect={false} onSelect={() => { }} onPreview={preview.handlePreview} onDownload={preview.handleDownload} onDelete={handleDelete} onDetail={handleOpenDetail} onCopyUrl={handleCopyUrl} canDelete={hasPermission('system:file:delete')} previewLoading={preview.previewLoadingId === file.id}/>))}\n </div>\n </div>)}\n </div>);\n })()}\n </Spin>\n </div>\n\n {/* Footer: summary + pagination */}\n <div className=\"storage-browser__footer\">\n <Text type=\"secondary\" size=\"small\">\n {(browseData?.folders.length ?? 0) > 0 && `${browseData!.folders.length} 个文件夹`}\n {(browseData?.folders.length ?? 0) > 0 && (browseData?.files.length ?? 0) > 0 && ','}\n {(browseData?.files.length ?? 0) > 0 && `${browseData!.files.length} 个文件`}\n {totalCount === 0 && !loading && '空目录'}\n </Text>\n {totalCount > 0 && (<Pagination currentPage={page} pageSize={PAGE_SIZE} total={totalCount} onPageChange={setPage} size=\"small\" hoverShowPageSelect/>)}\n </div>\n </SideSheet>\n\n {/* Image / non-image file preview */}\n <FilePreviewLayer preview={preview}/>\n\n {/* File detail modal */}\n <FileDetailModal visible={!!detailFile} file={displayedDetailFile} loading={detailFileLoading} resolveUrl={(file) => getFileFullUrl(file.url)} onCopyUrl={(file) => void handleCopyUrl(file)} onClose={() => setDetailFile(null)}/>\n </>);\n}\n","import { IS_GO_FOUNDATION } from \"@fudanda/react-core/lib/foundation-mode\";\nimport { FormPasswordInput } from \"@/components/PasswordInput\";\nimport { useEffect, useState } from 'react';\nimport { Button, Col, Form, Radio, Row, Select, Switch, Tag, Toast, Typography } from \"@/ui\";\nimport { PlugZap } from 'lucide-react';\nimport { type CreateFileStorageConfigInput, type FileObjectAcl, type FileStorageConfig, type FileUrlStrategy } from '@fudanda/shared/platform';\nimport { fileStorageConfigContract } from '@fudanda/react-core/lib/native-operations';\nimport { FILE_OBJECT_ACL_LABELS, FILE_OBJECT_ACL_SUPPORT, FILE_STORAGE_PROVIDER_LABELS, FILE_STORAGE_PROVIDER_OPTIONS, FILE_URL_STRATEGY_LABELS, FILE_URL_STRATEGY_OPTIONS, PRESIGNED_EXPIRY_DEFAULT_SECONDS, PRESIGNED_EXPIRY_MAX_SECONDS, PRESIGNED_EXPIRY_MIN_SECONDS } from '@fudanda/shared/platform';\nimport type { ColumnProps } from \"@/ui\";\nimport { usePermission } from \"@fudanda/react-core/hooks/usePermission\";\nimport { useEditModal, formSaveMutation } from \"@/hooks/useEditModal\";\nimport ExportButton from \"@/components/ExportButton\";\nimport { dateTimeColumn, EMPTY_PLACEHOLDER, renderEllipsis } from \"@/utils/table-columns\";\nimport ConfigurableTable from \"@/components/ConfigurableTable\";\nimport { buildTableSearch, useStatusToggle, useCrudOperationColumn } from \"@/components/list-page/index\";\nimport { abortSubmit } from \"@fudanda/react-core/lib/abort-submit\";\nimport { runSubmitAction } from '../../../utils/submit-action';\nimport StorageFileBrowser from \"@/pages/system/file-configs/StorageFileBrowser\";\nimport { useDeleteFileStorageConfigs, useFileStorageConfigDetail, useFileStorageConfigList, useSaveFileStorageConfig, useSetDefaultFileStorageConfig, useTestFileStorageConfig } from \"@fudanda/react-core/hooks/queries/file-storage-configs\";\nimport { CreateButton } from \"@/components/toolbar-controls\";\nimport { useListPage } from \"@/hooks/useListPage\";\nimport { EditFormSheet } from \"@/components/EditFormModal\";\nimport \"@/pages/system/file-configs/FileStorageConfigsPanel.css\";\nconst { Text } = Typography;\ntype FileStorageConfigFormValues = Partial<Omit<CreateFileStorageConfigInput, 'presignedExpirySeconds'>> & { presignedExpirySeconds?: number };\ntype FileStorageProvider = FileStorageConfig['provider'];\n/** 支持对象级读写权限(canned ACL)的 provider */\nconst OBJECT_ACL_PROVIDERS: readonly FileStorageProvider[] = ['s3'];\nfunction normalizeOptional(value?: string): string {\n return value?.trim() ?? '';\n}\n/** 各 provider 公共的基础字段(objectAcl 与专属凭据字段由各分支自行补充) */\nfunction buildPayload(provider: FileStorageProvider, isDefault: boolean, values: FileStorageConfigFormValues): CreateFileStorageConfigInput {\n return fileStorageConfigContract.create.input.shape.body.parse({\n name: values.name?.trim() ?? '', status: values.status ?? 'enabled', isDefault,\n basePath: normalizeOptional(values.basePath), objectAcl: 'default', urlStrategy: 'proxy', publicBaseUrl: '',\n presignedExpirySeconds: PRESIGNED_EXPIRY_DEFAULT_SECONDS, provider,\n remark: normalizeOptional(values.remark),\n ...(provider === 'local' ? { localRootPath: normalizeOptional(values.localRootPath) } : {\n s3Region: normalizeOptional(values.s3Region), s3Endpoint: normalizeOptional(values.s3Endpoint),\n s3Bucket: normalizeOptional(values.s3Bucket), s3AccessKeyId: normalizeOptional(values.s3AccessKeyId),\n s3SecretAccessKey: normalizeOptional(values.s3SecretAccessKey), s3ForcePathStyle: values.s3ForcePathStyle ?? false,\n }),\n });\n}\nfunction getStorageSummary(config: FileStorageConfig) {\n return config.provider === 'local' ? config.localRootPath || EMPTY_PLACEHOLDER : [config.s3Bucket, config.s3Region].filter(Boolean).join(' / ') || EMPTY_PLACEHOLDER;\n}\nexport default function FileStorageConfigsPanel() {\n const { hasPermission } = usePermission();\n const page = useListPage({\n contract: fileStorageConfigContract,\n useList: useFileStorageConfigList,\n });\n const { tableProps, filterQuery } = page;\n const [formProvider, setFormProvider] = useState<FileStorageProvider>('local');\n const [formIsDefault, setFormIsDefault] = useState(false);\n const [browsingConfig, setBrowsingConfig] = useState<FileStorageConfig | null>(null);\n const saveMutation = useSaveFileStorageConfig();\n const statusMutation = useSaveFileStorageConfig();\n const modal = useEditModal<FileStorageConfig, FileStorageConfigFormValues, CreateFileStorageConfigInput>({\n entityName: '文件配置',\n save: formSaveMutation(saveMutation, { create: fileStorageConfigContract.create.input.shape.body, update: fileStorageConfigContract.update.input.shape.body }),\n useDetail: useFileStorageConfigDetail,\n defaults: {\n name: '',\n provider: 'local',\n status: 'enabled',\n isDefault: false,\n basePath: '',\n objectAcl: 'default',\n urlStrategy: 'proxy',\n publicBaseUrl: '',\n presignedExpirySeconds: PRESIGNED_EXPIRY_DEFAULT_SECONDS,\n localRootPath: 'storage/local',\n remark: '',\n },\n // 密钥字段为 write-only:接口不返回,表单一律以空值起始,留空提交即沿用原值\n toValues: (config) => ({\n name: config.name, status: config.status, isDefault: config.isDefault, provider: config.provider,\n basePath: config.basePath, objectAcl: 'default', urlStrategy: 'proxy', publicBaseUrl: '',\n presignedExpirySeconds: PRESIGNED_EXPIRY_DEFAULT_SECONDS, localRootPath: config.localRootPath ?? '',\n s3Region: config.s3Region ?? '', s3Endpoint: config.s3Endpoint ?? '', s3Bucket: config.s3Bucket ?? '',\n s3AccessKeyId: config.s3AccessKeyId ?? '', s3SecretAccessKey: '', s3ForcePathStyle: config.s3ForcePathStyle ?? false,\n remark: config.remark ?? '',\n }),\n beforeSave: (values) => buildPayload(formProvider, formIsDefault, values),\n successMessage: ({ isEdit }) => isEdit ? '文件服务配置已更新' : '文件服务配置已创建',\n labelWidth: 150,\n });\n const deleteMutation = useDeleteFileStorageConfigs();\n const setDefaultMutation = useSetDefaultFileStorageConfig();\n const testMutation = useTestFileStorageConfig();\n const modalTestLoading = testMutation.isPending && !testMutation.variables?.id;\n const testingConfigId = testMutation.isPending ? (testMutation.variables?.id ?? null) : null;\n useEffect(() => {\n if (!modal.editing)\n return;\n setFormProvider(modal.editing.provider);\n setFormIsDefault(modal.editing.isDefault);\n }, [modal.editing]);\n const openCreate = () => {\n setFormProvider('local');\n setFormIsDefault(false);\n modal.openCreate();\n };\n const openEdit = (config: FileStorageConfig) => {\n setFormProvider(config.provider);\n setFormIsDefault(config.isDefault);\n modal.openEdit(config);\n };\n const handleModalTest = async () => {\n let values;\n try {\n values = await modal.formApi.current!.validate();\n }\n catch {\n return;\n }\n if (!values)\n return;\n const payload = buildPayload(formProvider, formIsDefault, values);\n await testMutation.mutateAsync(modal.editing ? { id: modal.editing.id, values: payload } : { values: payload });\n Toast.success('存储连接测试通过');\n };\n const handleSetDefault = async (config: FileStorageConfig) => {\n await setDefaultMutation.mutateAsync({ params: { id: config.id } });\n Toast.success('默认文件服务已更新');\n };\n const handleTestSaved = async (config: FileStorageConfig) => {\n await testMutation.mutateAsync({ id: config.id, values: {} });\n Toast.success('存储连接测试通过');\n };\n const status = useStatusToggle<FileStorageConfig>({\n toggle: async (config, enabled) => {\n if (!enabled && config.isDefault) {\n Toast.warning('默认配置不能禁用,请先将其他配置设为默认');\n abortSubmit('default-config-cannot-be-disabled');\n }\n await statusMutation.mutateAsync({ id: config.id, values: buildPayload(config.provider, config.isDefault, { ...config, status: enabled ? 'enabled' : 'disabled' }) });\n },\n confirmDisable: (config) => {\n if (config.isDefault) {\n return null;\n }\n return {\n danger: true,\n title: `确认禁用「${config.name}」?`,\n okText: '确认禁用',\n };\n },\n disabled: !hasPermission('system:file:config:update'),\n messages: { disabled: '已禁用' },\n });\n const operationColumn = useCrudOperationColumn<FileStorageConfig>({\n permission: 'system:file:config',\n edit: openEdit,\n remove: deleteMutation,\n title: '确认删除此文件服务配置?',\n content: '若已绑定文件记录,后端会阻止删除。',\n successMessage: '文件服务配置已删除',\n disabled: { remove: (record) => record.isDefault },\n extra: (record) => [\n {\n key: 'browse',\n label: '浏览',\n hidden: !hasPermission('system:file:list'),\n onClick: () => setBrowsingConfig(record),\n },\n {\n key: 'default',\n label: '设为默认',\n hidden: !hasPermission('system:file:config:default'),\n disabled: record.isDefault || record.status !== 'enabled',\n onClick: () => handleSetDefault(record),\n },\n ],\n extraBetween: (record) => [\n {\n key: 'test',\n label: '测试连接',\n loading: testingConfigId === record.id,\n hidden: !hasPermission('system:file:config'),\n onClick: () => { void handleTestSaved(record); },\n },\n ],\n width: 180,\n desktopInlineKeys: ['browse', 'edit'],\n });\n const columns: ColumnProps<FileStorageConfig>[] = [\n {\n title: '配置名称',\n dataIndex: 'name',\n minWidth: 180,\n render: renderEllipsis,\n },\n {\n title: '存储类型',\n dataIndex: 'provider',\n width: 120,\n render: (provider: FileStorageProvider) => {\n // 文案统一来自 @fudanda/shared;Tag 色为本页特化\n const colorMap: Record<FileStorageProvider, 'blue' | 'orange' | 'purple' | 'teal' | 'red' | 'cyan' | 'indigo' | 'violet' | 'green'> = {\n local: 'blue', s3: 'purple',\n };\n return <Tag color={colorMap[provider]} size=\"small\">{FILE_STORAGE_PROVIDER_LABELS[provider]}</Tag>;\n },\n },\n {\n title: '默认服务',\n dataIndex: 'isDefault',\n width: 110,\n align: 'center',\n render: (isDefault: boolean) => isDefault ? <Tag color=\"green\" size=\"small\">默认</Tag> : <span className=\"table-cell-placeholder\">—</span>,\n },\n {\n title: '存储信息',\n key: 'storageSummary',\n dataIndex: 'storageSummary',\n width: 180,\n render: (_: unknown, record: FileStorageConfig) => {\n const labelMap = { local: '目录', s3: 'Bucket' };\n const label = labelMap[record.provider] ?? 'Bucket';\n const summary = getStorageSummary(record);\n return renderEllipsis(`${label}: ${summary}`);\n },\n },\n {\n title: '基础路径',\n dataIndex: 'basePath',\n width: 160,\n render: (value?: string) => renderEllipsis(value),\n },\n {\n title: '读写权限',\n dataIndex: 'objectAcl',\n width: 120,\n align: 'center',\n render: (value: FileObjectAcl | undefined, record: FileStorageConfig) => {\n if (!OBJECT_ACL_PROVIDERS.includes(record.provider))\n return <span className=\"table-cell-placeholder\">—</span>;\n const colorMap: Record<FileObjectAcl, 'grey' | 'blue' | 'orange' | 'red'> = {\n 'default': 'grey',\n };\n const acl = value ?? 'default';\n return <Tag color={colorMap[acl]} size=\"small\">{FILE_OBJECT_ACL_LABELS[acl]}</Tag>;\n },\n },\n {\n title: '访问策略',\n dataIndex: 'urlStrategy',\n width: 120,\n align: 'center',\n render: (value: FileUrlStrategy | undefined) => {\n const strategy = value ?? 'proxy';\n const colorMap: Record<FileUrlStrategy, 'grey' | 'green' | 'purple'> = {\n proxy: 'grey',\n };\n return <Tag color={colorMap[strategy]} size=\"small\">{FILE_URL_STRATEGY_LABELS[strategy]}</Tag>;\n },\n },\n dateTimeColumn('更新时间', 'updatedAt'),\n status.column(),\n operationColumn,\n ];\n return (<div className=\"storage-configs-panel\">\n\n <div className=\"storage-configs-tip\" style={{ marginBottom: 0, marginTop: -4 }}>\n <Text type=\"secondary\">当前支持多文件服务配置,但上传时会优先走\"默认文件服务\"。切换默认服务不会影响历史文件记录。</Text>\n </div>\n\n <ConfigurableTable<FileStorageConfig> searchConfig={buildTableSearch({\n page: (page),\n filters: (['status', ['startTime', 'endTime']]),\n create: (<CreateButton permission=\"system:file:config:create\" onClick={openCreate}/>),\n actions: ((<ExportButton entity=\"system.file-storage-configs\" query={filterQuery}/>)),\n filterTitle: \"文件配置筛选\",\n actionTitle: \"文件配置操作\"\n })} columns={columns} {...tableProps}/>\n\n <EditFormSheet modal={modal} width={780} footer={(<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8 }}> <Button icon={<PlugZap size={14}/>} loading={modalTestLoading} disabled={modal.detailLoading} onClick={() => void handleModalTest()}> 测试连接 </Button> <div style={{ display: 'flex', gap: 8 }}> <Button type=\"tertiary\" onClick={modal.close}>取消</Button> <Button type=\"primary\" theme=\"solid\" loading={modal.modalProps.okButtonProps.loading} disabled={modal.modalProps.okButtonProps.disabled} onClick={() => void runSubmitAction(modal.modalProps.onOk)}> 保存 </Button> </div> </div>)}>\n <Form.Section text=\"基础信息\">\n <Row gutter={16}>\n <Col span={12}>\n <Form.Input field=\"name\" label=\"配置名称\" placeholder=\"请输入配置名称\" rules={[{ required: true, message: '请输入配置名称' }]}/>\n </Col>\n <Col span={12}>\n <Form.Select field=\"provider\" label=\"存储类型\" style={{ width: '100%' }} optionList={FILE_STORAGE_PROVIDER_OPTIONS.filter(option => ['local', 's3'].includes(option.value))} onChange={(value) => {\n if (value !== 'local' && value !== 's3') return;\n const next = value;\n setFormProvider(next);\n const currentAcl = modal.formApi.current?.getValue('objectAcl') as FileObjectAcl | undefined;\n if (currentAcl && !(FILE_OBJECT_ACL_SUPPORT[next] ?? []).includes(currentAcl)) {\n modal.formApi.current?.setValue('objectAcl', 'default');\n }\n }} placeholder=\"请选择存储类型\"/>\n </Col>\n </Row>\n <Row gutter={16}>\n <Col span={12}>\n <Form.Select field=\"status\" label=\"状态\" style={{ width: '100%' }} placeholder=\"请选择状态\">\n <Select.Option value=\"enabled\">启用</Select.Option>\n <Select.Option value=\"disabled\">禁用</Select.Option>\n </Form.Select>\n </Col>\n {(formProvider === 's3') && <Col span={12}>\n <Form.Input field=\"basePath\" label=\"基础路径\" placeholder=\"例如 uploads / images\"/>\n </Col>}\n </Row>\n <Form.Slot label=\"设为默认服务\">\n <Switch checked={formIsDefault} onChange={setFormIsDefault}/>\n </Form.Slot>\n <Row gutter={16}>\n <Col span={24}>\n <Form.Input field=\"remark\" label=\"备注\" placeholder=\"选填,说明该文件服务的用途\"/>\n </Col>\n </Row>\n </Form.Section>\n\n <Form.Section text={`${FILE_STORAGE_PROVIDER_LABELS[formProvider]} 连接配置`}>\n {formProvider === 'local' && (<Row gutter={16}>\n <Col span={24}>\n <Form.Input field=\"localRootPath\" label=\"存储目录\" placeholder=\"例如 storage/local 或 D:/uploads\" rules={[{ required: true, message: '请输入本地磁盘存储目录' }]}/>\n </Col>\n </Row>)}\n\n\n\n {formProvider === 's3' && (<>\n <Row gutter={16}>\n <Col span={12}>\n <Form.Input field=\"s3Region\" label=\"Region\" placeholder=\"请输入 S3 Region\" rules={[{ required: true, message: '请输入 S3 Region' }]}/>\n </Col>\n <Col span={12}>\n <Form.Input field=\"s3Bucket\" label=\"Bucket\" placeholder=\"请输入 S3 Bucket\" rules={[{ required: true, message: '请输入 S3 Bucket' }]}/>\n </Col>\n </Row>\n <Row gutter={16}>\n <Col span={24}>\n <Form.Input field=\"s3Endpoint\" label=\"Endpoint\" placeholder=\"可选,兼容 S3 自定义存储\"/>\n </Col>\n </Row>\n <Row gutter={16}>\n <Col span={24}>\n <Form.Input field=\"s3AccessKeyId\" label=\"Access Key ID\" placeholder=\"请输入 Access Key ID\" rules={[{ required: true, message: '请输入 Access Key ID' }]}/>\n </Col>\n </Row>\n <Row gutter={16}>\n <Col span={24}>\n <FormPasswordInput field=\"s3SecretAccessKey\" label=\"Secret Access Key\" placeholder={modal.isEdit ? '留空表示不修改' : '请输入 Secret Access Key'} rules={modal.isEdit ? [] : [{ required: true, message: '请输入 Secret Access Key' }]}/>\n </Col>\n </Row>\n <Form.Checkbox field=\"s3ForcePathStyle\" noLabel>强制路径样式(MinIO / Ceph 等兼容当需开启)</Form.Checkbox>\n </>)}\n\n\n\n\n\n\n\n\n\n\n\n\n </Form.Section>\n\n <Form.Section text=\"访问与权限\">\n {OBJECT_ACL_PROVIDERS.includes(formProvider) && (<Form.RadioGroup field=\"objectAcl\" label=\"读写权限\" type=\"button\" extraText={formProvider === 's3'\n ? '上传文件将按此权限设置对象 ACL。注意:AWS S3 新建桶默认禁用 ACL(Bucket owner enforced),启用前请先在桶设置中开启;MinIO / Cloudflare R2 不支持对象 ACL,请保持「继承 Bucket」。'\n : '上传文件将按此权限设置对象 ACL;「继承 Bucket」表示不单独指定、跟随 Bucket 权限。公共读 / 公共读写存在数据泄露风险,请谨慎选择。'}>\n {(['default'] as const).map((acl) => (<Radio key={acl} value={acl}>{FILE_OBJECT_ACL_LABELS[acl]}</Radio>))}\n </Form.RadioGroup>)}\n\n <Row gutter={16}>\n <Col span={24}>\n <Form.Select field=\"urlStrategy\" disabled={IS_GO_FOUNDATION} label=\"访问策略\" style={{ width: '100%' }} extraText=\"代理:文件流量经过服务端(兜底);公开直链:返回永久直连地址,要求对象可公开读;临时签名:按需签发限时直连地址,适合私有文件(本地磁盘 / SFTP 不支持)\">\n {(FILE_URL_STRATEGY_OPTIONS.filter(option => option.value === 'proxy')).map((opt) => (<Select.Option key={opt.value} value={opt.value}>{opt.label}</Select.Option>))}\n </Form.Select>\n </Col>\n </Row>\n <Row gutter={16}>\n <Col span={24}>\n <Form.InputNumber field=\"presignedExpirySeconds\" disabled={IS_GO_FOUNDATION} label=\"签名有效期(秒)\" style={{ width: '100%' }} min={PRESIGNED_EXPIRY_MIN_SECONDS} max={PRESIGNED_EXPIRY_MAX_SECONDS} extraText=\"仅临时签名策略生效;修改只影响新签发的链接\"/>\n </Col>\n </Row>\n <Row gutter={16}>\n <Col span={24}>\n <Form.Input field=\"publicBaseUrl\" disabled={IS_GO_FOUNDATION} label=\"访问域名(CDN)\" placeholder=\"可选,例如 https://cdn.example.com,公开直链优先使用该域名\"/>\n </Col>\n </Row>\n </Form.Section>\n </EditFormSheet>\n\n <StorageFileBrowser config={browsingConfig} onClose={() => setBrowsingConfig(null)}/>\n </div>);\n}\n","import { filesList, filesBatchDownload } from '@fudanda/shared/api/files';\nimport { goTransport } from '@fudanda/react-core/lib/go-transport';\nimport { useEffect, useRef, useState } from 'react';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { AppModal } from \"@/components/AppModal\";\nimport { FileDetailModal } from \"@/components/FileDetailModal\";\nimport { Button, Checkbox, List, Pagination, Progress, Space, Toast, Tooltip, Typography } from \"@/ui\";\nimport { Plus, FolderDown, LayoutGrid, List as ListIcon, CheckCircle2, XCircle, X } from 'lucide-react';\nimport type { ManagedFile } from '@fudanda/shared/platform';\nimport { FILE_STORAGE_PROVIDER_OPTIONS, FILE_TYPE_FILTER_OPTIONS } from '@fudanda/shared/platform';\n\n\nimport type { ColumnProps } from \"@/ui\";\nimport { formatDateTimeRangeValuesForApi } from \"@fudanda/react-core/utils/date\";\nimport { downloadBlob } from \"@fudanda/react-core/utils/download\";\nimport { getFileTypeIcon, fetchManagedFileBlob, getFileFullUrl, canPreviewFile } from \"@/utils/file-utils\";\nimport { buildManagedFileActions } from \"@/utils/managed-file-actions\";\nimport { chunkedUpload, CHUNKED_UPLOAD_CANCELLED } from \"@fudanda/react-core/utils/chunked-upload\";\nimport { FilePreviewLayer } from \"@/components/FilePreviewLayer\";\nimport { useFilePreview } from \"@/hooks/useFilePreview\";\nimport { FileGridCard } from \"@/pages/system/files/components/FileGridCard\";\nimport { FileNameCell } from \"@/components/FileNameCell\";\nimport { usePermission } from \"@fudanda/react-core/hooks/usePermission\";\nimport { dateTimeColumn, renderEllipsis } from \"@/utils/table-columns\";\nimport { usePreferences } from \"@fudanda/react-core/hooks/usePreferences\";\nimport { usePagination } from \"@fudanda/react-core/hooks/usePagination\";\nimport ConfigurableTable from \"@/components/ConfigurableTable\";\nimport { createOperationColumn } from \"@/components/ResponsiveTableActions\";\nimport { useDefaultFileStorageConfig } from \"@fudanda/react-core/hooks/queries/file-storage-configs\";\nimport { fileKeys, invalidateAfterFilesAdded, useChunkUploadThreshold, useDeleteFiles, useFileDetail, useFileList, useUploadFile } from \"@fudanda/react-core/hooks/queries/files\";\nimport { useListSearch } from \"@fudanda/react-core/hooks/useListSearch\";\nimport { BatchDeleteButton } from \"@/components/toolbar-controls\";\nimport { confirmAndDelete, buildTableSearch, TableSearchPanel, listTableProps } from \"@/components/list-page/index\";\nimport { copyTextWithToast } from \"@/utils/clipboard\";\nimport \"@/pages/system/files/FilesPage.css\";\nimport { formatBytes } from '@fudanda/shared/core';\nimport { DateRangeFilter, FilterSelect, KeywordInput } from \"@/components/search-filters\";\nimport { useFilterQuery } from \"@fudanda/react-core/hooks/useFilterQuery\";\nconst { Text } = Typography;\ninterface UploadItem {\n uid: string;\n name: string;\n size: number;\n progress: number;\n status: 'pending' | 'uploading' | 'success' | 'error' | 'cancelled';\n errorMsg?: string;\n}\nconst UPLOAD_ACTIVE_STATUSES: ReadonlyArray<UploadItem['status']> = ['pending', 'uploading'];\nconst isUploadFinished = (item: UploadItem) => !UPLOAD_ACTIVE_STATUSES.includes(item.status);\nconst FILE_GRID_PAGE_SIZE = 60;\nconst FILE_GRID_PAGE_SIZE_OPTIONS = [60, 120, 240];\nfunction getProgressStroke(status: UploadItem['status']): string | undefined {\n if (status === 'success')\n return 'var(--arc-antd-color-success)';\n if (status === 'error')\n return 'var(--arc-antd-color-danger)';\n if (status === 'cancelled')\n return 'var(--arc-antd-color-disabled-text)';\n return undefined;\n}\nfunction uploadSingleFile(file: File, uid: string, signal: AbortSignal, chunkThreshold: number, setItems: React.Dispatch<React.SetStateAction<UploadItem[]>>, uploadFile: (files: Blob[], onProgress: (percent: number) => void, signal: AbortSignal) => Promise<unknown>) {\n const updateItem = (updater: (item: UploadItem) => UploadItem) => setItems(prev => prev.map(item => item.uid === uid ? updater(item) : item));\n const onDone = () => updateItem(item => ({ ...item, progress: 100, status: 'success' }));\n const onFail = (err: unknown) => updateItem(item => signal.aborted\n ? { ...item, status: 'cancelled' }\n : { ...item, status: 'error', errorMsg: err instanceof Error ? err.message : '上传失败' });\n updateItem(item => ({ ...item, status: 'uploading' }));\n // 超过运行时设置的阈值走分片上传 + 断点续传\n if (file.size > chunkThreshold) {\n chunkedUpload(file, {\n signal,\n onProgress: (percent) => updateItem(item => ({ ...item, progress: percent })),\n }).then(onDone).catch(onFail);\n return;\n }\n void uploadFile([file], (percent) => updateItem(item => ({ ...item, progress: percent })), signal)\n .then(onDone).catch(onFail);\n}\n/** 加载图片并返回其分辨率,失败时返回 null;优先用 directUrl 直挂(免 blob fetch/CORS) */\nasync function loadImageResolution(file: {\n url: string;\n directUrl?: string | null;\n}): Promise<{\n width: number;\n height: number;\n} | null> {\n try {\n let src: string;\n const external = file.directUrl ?? (/^https?:\\/\\//.test(file.url) ? file.url : null);\n if (external) {\n src = external;\n }\n else {\n const blob = await fetchManagedFileBlob(file.url);\n src = URL.createObjectURL(blob);\n }\n return await new Promise((resolve) => {\n const img = new Image();\n const cleanup = () => {\n if (!external)\n URL.revokeObjectURL(src);\n };\n img.onload = () => { cleanup(); resolve({ width: img.naturalWidth, height: img.naturalHeight }); };\n img.onerror = () => { cleanup(); resolve(null); };\n img.src = src;\n });\n }\n catch {\n return null;\n }\n}\nexport default function FileListPanel({ active }: { active: boolean }) {\n const queryClient = useQueryClient();\n const { hasPermission } = usePermission();\n const { preferences, setPreferences, canEditPreference } = usePreferences();\n interface SearchParams {\n keyword: string;\n provider?: string;\n fileType?: string;\n timeRange: [\n Date,\n Date\n ] | null;\n }\n const defaultSearchParams: SearchParams = { keyword: '', provider: undefined, fileType: undefined, timeRange: null };\n const fileInputRef = useRef<HTMLInputElement | null>(null);\n const [uploadItems, setUploadItems] = useState<UploadItem[]>([]);\n const [uploadProgressVisible, setUploadProgressVisible] = useState(false);\n const uploadControllersRef = useRef(new Map<string, AbortController>());\n const { bind, bindKeyword, submittedParams, handleSearch, handleReset, } = useListSearch<SearchParams>({ defaults: defaultSearchParams, listKey: fileKeys.lists });\n const viewMode = preferences.filesViewMode ?? 'list';\n const listPagination = usePagination({ resetKey: viewMode });\n const gridPagination = usePagination({\n pageSize: FILE_GRID_PAGE_SIZE,\n pageSizeOpts: FILE_GRID_PAGE_SIZE_OPTIONS,\n resetKey: viewMode,\n });\n const { page, pageSize, setPage, setPageSize, buildPagination } = viewMode === 'grid' ? gridPagination : listPagination;\n const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);\n const [batchDownloadLoading, setBatchDownloadLoading] = useState(false);\n const [detailFile, setDetailFile] = useState<ManagedFile | null>(null);\n const [imageResolution, setImageResolution] = useState<{\n width: number;\n height: number;\n } | null>(null);\n const defaultConfigQuery = useDefaultFileStorageConfig();\n const defaultConfig = defaultConfigQuery.data?.config ?? null;\n // 已提交筛选 → 契约查询参数:只映射一次\n const [startDate, endDate] = formatDateTimeRangeValuesForApi(submittedParams.timeRange);\n const filterQuery = useFilterQuery({\n keyword: submittedParams.keyword,\n provider: filesList.query.shape.provider.safeParse(submittedParams.provider).data,\n fileType: filesList.query.shape.fileType.safeParse(submittedParams.fileType).data,\n startDate, endDate,\n });\n const listQuery = useFileList({ page, pageSize, ...filterQuery }, active);\n const data = listQuery.data ?? null;\n const preview = useFilePreview(() => data?.list ?? []);\n const uploadFileMutation = useUploadFile();\n const chunkThreshold = useChunkUploadThreshold();\n const deleteMutation = useDeleteFiles();\n const batchDeleteMutation = useDeleteFiles();\n const detailQuery = useFileDetail(detailFile?.id, !!detailFile);\n const displayedDetailFile = detailQuery.data ?? detailFile;\n const detailFileLoading = detailQuery.isFetching;\n const toggleViewMode = (mode: 'list' | 'grid') => {\n if (!canEditPreference('filesViewMode'))\n return;\n setPreferences({ filesViewMode: mode });\n };\n const handleGridSelect = (id: string, checked: boolean) => {\n setSelectedRowKeys((prev) => checked ? [...prev, id] : prev.filter((k) => k !== id));\n };\n const handleGridSelectAll = (checked: boolean) => {\n const currentPageIds = (data?.list ?? []).map((f) => f.id);\n if (checked) {\n setSelectedRowKeys((prev) => {\n const next = [...prev];\n for (const id of currentPageIds) {\n if (!next.includes(id))\n next.push(id);\n }\n return next;\n });\n }\n else {\n setSelectedRowKeys((prev) => prev.filter((k) => !currentPageIds.includes(k)));\n }\n };\n const handleOpenDetail = (file: ManagedFile) => {\n setDetailFile(file);\n setImageResolution(null);\n };\n useEffect(() => {\n const file = detailQuery.data;\n if (file?.mimeType?.startsWith('image/')) {\n void loadImageResolution(file).then((r) => {\n if (r)\n setImageResolution(r);\n });\n }\n }, [detailQuery.data]);\n useEffect(() => {\n if (uploadProgressVisible && uploadItems.length > 0 && uploadItems.every(isUploadFinished)) {\n const successCount = uploadItems.filter(item => item.status === 'success').length;\n const timer = setTimeout(() => {\n setUploadProgressVisible(false);\n if (successCount > 0) {\n Toast.success(successCount > 1 ? `成功上传 ${successCount} 个文件` : '文件上传成功');\n setPage(1);\n invalidateAfterFilesAdded(queryClient);\n }\n }, 1000);\n return () => clearTimeout(timer);\n }\n }, [uploadItems, uploadProgressVisible, queryClient, setPage]);\n const handlePickFile = () => {\n fileInputRef.current?.click();\n };\n const handleUpload = (event: React.ChangeEvent<HTMLInputElement>) => {\n const files = Array.from(event.target.files ?? []);\n event.target.value = '';\n if (files.length === 0)\n return;\n const items: UploadItem[] = files.map((f, i) => ({ uid: `${f.name}-${Date.now()}-${i}`, name: f.name, size: f.size, progress: 0, status: 'pending' as const }));\n uploadControllersRef.current.clear();\n setUploadItems(items);\n setUploadProgressVisible(true);\n for (const [i, file] of files.entries()) {\n const controller = new AbortController();\n uploadControllersRef.current.set(items[i].uid, controller);\n uploadSingleFile(file, items[i].uid, controller.signal, chunkThreshold, setUploadItems, (files, onProgress, signal) => uploadFileMutation.mutateAsync({ files, onProgress, signal }));\n }\n };\n /** 用户显式取消:带原因中止,分片上传据此通知服务端释放会话,不再续传 */\n const handleCancelUpload = (uid: string) => {\n uploadControllersRef.current.get(uid)?.abort(CHUNKED_UPLOAD_CANCELLED);\n };\n const handleDelete = async (file: ManagedFile) => {\n await deleteMutation.mutateAsync([file.id]);\n Toast.success('文件已删除');\n };\n const handleBatchDelete = () => {\n confirmAndDelete({\n title: `确认删除选中的 ${selectedRowKeys.length} 个文件?`,\n content: '删除后将同步尝试删除实际存储对象,无法恢复。',\n run: () => batchDeleteMutation.mutateAsync(selectedRowKeys),\n successMessage: '批量删除成功',\n onDeleted: () => setSelectedRowKeys([]),\n });\n };\n const handleBatchDownload = async () => {\n if (selectedRowKeys.length === 0)\n return;\n setBatchDownloadLoading(true);\n try {\n const response = await goTransport.download(filesBatchDownload, { body: { ids: selectedRowKeys } });\n const blob = await response.blob();\n if (!blob)\n return;\n downloadBlob(blob, `files_${Date.now()}.zip`);\n Toast.success(`已打包 ${selectedRowKeys.length} 个文件`);\n }\n catch {\n Toast.error('批量下载失败');\n }\n finally {\n setBatchDownloadLoading(false);\n }\n };\n const handleCopyUrl = async (file: ManagedFile) => {\n await copyTextWithToast(file.directUrl ?? getFileFullUrl(file.url), { success: '链接已复制', error: '复制失败,请手动复制' });\n };\n const columns: ColumnProps<ManagedFile>[] = [\n {\n title: '文件名',\n dataIndex: 'originalName',\n minWidth: 220,\n ellipsis: { showTitle: false },\n render: (name: string, record: ManagedFile) => {\n // 与操作列“预览”一致:仅可预览类型渲染为可点击\n const previewable = canPreviewFile(record.mimeType, name);\n return (<FileNameCell name={name} mimeType={record.mimeType} onClick={previewable ? () => void preview.handlePreview(record) : undefined}/>);\n },\n },\n {\n title: '来源服务',\n dataIndex: 'storageName',\n width: 120,\n ellipsis: true,\n render: (_: string, record: ManagedFile) => renderEllipsis(record.storageName),\n },\n {\n title: 'MIME 类型',\n dataIndex: 'mimeType',\n width: 160,\n ellipsis: true,\n render: renderEllipsis,\n },\n {\n title: '大小',\n dataIndex: 'size',\n width: 100,\n align: 'right' as const,\n render: (size: number) => formatBytes(size),\n },\n dateTimeColumn('上传时间', 'createdAt'),\n {\n title: '上传人',\n dataIndex: 'uploaderName',\n width: 100,\n ellipsis: true,\n render: renderEllipsis,\n },\n createOperationColumn<ManagedFile>({\n width: 180,\n desktopInlineKeys: ['preview', 'download'],\n actions: (record) => buildManagedFileActions(record, {\n preview,\n onDetail: (file) => { void handleOpenDetail(file); },\n onCopyUrl: handleCopyUrl,\n onDelete: handleDelete,\n canDelete: hasPermission('system:file:delete'),\n }),\n }),\n ];\n const fileTableSearch = buildTableSearch({\n keyword: ((<KeywordInput placeholder=\"搜索文件名 / 对象键 / 文件服务\" {...bindKeyword('keyword')} style={{ width: 'min(280px, 100%)' }}/>)),\n filters: ((<>\n <FilterSelect placeholder=\"全部存储类型\" items={FILE_STORAGE_PROVIDER_OPTIONS.filter(option => ['local', 's3'].includes(option.value))} {...bind('provider')} width={140}/>\n <FilterSelect placeholder=\"全部文件类型\" items={FILE_TYPE_FILTER_OPTIONS} {...bind('fileType')} width={140}/>\n <DateRangeFilter searchLabel=\"上传时间\" type=\"dateTimeRange\" {...bind('timeRange')}/>\n </>)),\n onSearch: (handleSearch),\n onReset: (handleReset),\n create: (hasPermission('system:file:upload') && (<Button type=\"primary\" icon={<Plus size={14}/>} loading={uploadProgressVisible && uploadItems.some(item => item.status === 'uploading' || item.status === 'pending')} disabled={!defaultConfig} onClick={handlePickFile}>\n 上传文件\n </Button>)),\n actions: (selectedRowKeys.length > 0 ? (<>\n <Button type=\"tertiary\" theme=\"light\" icon={<FolderDown size={14}/>} loading={batchDownloadLoading} onClick={handleBatchDownload}>\n 批量下载 ({selectedRowKeys.length})\n </Button>\n {hasPermission('system:file:delete') && (<BatchDeleteButton count={selectedRowKeys.length} loading={batchDeleteMutation.isPending} onClick={handleBatchDelete}/>)}\n <Button type=\"tertiary\" theme=\"light\" icon={<X size={12}/>} onClick={() => setSelectedRowKeys([])}>\n 取消选择\n </Button>\n </>) : null),\n filterTitle: \"文件筛选\",\n actionTitle: \"文件操作\"\n });\n // Keep upload queues and search state alive when another section is shown.\n if (!active) return null;\n return (<>\n {viewMode === 'grid' && <TableSearchPanel config={fileTableSearch}/>}\n {/* 隐藏的文件选择器放在工具栏之外:桌面 / 移动端菜单会各渲染一份 actions,ref 只能指向唯一实例 */}\n <input ref={fileInputRef} type=\"file\" hidden multiple onChange={handleUpload}/>\n\n <div className=\"files-default-tip\" style={{ padding: '8px 0' }}>\n <Space>\n <Text strong>默认文件服务:</Text>\n {defaultConfig ? (<Text>{defaultConfig.name}</Text>) : (<Text type=\"danger\">未配置默认文件服务,请先前往\"文件配置\"设置。</Text>)}\n </Space>\n {canEditPreference('filesViewMode') && <Space spacing={0}>\n <Button size=\"small\" theme={viewMode === 'list' ? 'solid' : 'light'} type={viewMode === 'list' ? 'primary' : 'tertiary'} icon={<ListIcon size={14}/>} style={{ borderRadius: '4px 0 0 4px' }} onClick={() => toggleViewMode('list')}/>\n <Button size=\"small\" theme={viewMode === 'grid' ? 'solid' : 'light'} type={viewMode === 'grid' ? 'primary' : 'tertiary'} icon={<LayoutGrid size={14}/>} style={{ borderRadius: '0 4px 4px 0' }} onClick={() => toggleViewMode('grid')}/>\n </Space>}\n </div>\n\n <AppModal title=\"上传进度\" visible={uploadProgressVisible} onCancel={() => setUploadProgressVisible(false)} footer={uploadItems.every(isUploadFinished)\n ? <Button type=\"primary\" onClick={() => setUploadProgressVisible(false)}>关闭</Button>\n : null} width={480} keepDOM={false}>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 14, padding: '4px 0' }}>\n {uploadItems.map((item) => (<div key={item.uid}>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>\n <Space spacing={6} style={{ overflow: 'hidden', flex: 1, minWidth: 0 }}>\n <span style={{ display: 'flex', alignItems: 'center', flexShrink: 0 }}>\n {getFileTypeIcon(undefined, 14, item.name)}\n </span>\n <Typography.Text ellipsis={{ showTooltip: true }} style={{ fontSize: 13, flex: 1, minWidth: 0 }}>\n {item.name}\n </Typography.Text>\n </Space>\n <Space spacing={6} style={{ flexShrink: 0, marginLeft: 8 }}>\n <Typography.Text type=\"tertiary\" size=\"small\">{formatBytes(item.size)}</Typography.Text>\n {item.status === 'uploading' && (<Typography.Text size=\"small\">{item.progress}%</Typography.Text>)}\n {item.status === 'success' && (<CheckCircle2 size={14} color=\"var(--arc-antd-color-success)\"/>)}\n {item.status === 'error' && (<Tooltip content={item.errorMsg}>\n <XCircle size={14} color=\"var(--arc-antd-color-danger)\"/>\n </Tooltip>)}\n {item.status === 'cancelled' && (<Typography.Text type=\"tertiary\" size=\"small\">已取消</Typography.Text>)}\n {!isUploadFinished(item) && (<Tooltip content=\"取消上传\">\n <Button theme=\"borderless\" type=\"tertiary\" size=\"small\" icon={<X size={14}/>} aria-label={`取消上传 ${item.name}`} onClick={() => handleCancelUpload(item.uid)}/>\n </Tooltip>)}\n </Space>\n </div>\n <Progress percent={item.progress} type=\"line\" size=\"small\" stroke={getProgressStroke(item.status)} showInfo={false} style={{ margin: 0 }}/>\n </div>))}\n </div>\n </AppModal>\n\n <FilePreviewLayer preview={preview}/>\n\n <FileDetailModal visible={!!detailFile} file={displayedDetailFile} loading={detailFileLoading} resolveUrl={(file) => file.directUrl ?? getFileFullUrl(file.url)} onCopyUrl={(file) => void handleCopyUrl(file)} onClose={() => { setDetailFile(null); setImageResolution(null); }} extraRows={imageResolution ? [{ key: '分辨率', value: `${imageResolution.width} × ${imageResolution.height} px` }] : []}/>\n\n {viewMode === 'list' ? (<ConfigurableTable<ManagedFile> searchConfig={fileTableSearch} columns={columns} empty=\"暂无文件记录\" {...listTableProps(listQuery, {\n pagination: buildPagination,\n rowSelection: hasPermission('system:file:delete') ? {\n selectedRowKeys,\n onChange: (keys) => setSelectedRowKeys((keys ?? []).map(String)),\n } : undefined,\n })}/>) : (<>\n {hasPermission('system:file:delete') && (data?.list ?? []).length > 0 && (() => {\n const currentPageIds = (data?.list ?? []).map((f) => f.id);\n const selectedOnPage = currentPageIds.filter((id) => selectedRowKeys.includes(id));\n const allSelected = selectedOnPage.length === currentPageIds.length;\n const someSelected = selectedOnPage.length > 0 && !allSelected;\n return (<div className=\"files-grid-select-bar\" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <Checkbox checked={allSelected} indeterminate={someSelected} onChange={() => handleGridSelectAll(!allSelected)}>\n <span style={{ fontSize: 14, color: 'var(--arc-antd-color-text-0)' }}>\n 全选当前页\n {selectedOnPage.length > 0 && (<span style={{ marginLeft: 4, color: 'var(--arc-antd-color-text-2)', fontWeight: 400 }}>\n ({selectedOnPage.length}/{currentPageIds.length})\n </span>)}\n </span>\n </Checkbox>\n {selectedRowKeys.length > 0 && (<Button size=\"small\" theme=\"light\" type=\"tertiary\" icon={<X size={12}/>} onClick={() => setSelectedRowKeys([])}>\n 已选 {selectedRowKeys.length} 项,取消选择\n </Button>)}\n </div>);\n })()}\n <List grid={{\n gutter: [6, 8],\n xs: 8,\n sm: 6,\n md: 4,\n lg: 3,\n xl: 2,\n xxl: 2,\n }} dataSource={data?.list ?? []} loading={listQuery.isFetching} split={false} emptyContent={<div className=\"files-grid-empty\">暂无文件记录</div>} renderItem={(file) => (<List.Item key={file.id} style={{ padding: 0, height: '100%' }}>\n <FileGridCard file={file} selected={selectedRowKeys.includes(file.id)} canSelect={hasPermission('system:file:delete')} onSelect={handleGridSelect} onPreview={preview.handlePreview} onDownload={preview.handleDownload} onDelete={handleDelete} onDetail={handleOpenDetail} onCopyUrl={handleCopyUrl} canDelete={hasPermission('system:file:delete')} previewLoading={preview.previewLoadingId === file.id}/>\n </List.Item>)}/>\n {(data?.total ?? 0) > 0 && (<div className=\"files-grid-pagination\">\n <Pagination currentPage={page} pageSize={pageSize} total={data?.total ?? 0} onPageChange={(p) => { setPage(p); }} onPageSizeChange={(size) => { setPageSize(size); setPage(1); }} showSizeChanger showTotal pageSizeOpts={FILE_GRID_PAGE_SIZE_OPTIONS}/>\n </div>)}\n </>)}\n </>);\n}\n","import { CartesianChartSpecTransformer } from \"../cartesian\";\n\nimport { setDefaultCrosshairForCartesianChart } from \"../util\";\n\nexport class LineChartSpecTransformer extends CartesianChartSpecTransformer {\n _getDefaultSeriesSpec(spec) {\n var _a;\n const seriesSpec = super._getDefaultSeriesSpec(spec, [ \"point\", \"line\", \"activePoint\", \"sampling\", \"samplingFactor\", \"pointDis\", \"pointDisMul\", \"markOverlap\", \"lineLabel\" ]);\n return seriesSpec.seriesMark = null !== (_a = spec.seriesMark) && void 0 !== _a ? _a : \"line\", \n seriesSpec;\n }\n transformSpec(spec) {\n super.transformSpec(spec), setDefaultCrosshairForCartesianChart(spec);\n }\n}\n//# sourceMappingURL=line-transformer.js.map\n","import { SeriesTypeEnum } from \"../../series/interface/type\";\n\nimport { registerLineSeries } from \"../../series/line/line\";\n\nimport { Factory } from \"../../core/factory\";\n\nimport { LineChartSpecTransformer } from \"./line-transformer\";\n\nimport { BaseChart } from \"../base\";\n\nimport { StackChartMixin } from \"../stack\";\n\nimport { mixin } from \"@visactor/vutils\";\n\nimport { registerDimensionHover } from \"../../interaction/triggers/dimension-hover\";\n\nimport { registerDimensionEvents } from \"../../event/events\";\n\nimport { getCartesianDimensionInfo, getDimensionInfoByValue } from \"../../event/events/dimension/util/cartesian\";\n\nimport { getCartesianCrosshairRect } from \"../../component/crosshair/utils/cartesian\";\n\nimport { registerDimensionTooltipProcessor } from \"../../component/tooltip/processor/dimension-tooltip\";\n\nimport { registerMarkTooltipProcessor } from \"../../component/tooltip/processor/mark-tooltip\";\n\nimport { registerGroupTooltipProcessor } from \"../../component/tooltip/processor/group-tooltip\";\n\nexport class LineChart extends BaseChart {\n constructor() {\n super(...arguments), this.transformerConstructor = LineChartSpecTransformer, this.type = \"line\", \n this.seriesType = SeriesTypeEnum.line;\n }\n _setModelOption() {\n this._modelOption.getDimensionInfo = getCartesianDimensionInfo, this._modelOption.getDimensionInfoByValue = getDimensionInfoByValue, \n this._modelOption.getRectByDimensionData = getCartesianCrosshairRect;\n }\n}\n\nLineChart.type = \"line\", LineChart.seriesType = SeriesTypeEnum.line, LineChart.transformerConstructor = LineChartSpecTransformer, \nmixin(LineChart, StackChartMixin);\n\nexport const registerLineChart = () => {\n registerDimensionTooltipProcessor(), registerMarkTooltipProcessor(), registerGroupTooltipProcessor(), \n registerDimensionEvents(), registerDimensionHover(), registerLineSeries(), Factory.registerChart(LineChart.type, LineChart);\n};\n//# sourceMappingURL=line.js.map\n","import { VChart, registerLineChart, registerLabel } from \"@visactor/vchart\";\n\nimport { createChart } from \"./BaseChart\";\n\nimport { registers } from \"./registers/cartesian\";\n\nexport const LineChart = createChart(\"LineChart\", {\n type: \"line\",\n vchartConstructor: VChart\n}, [ registerLineChart, registerLabel, ...registers ]);\n//# sourceMappingURL=LineChart.js.map","import type { CSSProperties } from 'react';\nimport \"@/components/data-viz/bars.css\";\n/** 排名、占比、分布等相对数据条;仅作视觉编码,数值必须由相邻文本表达。 */\nexport interface DataBarProps {\n value: number;\n max: number;\n minPercent?: number;\n color?: string;\n fillOpacity?: number;\n track?: string;\n height?: number;\n radius?: CSSProperties['borderRadius'];\n title?: string;\n className?: string;\n style?: CSSProperties;\n}\nexport function DataBar({ value, max, minPercent = 0, color, fillOpacity, track, height = 8, radius, title, className, style, }: Readonly<DataBarProps>) {\n const ratio = Number.isFinite(value) && Number.isFinite(max) && max > 0\n ? value / max\n : 0;\n const percent = Math.min(100, Math.max(value > 0 ? minPercent : 0, ratio * 100));\n const classes = ['zx-data-bar', className].filter(Boolean).join(' ');\n const barStyle = {\n '--zx-bar-height': `${height}px`,\n '--zx-bar-scale': percent / 100,\n ...(color ? { '--zx-bar-stroke': color } : {}),\n ...(fillOpacity !== undefined ? { '--zx-bar-opacity': fillOpacity } : {}),\n ...(track ? { '--zx-bar-track': track } : {}),\n ...(radius !== undefined ? { '--zx-bar-radius': typeof radius === 'number' ? `${radius}px` : radius } : {}),\n ...style,\n } as CSSProperties;\n return (<div className={classes} style={barStyle} title={title} aria-hidden=\"true\">\n <span className=\"zx-data-bar__fill\"/>\n </div>);\n}\nexport default DataBar;\n","import React, { useMemo } from 'react';\nimport { Spin } from \"@/ui\";\nimport { BarChart, LineChart, chartOptions, makeBarSpec, makeLineSpec, useChartPalette, StatCard, StatGrid } from \"@/components/charts/index\";\nimport { FileImage, Video, Music, FileText, File } from 'lucide-react';\nimport { useFileStats } from \"@fudanda/react-core/hooks/queries/files\";\nimport { FILE_STORAGE_PROVIDER_LABELS } from '@fudanda/shared/platform';\nimport { DataBar } from \"@/components/data-viz/DataBar\";\nimport { EMPTY_PLACEHOLDER } from \"@/utils/table-columns\";\nimport { formatBytes } from '@fudanda/shared/core';\nconst PROVIDER_LABELS: Record<string, string> = FILE_STORAGE_PROVIDER_LABELS;\nconst PROVIDER_COLORS = [\n '#3b82f6', '#10b981', '#f59e0b', '#ef4444',\n '#8b5cf6', '#06b6d4', '#f97316', '#84cc16', '#ec4899',\n];\nconst FILE_TYPE_CONFIG = [\n { type: 'image', label: '图片', Icon: FileImage, color: '#3b82f6', bgColor: 'rgba(59,130,246,0.12)' },\n { type: 'video', label: '视频', Icon: Video, color: '#8b5cf6', bgColor: 'rgba(139,92,246,0.12)' },\n { type: 'audio', label: '音频', Icon: Music, color: '#f59e0b', bgColor: 'rgba(245,158,11,0.12)' },\n { type: 'document', label: '文档', Icon: FileText, color: '#10b981', bgColor: 'rgba(16,185,129,0.12)' },\n { type: 'other', label: '其他', Icon: File, color: '#6b7280', bgColor: 'rgba(107,114,128,0.12)' },\n] as const;\nconst sectionTitleStyle: React.CSSProperties = {\n fontSize: 14,\n fontWeight: 600,\n color: 'var(--arc-antd-color-text-0)',\n marginBottom: 12,\n};\nexport default function FileStatsPanel() {\n const palette = useChartPalette();\n const statsQuery = useFileStats();\n const stats = statsQuery.data ?? null;\n const summary = stats?.summary;\n const totalFiles = summary?.totalFiles ?? 0;\n const providerData = useMemo(() => (stats?.providerStats ?? []).map((p, i) => ({\n ...p,\n providerLabel: PROVIDER_LABELS[p.provider] ?? p.provider,\n fill: PROVIDER_COLORS[i % PROVIDER_COLORS.length],\n })), [stats]);\n const uploaderData = useMemo(() => (stats?.uploaderStats ?? []).map((u) => ({ ...u, sizeLabel: formatBytes(u.size) })), [stats]);\n const providerSpec = useMemo(() => makeBarSpec({\n data: providerData,\n xField: 'providerLabel',\n series: [{ field: 'count', name: '文件数', color: '#3b82f6' }],\n palette,\n horizontal: true,\n categoryAxisWidth: 80,\n colorByDatum: (d) => String(d?.fill),\n tooltip: { value: (v) => `${v} 个文件` },\n }), [palette, providerData]);\n const monthlySpec = useMemo(() => makeLineSpec({\n data: stats?.monthlyStats ?? [],\n xField: 'month',\n series: [{ field: 'count', name: '新增文件', color: '#3b82f6' }],\n palette,\n point: true,\n tooltip: { value: (v) => `${v} 个` },\n }), [palette, stats]);\n const sizeRangeSpec = useMemo(() => makeBarSpec({\n data: stats?.sizeRangeStats ?? [],\n xField: 'range',\n series: [{ field: 'count', name: '文件数', color: '#10b981' }],\n palette,\n tooltip: { value: (v) => `${v} 个` },\n }), [palette, stats]);\n const uploaderSpec = useMemo(() => makeBarSpec({\n data: uploaderData,\n xField: 'username',\n series: [{ field: 'count', name: '文件数', color: '#8b5cf6' }],\n palette,\n horizontal: true,\n categoryAxisWidth: 80,\n tooltip: { value: (v) => `${v} 个文件` },\n }), [palette, uploaderData]);\n return (<Spin spinning={statsQuery.isFetching}>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>\n\n {/* 汇总卡片 */}\n <StatGrid>\n <StatCard title=\"文件总数\" value={totalFiles > 0 ? totalFiles.toLocaleString() : EMPTY_PLACEHOLDER}/>\n <StatCard title=\"占用空间\" value={summary ? formatBytes(summary.totalSize) : EMPTY_PLACEHOLDER}/>\n <StatCard title=\"今日新增\" value={summary?.todayCount == null ? EMPTY_PLACEHOLDER : summary.todayCount.toLocaleString()} sub=\"今日共上传\"/>\n <StatCard title=\"本月新增\" value={summary?.thisMonthCount == null ? EMPTY_PLACEHOLDER : summary.thisMonthCount.toLocaleString()} sub=\"本月共上传\"/>\n </StatGrid>\n\n {/* 文件类型卡片 */}\n <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(160px, 100%), 1fr))', gap: 12 }}>\n {FILE_TYPE_CONFIG.map(({ type, label, Icon, color, bgColor }) => {\n const stat = stats?.typeStats.find(t => t.type === type);\n const count = stat?.count ?? 0;\n const size = stat?.size ?? 0;\n const percent = totalFiles > 0 ? (count / totalFiles) * 100 : 0;\n return (<div key={type} className=\"zx-panel\" style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 10 }}>\n {/* 图标 + 右侧内容 */}\n <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>\n <div style={{ width: 38, height: 38, borderRadius: 'var(--arc-antd-border-radius-large)', background: bgColor, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>\n <Icon size={19} color={color}/>\n </div>\n <div style={{ flex: 1, minWidth: 0 }}>\n {/* 类型名 + 数量同行 */}\n <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 4 }}>\n <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--arc-antd-color-text-0)' }}>{label}</span>\n <span style={{ fontSize: 18, fontWeight: 700, color: 'var(--arc-antd-color-text-0)', flexShrink: 0 }}>{count.toLocaleString()}</span>\n </div>\n <div style={{ fontSize: 11, color: 'var(--arc-antd-color-text-2)', marginTop: 3 }}>{formatBytes(size)}</div>\n </div>\n </div>\n {/* 进度条 */}\n <div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 5 }}>\n <span style={{ fontSize: 11, color: 'var(--arc-antd-color-text-2)' }}>占比</span>\n <span style={{ fontSize: 11, fontWeight: 600, color }}>{percent.toFixed(1)}%</span>\n </div>\n <DataBar value={percent} max={100} color={color} height={5}/>\n </div>\n </div>);\n })}\n </div>\n\n {/* 存储类型分布 + 月度上传趋势 */}\n <div className=\"chart-grid\">\n <div className=\"zx-panel\">\n <div style={sectionTitleStyle}>存储类型分布</div>\n <BarChart {...providerSpec} options={chartOptions} height={220}/>\n </div>\n <div className=\"zx-panel\">\n <div style={sectionTitleStyle}>月度上传趋势(近 12 个月)</div>\n <LineChart {...monthlySpec} options={chartOptions} height={220}/>\n </div>\n </div>\n\n {/* 文件大小分布 + Top 上传人 */}\n <div className=\"chart-grid\">\n <div className=\"zx-panel\">\n <div style={sectionTitleStyle}>文件大小分布</div>\n <BarChart {...sizeRangeSpec} options={chartOptions} height={220}/>\n </div>\n {stats && stats.uploaderStats.length > 0 && (<div className=\"zx-panel\">\n <div style={sectionTitleStyle}>Top 上传人(按文件数)</div>\n <BarChart {...uploaderSpec} options={chartOptions} height={220}/>\n </div>)}\n </div>\n\n </div>\n </Spin>);\n}\n","import { useMemo } from 'react';\nimport { BarChart3, Files, HardDriveUpload } from 'lucide-react';\nimport { Empty } from '@/ui';\nimport { MasterDetailLayout } from '@/components/MasterDetailLayout';\nimport { NavListPanel } from '@/components/NavListPanel';\nimport { SectionNavigation } from '@/components/SectionNavigation';\nimport { useSectionSelection } from '@/hooks/useSectionSelection';\nimport { usePermission } from '@fudanda/react-core/hooks/usePermission';\nimport FileStorageConfigsPanel from '../file-configs/FileStorageConfigsPanel';\nimport FileListPanel from './FileListPanel';\nimport FileStatsPanel from './FileStatsPanel';\n\nconst SECTIONS = [\n { key: 'config', title: '文件配置', description: '本地存储与对象存储服务', icon: HardDriveUpload },\n { key: 'list', title: '文件列表', description: '上传、查看和管理文件', icon: Files },\n { key: 'stats', title: '统计分析', description: '存储用量与文件分布', icon: BarChart3 },\n] as const;\n\nexport default function FilesPage() {\n const { hasPermission } = usePermission();\n const canConfigure = hasPermission('system:file:config');\n const canList = hasPermission('system:file:list');\n const sections = useMemo(() => SECTIONS.filter(section => section.key === 'config' ? canConfigure : canList), [canConfigure, canList]);\n const navigation = useSectionSelection(sections.map(section => section.key), canList ? 'list' : 'config');\n const activeSection = navigation.active;\n if (sections.length === 0) return <Empty description=\"无访问权限\" />;\n return <div className=\"page-container page-container--stretch\">\n <MasterDetailLayout defaultSize={260} minSize={200} maxSize={400} persistKey=\"file-management\"\n style={{ flex: 1, minHeight: 0, overflow: 'hidden' }} showDetail={activeSection !== null}\n onBack={navigation.back} onResponsiveChange={navigation.responsiveChange}\n master={<NavListPanel title=\"文件管理\" rawBody>\n <SectionNavigation sections={sections} active={activeSection} onSelect={navigation.select} />\n </NavListPanel>}\n detail={<MasterDetailLayout.Body padding={activeSection === 'config' ? 0 : 16}\n scroll={activeSection === 'stats' ? 'auto' : 'hidden'} style={{ display: 'flex', flexDirection: 'column' }}>\n {canList && <FileListPanel active={activeSection === 'list'} />}\n {canConfigure && activeSection === 'config' && <FileStorageConfigsPanel />}\n {canList && activeSection === 'stats' && <FileStatsPanel />}\n </MasterDetailLayout.Body>}\n />\n </div>;\n}\n"]}
1
+ {"version":3,"file":"FilesPage-DrgWELKy.js","mappings":"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","names":["__iconData","__iconData$8","createLucideIcon","__iconData$7","__iconData$6","__iconData$5","File","__iconData$4","__iconData$3","__iconData$2","__iconData$1","getFileIcon","lower","fileName","NAME_MAP","dot","ext","EXT_MAP","DEFAULT_FILE_ICON","getFileFullUrl","url","config","getIconIdForMime","mime","mimeType","isOfdFile","isArchiveFile","getFileTypeIcon","iconId","byName","FileTypeIcon","iconSize","canPreviewFile","resolvedMimeType","resolveFileMimeType","isEmailFile","isMindMapFile","isDrawingFile","isDataAssetFile","isGeoFile","isSpreadsheetFile","isWordFile","isPresentationFile","isMarkdownFile","isPlainTextFile","isJsonFile","isSvgFile","isCodeFile","EMAIL_MIME_TYPES","DRAWING_MIME_TYPES","DATA_ASSET_MIME_TYPES","isGalleryImageFile","GEO_MIME_TYPES","isZipFile","ARCHIVE_MIME_TYPES","fetchProtectedFile","id","extractManagedFileId","operation","filesPrivateContent","filesContent","goTransport","response","MANAGED_FILE_CONTENT_URL","fetchManagedFileBlob","fileId","__vitePreload","access","getFileAccessUrl","prefix","escapeRegExp","buildManagedFileActions","record","preview","onDetail","onCopyUrl","canDelete","confirmDelete","onDelete","revokePreviewUrl","data","PreviewModalShell","AppModal","fullscreen","viewportHeight","Suspense","Spin","FilePreviewModal","setFullscreen","useState","toggleFullscreen","useCallback","f","useThemeController","useMemo","previewKind","isImage","isMpegTsAsCode","previewQuery","useQuery","contractKey","fileContract","visible","fileUrl","blob","previewData","useEffect","onFallback","onClose","Toast","handleClose","previewTitle","Typography","Modal","PDFPreviewPanel","isPresentation","isWide","FileViewerPreviewPanel","isRawText","displayContent","MarkdownPreviewPanel","JsonPreviewPanel","MonacoPreviewPanel","createPortal","AudioPlayer","isDark","X","VideoPlayer","lazy","n","m","PreviewWatermark","lines","content","key","style","setStyle","tile","generateWatermarkTile","FilePreviewLayer","_Fragment","ImagePreview","v","watermark","FileDetailModal","Space","Button","file","loading","Descriptions","formatBytes","extraRows","row","Text","resolveUrl","formatDateTime","Text$3","useFilePreview","previewVisible","setPreviewVisible","previewSrcList","setPreviewSrcList","previewCurrentIndex","setPreviewCurrentIndex","previewLoadingId","setPreviewLoadingId","filePreview","setFilePreview","downloadLoadingId","setDownloadLoadingId","previewBlobUrlsRef","useRef","previewSessionRef","cleanupPreviewBlobs","objectUrl","error","imageFiles","getImageFiles","clickedIndex","mySession","initialUrls","clickedFile","clickedBlob","clickedUrl","createDisplayableImageUrl","i","imgFile","updated","prev","link","withViewportMenuStyle","cloneElement","viewportMenuStyle","getPopupContainer","CursorContextDropdown","rePosKey","contextKey","point","closedKeyRef","closeOnce","Dropdown","render","FileGridCard","ctxPos","setCtxPos","isPreviewable","selected","canSelect","e","onSelect","onPreview","Checkbox","previewLoading","Tooltip","onDownload","confirmAndDelete","FileNameCell","parts","highlight","name","icon","part","index","onClick","StorageFileBrowser","usePermission","viewMode","setViewMode","currentPath","setCurrentPath","usePagination","browseQuery","useStorageBrowse","browseData","deleteMutation","useDeleteFiles","historyStack","setHistoryStack","historyIndex","setHistoryIndex","canGoBack","canGoForward","detailFile","setDetailFile","detailQuery","useFileDetail","displayedDetailFile","detailFileLoading","setPage","navigateTo","path","pushHistory","next","navigateToFolder","folder","navigateToPath","goBack","newIndex","goForward","breadcrumbSegments","handleDelete","handleOpenDetail","handleCopyUrl","copyTextWithToast","listColumns","Folder","previewable","DateTimeText","createOperationColumn","hasPermission","allItems","totalCount","pagedItems","page","SideSheet","ChevronLeft","ChevronRight","Breadcrumb","segPath","idx","isLast","seg","ListIcon","LayoutGrid","ConfigurableTable","pagedFolders","r","pagedFiles","Pagination","Text$2","normalizeOptional","value","buildPayload","fileStorageConfigContract","values","PRESIGNED_EXPIRY_DEFAULT_SECONDS","provider","getStorageSummary","EMPTY_PLACEHOLDER","FileStorageConfigsPanel","useListPage","useFileStorageConfigList","formProvider","setFormProvider","formIsDefault","setFormIsDefault","browsingConfig","setBrowsingConfig","saveMutation","useSaveFileStorageConfig","statusMutation","modal","useEditModal","formSaveMutation","useFileStorageConfigDetail","isEdit","useDeleteFileStorageConfigs","setDefaultMutation","useSetDefaultFileStorageConfig","testMutation","useTestFileStorageConfig","modalTestLoading","testingConfigId","openCreate","openEdit","handleModalTest","payload","handleSetDefault","handleTestSaved","status","useStatusToggle","enabled","abortSubmit","operationColumn","useCrudOperationColumn","columns","renderEllipsis","Tag","FILE_STORAGE_PROVIDER_LABELS","isDefault","label","summary","OBJECT_ACL_PROVIDERS","colorMap","\"default\"","acl","FILE_OBJECT_ACL_LABELS","strategy","FILE_URL_STRATEGY_LABELS","dateTimeColumn","buildTableSearch","CreateButton","ExportButton","filterQuery","tableProps","EditFormSheet","PlugZap","runSubmitAction","Form","Row","Col","FILE_STORAGE_PROVIDER_OPTIONS","option","currentAcl","FILE_OBJECT_ACL_SUPPORT","Select","Switch","FormPasswordInput","Radio","IS_GO_FOUNDATION","FILE_URL_STRATEGY_OPTIONS","opt","PRESIGNED_EXPIRY_MIN_SECONDS","PRESIGNED_EXPIRY_MAX_SECONDS","Text$1","getProgressStroke","uploadSingleFile","updateItem","setItems","item","uid","updater","onDone","onFail","signal","err","chunkThreshold","chunkedUpload","percent","uploadFile","loadImageResolution","src","external","img","cleanup","resolve","FileListPanel","queryClient","useQueryClient","usePreferences","defaultSearchParams","fileInputRef","uploadItems","setUploadItems","uploadProgressVisible","setUploadProgressVisible","uploadControllersRef","useListSearch","fileKeys","preferences","listPagination","gridPagination","FILE_GRID_PAGE_SIZE","FILE_GRID_PAGE_SIZE_OPTIONS","selectedRowKeys","setSelectedRowKeys","batchDownloadLoading","setBatchDownloadLoading","imageResolution","setImageResolution","defaultConfig","useDefaultFileStorageConfig","startDate","endDate","formatDateTimeRangeValuesForApi","submittedParams","useFilterQuery","filesList","listQuery","useFileList","active","uploadFileMutation","useUploadFile","useChunkUploadThreshold","batchDeleteMutation","toggleViewMode","canEditPreference","setPreferences","mode","handleGridSelect","checked","k","handleGridSelectAll","currentPageIds","isUploadFinished","successCount","timer","invalidateAfterFilesAdded","handlePickFile","handleUpload","files","event","items","controller","handleCancelUpload","CHUNKED_UPLOAD_CANCELLED","handleBatchDelete","handleBatchDownload","filesBatchDownload","downloadBlob","size","fileTableSearch","KeywordInput","bindKeyword","FilterSelect","bind","FILE_TYPE_FILTER_OPTIONS","DateRangeFilter","handleSearch","handleReset","Plus","FolderDown","BatchDeleteButton","TableSearchPanel","CheckCircle2","XCircle","Progress","listTableProps","buildPagination","keys","selectedOnPage","allSelected","someSelected","List","p","setPageSize","UPLOAD_ACTIVE_STATUSES","CartesianChartSpecTransformer","seriesSpec","spec","setDefaultCrosshairForCartesianChart","LineChart","BaseChart","LineChartSpecTransformer","SeriesTypeEnum","getCartesianDimensionInfo","getDimensionInfoByValue","getCartesianCrosshairRect","mixin","StackChartMixin","registerDimensionTooltipProcessor","registerMarkTooltipProcessor","registerGroupTooltipProcessor","registerDimensionEvents","registerDimensionHover","registerLineSeries","Factory","createChart","VChart","registerLineChart","registerLabel","registers","DataBar","ratio","max","minPercent","classes","className","barStyle","height","color","fillOpacity","track","radius","FileStatsPanel","palette","useChartPalette","statsQuery","useFileStats","stats","totalFiles","providerData","PROVIDER_LABELS","PROVIDER_COLORS","uploaderData","u","providerSpec","makeBarSpec","d","monthlySpec","makeLineSpec","sizeRangeSpec","uploaderSpec","StatGrid","StatCard","FILE_TYPE_CONFIG","stat","type","count","bgColor","Icon","sectionTitleStyle","BarChart","chartOptions","FileImage","Video","Music","FileText","FilesPage","canConfigure","canList","sections","SECTIONS","section","navigation","useSectionSelection","activeSection","Empty","MasterDetailLayout","NavListPanel","SectionNavigation","HardDriveUpload","Files","BarChart3"],"ignoreList":[0,1,2,3,4,5,6,7,8,23,24,25],"sources":["../../../../node_modules/lucide-react/dist/esm/icons/eighth-notes.mjs","../../../../node_modules/lucide-react/dist/esm/icons/file-image.mjs","../../../../node_modules/lucide-react/dist/esm/icons/file-text.mjs","../../../../node_modules/lucide-react/dist/esm/icons/file.mjs","../../../../node_modules/lucide-react/dist/esm/icons/files.mjs","../../../../node_modules/lucide-react/dist/esm/icons/folder-down.mjs","../../../../node_modules/lucide-react/dist/esm/icons/folder.mjs","../../../../node_modules/lucide-react/dist/esm/icons/hard-drive-upload.mjs","../../../../node_modules/lucide-react/dist/esm/icons/video.mjs","../../src/utils/fileIcons.ts","../../src/utils/file-utils.tsx","../../src/utils/managed-file-actions.ts","../../src/components/FilePreviewModal/index.tsx","../../src/components/PreviewWatermark.tsx","../../src/components/FilePreviewLayer.tsx","../../src/components/FileDetailModal.tsx","../../src/hooks/useFilePreview.ts","../../src/components/CursorContextDropdown.tsx","../../src/pages/system/files/components/FileGridCard.tsx","../../src/components/FileNameCell.tsx","../../src/pages/system/file-configs/StorageFileBrowser.tsx","../../src/pages/system/file-configs/FileStorageConfigsPanel.tsx","../../src/pages/system/files/FileListPanel.tsx","../../../../node_modules/@visactor/vchart/esm/chart/line/line-transformer.js","../../../../node_modules/@visactor/vchart/esm/chart/line/line.js","../../../../node_modules/@visactor/react-vchart/esm/charts/LineChart.js","../../src/components/data-viz/DataBar.tsx","../../src/pages/system/files/FileStatsPanel.tsx","../../src/pages/system/files/FilesPage.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"eighth-notes\",\n size: 24,\n node: [\n [\"path\", { d: \"M21 16V3a1 1 0 0 0-1.28-.96l-10 2.917a1 1 0 0 0-.72.96V19\", key: \"uy6ho8\" }],\n [\"circle\", { cx: \"18\", cy: \"16\", r: \"3\", key: \"1hluhg\" }],\n [\"circle\", { cx: \"6\", cy: \"19\", r: \"3\", key: \"1kj8tv\" }]\n ],\n aliases: [\"music\"]\n};\n__iconData.node;\nconst EighthNotes = createLucideIcon(__iconData);\n\nexport { __iconData, EighthNotes as default };\n//# sourceMappingURL=eighth-notes.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"file-image\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\",\n key: \"1oefj6\"\n }\n ],\n [\"path\", { d: \"M14 2v5a1 1 0 0 0 1 1h5\", key: \"wfsgrz\" }],\n [\"circle\", { cx: \"10\", cy: \"12\", r: \"2\", key: \"737tya\" }],\n [\"path\", { d: \"m20 17-1.296-1.296a2.41 2.41 0 0 0-3.408 0L9 22\", key: \"wt3hpn\" }]\n ]\n};\n__iconData.node;\nconst FileImage = createLucideIcon(__iconData);\n\nexport { __iconData, FileImage as default };\n//# sourceMappingURL=file-image.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"file-text\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\",\n key: \"1oefj6\"\n }\n ],\n [\"path\", { d: \"M14 2v5a1 1 0 0 0 1 1h5\", key: \"wfsgrz\" }],\n [\"path\", { d: \"M10 9H8\", key: \"b1mrlr\" }],\n [\"path\", { d: \"M16 13H8\", key: \"t4e002\" }],\n [\"path\", { d: \"M16 17H8\", key: \"z1uh3a\" }]\n ]\n};\n__iconData.node;\nconst FileText = createLucideIcon(__iconData);\n\nexport { __iconData, FileText as default };\n//# sourceMappingURL=file-text.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"file\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\",\n key: \"1oefj6\"\n }\n ],\n [\"path\", { d: \"M14 2v5a1 1 0 0 0 1 1h5\", key: \"wfsgrz\" }]\n ]\n};\n__iconData.node;\nconst File = createLucideIcon(__iconData);\n\nexport { __iconData, File as default };\n//# sourceMappingURL=file.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"files\",\n size: 24,\n node: [\n [\"path\", { d: \"M15 2h-4a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V8\", key: \"14sh0y\" }],\n [\n \"path\",\n {\n d: \"M16.706 2.706A2.4 2.4 0 0 0 15 2v5a1 1 0 0 0 1 1h5a2.4 2.4 0 0 0-.706-1.706z\",\n key: \"1970lx\"\n }\n ],\n [\"path\", { d: \"M5 7a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h8a2 2 0 0 0 1.732-1\", key: \"l4dndm\" }]\n ]\n};\n__iconData.node;\nconst Files = createLucideIcon(__iconData);\n\nexport { __iconData, Files as default };\n//# sourceMappingURL=files.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"folder-down\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\",\n key: \"1kt360\"\n }\n ],\n [\"path\", { d: \"M12 10v6\", key: \"1bos4e\" }],\n [\"path\", { d: \"m15 13-3 3-3-3\", key: \"6j2sf0\" }]\n ]\n};\n__iconData.node;\nconst FolderDown = createLucideIcon(__iconData);\n\nexport { __iconData, FolderDown as default };\n//# sourceMappingURL=folder-down.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"folder\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z\",\n key: \"1kt360\"\n }\n ]\n ]\n};\n__iconData.node;\nconst Folder = createLucideIcon(__iconData);\n\nexport { __iconData, Folder as default };\n//# sourceMappingURL=folder.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"hard-drive-upload\",\n size: 24,\n node: [\n [\"path\", { d: \"m16 6-4-4-4 4\", key: \"13yo43\" }],\n [\"path\", { d: \"M12 2v8\", key: \"1q4o3n\" }],\n [\"rect\", { width: \"20\", height: \"8\", x: \"2\", y: \"14\", rx: \"2\", key: \"w68u3i\" }],\n [\"path\", { d: \"M6 18h.01\", key: \"uhywen\" }],\n [\"path\", { d: \"M10 18h.01\", key: \"h775k\" }]\n ]\n};\n__iconData.node;\nconst HardDriveUpload = createLucideIcon(__iconData);\n\nexport { __iconData, HardDriveUpload as default };\n//# sourceMappingURL=hard-drive-upload.mjs.map\n","/**\n * @license lucide-react v1.67.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"video\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5\",\n key: \"ftymec\"\n }\n ],\n [\"rect\", { x: \"2\", y: \"6\", width: \"14\", height: \"12\", rx: \"2\", key: \"158x01\" }]\n ]\n};\n__iconData.node;\nconst Video = createLucideIcon(__iconData);\n\nexport { __iconData, Video as default };\n//# sourceMappingURL=video.mjs.map\n","/**\n * 文件 / 文件夹图标映射表\n *\n * 使用 Iconify vscode-icons 图标集的图标 id,视觉风格与 VS Code material-icon-theme 高度一致。\n * 图标本体不再由运行时从公网 Iconify API 拉取:`scripts/gen-iconify-assets.mjs` 在构建期扫描这里的字面量,\n * 从 @iconify-json/vscode-icons 抽成 `assets/file-icons/*.svg`(文件夹图标连同 `-opened` 展开态),\n * 由 `components/FileTypeIcon` 按需渲染;新增映射后运行 `npm run icons:iconify -w @fudanda/web`。\n */\n/** 文件扩展名 → iconify 图标 ID */\nconst EXT_MAP: Readonly<Record<string, string>> = {\n // TypeScript\n ts: 'vscode-icons:file-type-typescript',\n tsx: 'vscode-icons:file-type-typescript',\n // JavaScript\n js: 'vscode-icons:file-type-js-official',\n jsx: 'vscode-icons:file-type-js-official',\n mjs: 'vscode-icons:file-type-js-official',\n cjs: 'vscode-icons:file-type-js-official',\n // JSON\n json: 'vscode-icons:file-type-json',\n jsonc: 'vscode-icons:file-type-json',\n json5: 'vscode-icons:file-type-json',\n // Markdown\n md: 'vscode-icons:file-type-markdown',\n mdx: 'vscode-icons:file-type-markdown',\n markdown: 'vscode-icons:file-type-markdown',\n // HTML\n html: 'vscode-icons:file-type-html',\n htm: 'vscode-icons:file-type-html',\n xhtml: 'vscode-icons:file-type-html',\n // CSS\n css: 'vscode-icons:file-type-css',\n scss: 'vscode-icons:file-type-scss',\n sass: 'vscode-icons:file-type-sass',\n less: 'vscode-icons:file-type-less',\n styl: 'vscode-icons:file-type-stylus',\n stylus: 'vscode-icons:file-type-stylus',\n // Python\n py: 'vscode-icons:file-type-python',\n pyi: 'vscode-icons:file-type-python',\n // Go\n go: 'vscode-icons:file-type-go',\n // Rust\n rs: 'vscode-icons:file-type-rust',\n // Java\n java: 'vscode-icons:file-type-java',\n // C / C++\n c: 'vscode-icons:file-type-c',\n h: 'vscode-icons:file-type-c',\n cpp: 'vscode-icons:file-type-cpp',\n cc: 'vscode-icons:file-type-cpp',\n cxx: 'vscode-icons:file-type-cpp',\n hpp: 'vscode-icons:file-type-cpp',\n hxx: 'vscode-icons:file-type-cpp',\n // C#\n cs: 'vscode-icons:file-type-csharp',\n // PHP\n php: 'vscode-icons:file-type-php',\n // Ruby\n rb: 'vscode-icons:file-type-ruby',\n // Swift\n swift: 'vscode-icons:file-type-swift',\n // Kotlin\n kt: 'vscode-icons:file-type-kotlin',\n kts: 'vscode-icons:file-type-kotlin',\n // Dart\n dart: 'vscode-icons:file-type-dartlang',\n // Scala\n scala: 'vscode-icons:file-type-scala',\n // Lua\n lua: 'vscode-icons:file-type-lua',\n // R\n r: 'vscode-icons:file-type-r',\n // YAML\n yml: 'vscode-icons:file-type-yaml',\n yaml: 'vscode-icons:file-type-yaml',\n // XML\n xml: 'vscode-icons:file-type-xml',\n xsl: 'vscode-icons:file-type-xml',\n xslt: 'vscode-icons:file-type-xml',\n // SQL\n sql: 'vscode-icons:file-type-sql',\n // Shell\n sh: 'vscode-icons:file-type-shell',\n bash: 'vscode-icons:file-type-shell',\n zsh: 'vscode-icons:file-type-shell',\n fish: 'vscode-icons:file-type-shell',\n // PowerShell\n ps1: 'vscode-icons:file-type-powershell',\n psm1: 'vscode-icons:file-type-powershell',\n // Batch\n bat: 'vscode-icons:file-type-bat',\n cmd: 'vscode-icons:file-type-bat',\n // Config / env\n env: 'vscode-icons:file-type-dotenv',\n ini: 'vscode-icons:file-type-config',\n conf: 'vscode-icons:file-type-config',\n cfg: 'vscode-icons:file-type-config',\n toml: 'vscode-icons:file-type-toml',\n // Text\n txt: 'vscode-icons:file-type-text',\n log: 'vscode-icons:file-type-log',\n // Lock\n lock: 'vscode-icons:default-file',\n // Images\n png: 'vscode-icons:file-type-image',\n jpg: 'vscode-icons:file-type-image',\n jpeg: 'vscode-icons:file-type-image',\n gif: 'vscode-icons:file-type-image',\n bmp: 'vscode-icons:file-type-image',\n webp: 'vscode-icons:file-type-image',\n ico: 'vscode-icons:file-type-image',\n tiff: 'vscode-icons:file-type-image',\n svg: 'vscode-icons:file-type-svg',\n // Video\n mp4: 'vscode-icons:file-type-video',\n mov: 'vscode-icons:file-type-video',\n avi: 'vscode-icons:file-type-video',\n mkv: 'vscode-icons:file-type-video',\n webm: 'vscode-icons:file-type-video',\n // Audio\n mp3: 'vscode-icons:file-type-audio',\n wav: 'vscode-icons:file-type-audio',\n ogg: 'vscode-icons:file-type-audio',\n flac: 'vscode-icons:file-type-audio',\n // Archive\n zip: 'vscode-icons:file-type-zip',\n tar: 'vscode-icons:file-type-zip',\n gz: 'vscode-icons:file-type-zip',\n rar: 'vscode-icons:file-type-zip',\n '7z': 'vscode-icons:file-type-zip',\n // PDF\n pdf: 'vscode-icons:file-type-pdf2',\n // Vue / Svelte\n vue: 'vscode-icons:file-type-vue',\n svelte: 'vscode-icons:file-type-svelte',\n // GraphQL\n graphql: 'vscode-icons:file-type-graphql',\n gql: 'vscode-icons:file-type-graphql',\n // Prisma\n prisma: 'vscode-icons:file-type-prisma',\n // Protobuf\n proto: 'vscode-icons:file-type-protobuf',\n // WASM\n wasm: 'vscode-icons:file-type-wasm',\n // Certificate / Key\n pem: 'vscode-icons:file-type-cert',\n crt: 'vscode-icons:file-type-cert',\n cer: 'vscode-icons:file-type-cert',\n key: 'vscode-icons:file-type-key',\n // Binary\n exe: 'vscode-icons:file-type-binary',\n dll: 'vscode-icons:file-type-binary',\n so: 'vscode-icons:file-type-binary',\n dylib: 'vscode-icons:file-type-binary',\n // 字体\n woff: 'vscode-icons:file-type-font',\n woff2: 'vscode-icons:file-type-font',\n ttf: 'vscode-icons:file-type-font',\n eot: 'vscode-icons:file-type-font',\n otf: 'vscode-icons:file-type-font',\n // Jupyter\n ipynb: 'vscode-icons:file-type-jupyter',\n // 数据库\n db: 'vscode-icons:file-type-db',\n sqlite: 'vscode-icons:file-type-db',\n sqlite3: 'vscode-icons:file-type-db',\n // Office 文档\n doc: 'vscode-icons:file-type-word',\n docx: 'vscode-icons:file-type-word',\n xls: 'vscode-icons:file-type-excel',\n xlsx: 'vscode-icons:file-type-excel',\n csv: 'vscode-icons:file-type-excel2',\n tsv: 'vscode-icons:file-type-excel2',\n ppt: 'vscode-icons:file-type-powerpoint',\n pptx: 'vscode-icons:file-type-powerpoint',\n // 模板语言\n erb: 'vscode-icons:file-type-erb',\n haml: 'vscode-icons:file-type-haml',\n slim: 'vscode-icons:file-type-slim',\n jinja: 'vscode-icons:file-type-jinja',\n j2: 'vscode-icons:file-type-jinja',\n twig: 'vscode-icons:file-type-twig',\n astro: 'vscode-icons:file-type-astro',\n // 函数式语言\n ex: 'vscode-icons:file-type-elixir',\n exs: 'vscode-icons:file-type-elixir',\n hs: 'vscode-icons:file-type-haskell',\n lhs: 'vscode-icons:file-type-haskell',\n erl: 'vscode-icons:file-type-erlang',\n hrl: 'vscode-icons:file-type-erlang',\n clj: 'vscode-icons:file-type-clojure',\n cljs: 'vscode-icons:file-type-clojure',\n cljc: 'vscode-icons:file-type-clojure',\n edn: 'vscode-icons:file-type-clojure',\n fs: 'vscode-icons:file-type-fsharp',\n fsi: 'vscode-icons:file-type-fsharp',\n fsx: 'vscode-icons:file-type-fsharp',\n ml: 'vscode-icons:file-type-ocaml',\n mli: 'vscode-icons:file-type-ocaml',\n // 新兴语言\n nim: 'vscode-icons:file-type-nim',\n zig: 'vscode-icons:file-type-zig',\n cr: 'vscode-icons:file-type-crystal',\n d: 'vscode-icons:file-type-dlang',\n coffee: 'vscode-icons:file-type-coffeescript',\n // Terraform / HCL\n tf: 'vscode-icons:file-type-terraform',\n tfvars: 'vscode-icons:file-type-terraform',\n hcl: 'vscode-icons:file-type-terraform',\n // Nix\n nix: 'vscode-icons:file-type-nix',\n // JVM 编译产物\n jar: 'vscode-icons:file-type-java',\n class: 'vscode-icons:file-type-java',\n};\n/** 特殊文件名(完整,小写)→ iconify 图标 ID */\nconst NAME_MAP: Readonly<Record<string, string>> = {\n dockerfile: 'vscode-icons:file-type-docker',\n 'docker-compose.yml': 'vscode-icons:file-type-docker',\n 'docker-compose.yaml': 'vscode-icons:file-type-docker',\n 'docker-compose.override.yml': 'vscode-icons:file-type-docker',\n makefile: 'vscode-icons:file-type-makefile',\n '.gitignore': 'vscode-icons:file-type-git',\n '.gitattributes': 'vscode-icons:file-type-git',\n '.gitmodules': 'vscode-icons:file-type-git',\n '.env': 'vscode-icons:file-type-dotenv',\n '.env.local': 'vscode-icons:file-type-dotenv',\n '.env.example': 'vscode-icons:file-type-dotenv',\n '.env.development': 'vscode-icons:file-type-dotenv',\n '.env.production': 'vscode-icons:file-type-dotenv',\n '.env.test': 'vscode-icons:file-type-dotenv',\n 'package.json': 'vscode-icons:file-type-npm',\n 'package-lock.json': 'vscode-icons:file-type-npm',\n 'yarn.lock': 'vscode-icons:file-type-yarn',\n '.yarnrc': 'vscode-icons:file-type-yarn',\n '.yarnrc.yml': 'vscode-icons:file-type-yarn',\n 'pnpm-lock.yaml': 'vscode-icons:file-type-pnpm',\n '.pnpmfile.cjs': 'vscode-icons:file-type-pnpm',\n 'bun.lockb': 'vscode-icons:file-type-bun',\n 'bunfig.toml': 'vscode-icons:file-type-bun',\n 'tsconfig.json': 'vscode-icons:file-type-tsconfig',\n 'tsconfig.node.json': 'vscode-icons:file-type-tsconfig',\n 'tsconfig.base.json': 'vscode-icons:file-type-tsconfig',\n 'jsconfig.json': 'vscode-icons:file-type-jsconfig',\n 'vite.config.ts': 'vscode-icons:file-type-vite',\n 'vite.config.js': 'vscode-icons:file-type-vite',\n 'vite.config.mts': 'vscode-icons:file-type-vite',\n 'vite.config.mjs': 'vscode-icons:file-type-vite',\n 'vitest.config.ts': 'vscode-icons:file-type-vitest',\n 'vitest.config.js': 'vscode-icons:file-type-vitest',\n 'webpack.config.js': 'vscode-icons:file-type-webpack',\n 'webpack.config.ts': 'vscode-icons:file-type-webpack',\n 'rollup.config.js': 'vscode-icons:file-type-rollup',\n 'rollup.config.ts': 'vscode-icons:file-type-rollup',\n 'eslint.config.js': 'vscode-icons:file-type-eslint',\n 'eslint.config.ts': 'vscode-icons:file-type-eslint',\n 'eslint.config.mjs': 'vscode-icons:file-type-eslint',\n '.eslintrc': 'vscode-icons:file-type-eslint',\n '.eslintrc.js': 'vscode-icons:file-type-eslint',\n '.eslintrc.json': 'vscode-icons:file-type-eslint',\n '.eslintrc.yml': 'vscode-icons:file-type-eslint',\n '.eslintignore': 'vscode-icons:file-type-eslint',\n '.prettierrc': 'vscode-icons:file-type-prettier',\n '.prettierrc.js': 'vscode-icons:file-type-prettier',\n '.prettierrc.json': 'vscode-icons:file-type-prettier',\n '.prettierrc.yml': 'vscode-icons:file-type-prettier',\n '.prettierignore': 'vscode-icons:file-type-prettier',\n 'readme.md': 'vscode-icons:file-type-markdown',\n readme: 'vscode-icons:file-type-markdown',\n license: 'vscode-icons:file-type-license',\n 'license.md': 'vscode-icons:file-type-license',\n licence: 'vscode-icons:file-type-license',\n 'changelog.md': 'vscode-icons:file-type-log',\n changelog: 'vscode-icons:file-type-log',\n '.editorconfig': 'vscode-icons:file-type-editorconfig',\n '.nvmrc': 'vscode-icons:file-type-node',\n '.node-version': 'vscode-icons:file-type-node',\n 'jest.config.js': 'vscode-icons:file-type-jest',\n 'jest.config.ts': 'vscode-icons:file-type-jest',\n 'babel.config.js': 'vscode-icons:file-type-babel',\n 'babel.config.json': 'vscode-icons:file-type-babel',\n '.babelrc': 'vscode-icons:file-type-babel',\n 'tailwind.config.js': 'vscode-icons:file-type-tailwind',\n 'tailwind.config.ts': 'vscode-icons:file-type-tailwind',\n 'postcss.config.js': 'vscode-icons:file-type-postcss',\n 'next.config.js': 'vscode-icons:file-type-next',\n 'next.config.ts': 'vscode-icons:file-type-next',\n 'nuxt.config.ts': 'vscode-icons:file-type-nuxt',\n 'nuxt.config.js': 'vscode-icons:file-type-nuxt',\n 'astro.config.mjs': 'vscode-icons:file-type-astro',\n 'drizzle.config.ts': 'vscode-icons:file-type-drizzle-orm',\n 'drizzle.config.js': 'vscode-icons:file-type-drizzle-orm',\n // Python 项目文件\n 'requirements.txt': 'vscode-icons:file-type-python',\n 'requirements-dev.txt': 'vscode-icons:file-type-python',\n 'setup.py': 'vscode-icons:file-type-python',\n 'setup.cfg': 'vscode-icons:file-type-python',\n 'pyproject.toml': 'vscode-icons:file-type-python',\n pipfile: 'vscode-icons:file-type-python',\n 'pipfile.lock': 'vscode-icons:file-type-python',\n 'tox.ini': 'vscode-icons:file-type-python',\n '.python-version': 'vscode-icons:file-type-python',\n 'conda.yml': 'vscode-icons:file-type-python',\n 'environment.yml': 'vscode-icons:file-type-python',\n // Rust\n 'cargo.toml': 'vscode-icons:file-type-rust',\n 'cargo.lock': 'vscode-icons:file-type-rust',\n 'rust-toolchain': 'vscode-icons:file-type-rust',\n 'rust-toolchain.toml': 'vscode-icons:file-type-rust',\n // Go\n 'go.mod': 'vscode-icons:file-type-go',\n 'go.sum': 'vscode-icons:file-type-go',\n 'go.work': 'vscode-icons:file-type-go',\n // Java / Maven / Gradle\n 'pom.xml': 'vscode-icons:file-type-maven',\n 'build.gradle': 'vscode-icons:file-type-gradle',\n 'build.gradle.kts': 'vscode-icons:file-type-gradle',\n 'settings.gradle': 'vscode-icons:file-type-gradle',\n 'settings.gradle.kts': 'vscode-icons:file-type-gradle',\n 'gradle.properties': 'vscode-icons:file-type-gradle',\n gradlew: 'vscode-icons:file-type-gradle',\n // Ruby\n gemfile: 'vscode-icons:file-type-ruby',\n 'gemfile.lock': 'vscode-icons:file-type-ruby',\n rakefile: 'vscode-icons:file-type-ruby',\n '.ruby-version': 'vscode-icons:file-type-ruby',\n // PHP\n 'composer.json': 'vscode-icons:file-type-composer',\n 'composer.lock': 'vscode-icons:file-type-composer',\n '.htaccess': 'vscode-icons:file-type-apache',\n // npm / Node\n '.npmrc': 'vscode-icons:file-type-npm',\n '.npmignore': 'vscode-icons:file-type-npm',\n // 代码规范\n 'commitlint.config.js': 'vscode-icons:file-type-commitlint',\n 'commitlint.config.ts': 'vscode-icons:file-type-commitlint',\n 'commitlint.config.cjs': 'vscode-icons:file-type-commitlint',\n '.commitlintrc': 'vscode-icons:file-type-commitlint',\n '.commitlintrc.json': 'vscode-icons:file-type-commitlint',\n '.czrc': 'vscode-icons:file-type-commitlint',\n '.cz.json': 'vscode-icons:file-type-commitlint',\n '.stylelintrc': 'vscode-icons:file-type-stylelint',\n '.stylelintrc.json': 'vscode-icons:file-type-stylelint',\n '.stylelintrc.yml': 'vscode-icons:file-type-stylelint',\n '.stylelintignore': 'vscode-icons:file-type-stylelint',\n 'stylelint.config.js': 'vscode-icons:file-type-stylelint',\n // Monorepo / 工具类\n 'nx.json': 'vscode-icons:file-type-nx',\n 'turbo.json': 'vscode-icons:file-type-turbo',\n 'lerna.json': 'vscode-icons:file-type-lerna',\n 'rush.json': 'vscode-icons:file-type-npm',\n // CI / 依赖更新\n 'renovate.json': 'vscode-icons:file-type-renovate',\n '.renovaterc': 'vscode-icons:file-type-renovate',\n '.renovaterc.json': 'vscode-icons:file-type-renovate',\n '.travis.yml': 'vscode-icons:file-type-travis',\n 'codecov.yml': 'vscode-icons:file-type-yaml',\n // Web manifest\n 'robots.txt': 'vscode-icons:file-type-robots',\n 'manifest.json': 'vscode-icons:file-type-manifest',\n '.webmanifest': 'vscode-icons:file-type-manifest',\n // 部署 / Hosting\n 'firebase.json': 'vscode-icons:file-type-firebase',\n '.firebaserc': 'vscode-icons:file-type-firebase',\n 'vercel.json': 'vscode-icons:file-type-vercel',\n 'netlify.toml': 'vscode-icons:file-type-netlify',\n 'fly.toml': 'vscode-icons:file-type-toml',\n // Capacitor / Electron\n 'capacitor.config.ts': 'vscode-icons:file-type-capacitor',\n 'capacitor.config.json': 'vscode-icons:file-type-capacitor',\n 'electron-builder.config.js': 'vscode-icons:file-type-electron',\n 'electron-builder.yml': 'vscode-icons:file-type-electron',\n // Terraform\n 'main.tf': 'vscode-icons:file-type-terraform',\n 'variables.tf': 'vscode-icons:file-type-terraform',\n 'outputs.tf': 'vscode-icons:file-type-terraform',\n 'terraform.tfvars': 'vscode-icons:file-type-terraform',\n};\n/** 文件夹名(小写)→ iconify 图标 ID 基础值(不含 -open 后缀) */\nconst FOLDER_MAP: Readonly<Record<string, string>> = {\n src: 'vscode-icons:folder-type-src',\n source: 'vscode-icons:folder-type-src',\n node_modules: 'vscode-icons:folder-type-node',\n public: 'vscode-icons:folder-type-public',\n dist: 'vscode-icons:folder-type-dist',\n build: 'vscode-icons:folder-type-dist',\n out: 'vscode-icons:folder-type-dist',\n output: 'vscode-icons:folder-type-dist',\n '.git': 'vscode-icons:folder-type-git',\n '.github': 'vscode-icons:folder-type-github',\n components: 'vscode-icons:folder-type-component',\n pages: 'vscode-icons:folder-type-view',\n views: 'vscode-icons:folder-type-view',\n screens: 'vscode-icons:folder-type-view',\n hooks: 'vscode-icons:folder-type-hook',\n utils: 'vscode-icons:folder-type-tools',\n helpers: 'vscode-icons:folder-type-helper',\n styles: 'vscode-icons:folder-type-style',\n css: 'vscode-icons:folder-type-style',\n assets: 'vscode-icons:folder-type-asset',\n images: 'vscode-icons:folder-type-images',\n img: 'vscode-icons:folder-type-images',\n icons: 'vscode-icons:folder-type-images',\n fonts: 'vscode-icons:folder-type-fonts',\n tests: 'vscode-icons:folder-type-test',\n test: 'vscode-icons:folder-type-test',\n __tests__: 'vscode-icons:folder-type-test',\n spec: 'vscode-icons:folder-type-test',\n __mocks__: 'vscode-icons:folder-type-mock',\n mocks: 'vscode-icons:folder-type-mock',\n mock: 'vscode-icons:folder-type-mock',\n types: 'vscode-icons:folder-type-typings',\n typings: 'vscode-icons:folder-type-typings',\n interfaces: 'vscode-icons:folder-type-typings',\n api: 'vscode-icons:folder-type-api',\n routes: 'vscode-icons:folder-type-route',\n middleware: 'vscode-icons:folder-type-middleware',\n services: 'vscode-icons:folder-type-services',\n config: 'vscode-icons:folder-type-config',\n configs: 'vscode-icons:folder-type-config',\n docs: 'vscode-icons:folder-type-docs',\n doc: 'vscode-icons:folder-type-docs',\n documentation: 'vscode-icons:folder-type-docs',\n scripts: 'vscode-icons:folder-type-script',\n script: 'vscode-icons:folder-type-script',\n lib: 'vscode-icons:folder-type-library',\n library: 'vscode-icons:folder-type-library',\n packages: 'vscode-icons:folder-type-package',\n logs: 'vscode-icons:folder-type-log',\n log: 'vscode-icons:folder-type-log',\n cache: 'vscode-icons:folder-type-temp',\n tmp: 'vscode-icons:folder-type-temp',\n temp: 'vscode-icons:folder-type-temp',\n models: 'vscode-icons:folder-type-model',\n model: 'vscode-icons:folder-type-model',\n db: 'vscode-icons:folder-type-db',\n database: 'vscode-icons:folder-type-db',\n docker: 'vscode-icons:folder-type-docker',\n '.docker': 'vscode-icons:folder-type-docker',\n '.vscode': 'vscode-icons:folder-type-vscode',\n '.idea': 'vscode-icons:folder-type-idea',\n store: 'vscode-icons:default-folder',\n redux: 'vscode-icons:folder-type-redux',\n context: 'vscode-icons:default-folder',\n providers: 'vscode-icons:default-folder',\n layouts: 'vscode-icons:folder-type-view',\n layout: 'vscode-icons:folder-type-view',\n drizzle: 'vscode-icons:folder-type-db',\n migrations: 'vscode-icons:folder-type-db',\n storage: 'vscode-icons:default-folder',\n // NestJS 各层\n controllers: 'vscode-icons:folder-type-route',\n controller: 'vscode-icons:folder-type-route',\n guards: 'vscode-icons:default-folder',\n guard: 'vscode-icons:default-folder',\n filters: 'vscode-icons:folder-type-middleware',\n filter: 'vscode-icons:folder-type-middleware',\n interceptors: 'vscode-icons:folder-type-middleware',\n interceptor: 'vscode-icons:folder-type-middleware',\n pipes: 'vscode-icons:folder-type-middleware',\n pipe: 'vscode-icons:folder-type-middleware',\n decorators: 'vscode-icons:folder-type-tools',\n decorator: 'vscode-icons:folder-type-tools',\n validators: 'vscode-icons:folder-type-tools',\n validator: 'vscode-icons:folder-type-tools',\n exceptions: 'vscode-icons:folder-type-tools',\n exception: 'vscode-icons:folder-type-tools',\n modules: 'vscode-icons:default-folder',\n module: 'vscode-icons:default-folder',\n // ORM 层\n entities: 'vscode-icons:folder-type-db',\n entity: 'vscode-icons:folder-type-db',\n repositories: 'vscode-icons:folder-type-db',\n repository: 'vscode-icons:folder-type-db',\n // Redux / 状态管理\n actions: 'vscode-icons:folder-type-redux',\n action: 'vscode-icons:folder-type-redux',\n reducers: 'vscode-icons:folder-type-redux',\n reducer: 'vscode-icons:folder-type-redux',\n selectors: 'vscode-icons:folder-type-redux',\n selector: 'vscode-icons:folder-type-redux',\n sagas: 'vscode-icons:folder-type-redux',\n saga: 'vscode-icons:folder-type-redux',\n effects: 'vscode-icons:folder-type-redux',\n effect: 'vscode-icons:folder-type-redux',\n epics: 'vscode-icons:folder-type-redux',\n // 异步任务\n workers: 'vscode-icons:default-folder',\n worker: 'vscode-icons:default-folder',\n jobs: 'vscode-icons:default-folder',\n job: 'vscode-icons:default-folder',\n tasks: 'vscode-icons:default-folder',\n task: 'vscode-icons:default-folder',\n queues: 'vscode-icons:default-folder',\n queue: 'vscode-icons:default-folder',\n // 事件\n events: 'vscode-icons:default-folder',\n event: 'vscode-icons:default-folder',\n listeners: 'vscode-icons:default-folder',\n listener: 'vscode-icons:default-folder',\n subscribers: 'vscode-icons:default-folder',\n subscriber: 'vscode-icons:default-folder',\n handlers: 'vscode-icons:default-folder',\n handler: 'vscode-icons:default-folder',\n // 国际化\n locales: 'vscode-icons:folder-type-locale',\n locale: 'vscode-icons:folder-type-locale',\n i18n: 'vscode-icons:folder-type-locale',\n intl: 'vscode-icons:folder-type-locale',\n translations: 'vscode-icons:folder-type-locale',\n translation: 'vscode-icons:folder-type-locale',\n l10n: 'vscode-icons:folder-type-locale',\n lang: 'vscode-icons:folder-type-locale',\n // 应用架构\n features: 'vscode-icons:default-folder',\n feature: 'vscode-icons:default-folder',\n server: 'vscode-icons:folder-type-server',\n client: 'vscode-icons:folder-type-client',\n shared: 'vscode-icons:folder-type-shared',\n common: 'vscode-icons:folder-type-common',\n core: 'vscode-icons:default-folder',\n app: 'vscode-icons:folder-type-app',\n // 插件 / 主题\n plugins: 'vscode-icons:folder-type-plugin',\n plugin: 'vscode-icons:folder-type-plugin',\n theme: 'vscode-icons:folder-type-theme',\n themes: 'vscode-icons:folder-type-theme',\n // CI / 基础设施\n '.circleci': 'vscode-icons:default-folder',\n '.gitlab': 'vscode-icons:default-folder',\n workflows: 'vscode-icons:default-folder',\n devops: 'vscode-icons:default-folder',\n terraform: 'vscode-icons:default-folder',\n infra: 'vscode-icons:default-folder',\n infrastructure: 'vscode-icons:default-folder',\n k8s: 'vscode-icons:folder-type-kubernetes',\n kubernetes: 'vscode-icons:folder-type-kubernetes',\n // 测试覆盖率 / 供应商\n coverage: 'vscode-icons:folder-type-coverage',\n vendor: 'vscode-icons:folder-type-library',\n // 工具类\n bin: 'vscode-icons:folder-type-binary',\n tools: 'vscode-icons:folder-type-tools',\n tooling: 'vscode-icons:folder-type-tools',\n patches: 'vscode-icons:default-folder',\n // 示例 / 演示\n sandbox: 'vscode-icons:default-folder',\n demo: 'vscode-icons:default-folder',\n demos: 'vscode-icons:default-folder',\n examples: 'vscode-icons:default-folder',\n example: 'vscode-icons:default-folder',\n playground: 'vscode-icons:default-folder',\n // 数据 / 内容\n data: 'vscode-icons:folder-type-asset',\n content: 'vscode-icons:folder-type-asset',\n // 上传 / 媒体\n uploads: 'vscode-icons:default-folder',\n upload: 'vscode-icons:default-folder',\n media: 'vscode-icons:default-folder',\n // 导出\n reports: 'vscode-icons:folder-type-dist',\n exports: 'vscode-icons:folder-type-dist',\n};\nconst DEFAULT_FILE_ICON = 'vscode-icons:default-file';\nconst DEFAULT_FOLDER_ICON = 'vscode-icons:default-folder';\nconst DEFAULT_FOLDER_OPEN_ICON = 'vscode-icons:default-folder-opened';\n/**\n * 根据文件名获取 Iconify 图标 ID。\n * 优先完整文件名匹配(大小写不敏感),再匹配扩展名,兜底通用文件图标。\n */\nexport function getFileIcon(fileName: string): string {\n const lower = fileName.toLowerCase();\n if (NAME_MAP[lower])\n return NAME_MAP[lower];\n const dot = lower.lastIndexOf('.');\n const ext = dot >= 0 ? lower.slice(dot + 1) : '';\n return EXT_MAP[ext] ?? DEFAULT_FILE_ICON;\n}\n/**\n * 根据文件夹名获取 Iconify 图标 ID。\n * isOpen 为 true 时返回展开状态图标(vscode-icons 集合的展开态为 -opened 后缀)。\n */\nexport function getFolderIcon(folderName: string, isOpen = false): string {\n const lower = folderName.toLowerCase();\n const base = FOLDER_MAP[lower];\n if (base) {\n return isOpen ? `${base}-opened` : base;\n }\n return isOpen ? DEFAULT_FOLDER_OPEN_ICON : DEFAULT_FOLDER_ICON;\n}\n/** Shell 类型 ID → iconify 图标 ID(用于终端标签,与 listShells() 返回的 id 对齐) */\nconst SHELL_ICON_MAP: Readonly<Record<string, string>> = {\n powershell: 'vscode-icons:file-type-powershell',\n cmd: 'codicon:terminal-cmd',\n bash: 'vscode-icons:file-type-shell',\n zsh: 'vscode-icons:file-type-shell',\n fish: 'vscode-icons:file-type-shell',\n sh: 'vscode-icons:file-type-shell',\n};\nconst DEFAULT_SHELL_ICON = 'vscode-icons:file-type-shell';\n/**\n * 根据 shell 类型 ID 获取 Iconify 图标 ID。\n * 未知类型兜底为通用 shell 图标。\n */\nexport function getShellIcon(shellId: string | undefined): string {\n if (!shellId)\n return DEFAULT_SHELL_ICON;\n return SHELL_ICON_MAP[shellId.toLowerCase()] ?? DEFAULT_SHELL_ICON;\n}\n","import { FileTypeIcon } from \"@/components/FileTypeIcon\";\nimport { escapeRegExp } from '@fudanda/shared/core';\nimport { filesContent, filesPrivateContent } from '@fudanda/shared/api/file_content';\nimport { goTransport } from '@fudanda/react-core/lib/go-transport';\nimport { config } from \"@fudanda/react-core/config\";\nimport { getFileIcon } from \"@/utils/fileIcons\";\nimport { resolveFileMimeType } from \"@fudanda/react-core/utils/file-mime\";\nexport { guessMimeTypeFromName, resolveFileMimeType } from \"@fudanda/react-core/utils/file-mime\";\n/** 将相对路径拼接为完整可访问 URL */\nexport function getFileFullUrl(url: string): string {\n if (/^https?:\\/\\//.test(url))\n return url;\n const base = config.apiBaseUrl || globalThis.location.origin;\n return `${base}${url}`;\n}\n/** MIME 类型 → vscode-icons 图标 ID(无法识别时返回 null,由调用方兜底) */\nfunction getIconIdForMime(mimeType: string): string | null {\n const mime = mimeType.toLowerCase();\n if (mime === 'image/svg+xml')\n return 'vscode-icons:file-type-svg';\n if (mime.startsWith('image/'))\n return 'vscode-icons:file-type-image';\n if (mime.startsWith('video/'))\n return 'vscode-icons:file-type-video';\n if (mime.startsWith('audio/'))\n return 'vscode-icons:file-type-audio';\n if (mime === 'application/pdf')\n return 'vscode-icons:file-type-pdf2';\n if (isOfdFile(mime))\n return 'vscode-icons:file-type-pdf2';\n if (mime.includes('msword') || mime.includes('wordprocessingml') ||\n mime.includes('vnd.ms-word') || mime.includes('opendocument.text') || mime.includes('rtf'))\n return 'vscode-icons:file-type-word';\n if (mime.includes('presentationml') || mime.includes('powerpoint') ||\n mime.includes('opendocument.presentation'))\n return 'vscode-icons:file-type-powerpoint';\n if (mime === 'text/csv' || mime === 'application/csv' || mime === 'text/tab-separated-values')\n return 'vscode-icons:file-type-excel2';\n if (mime.includes('spreadsheetml') || mime.includes('excel') ||\n mime.includes('opendocument.spreadsheet') || mime.includes('apple.numbers') ||\n mime.includes('iwork-numbers'))\n return 'vscode-icons:file-type-excel';\n if (isArchiveFile(mime))\n return 'vscode-icons:file-type-zip';\n if (mime.startsWith('font/') || mime.includes('ttf') || mime.includes('woff') || mime.includes('opentype'))\n return 'vscode-icons:file-type-font';\n if (mime.includes('json'))\n return 'vscode-icons:file-type-json';\n if (mime.includes('javascript'))\n return 'vscode-icons:file-type-js-official';\n if (mime.includes('typescript'))\n return 'vscode-icons:file-type-typescript';\n if (mime.includes('html'))\n return 'vscode-icons:file-type-html';\n if (mime.includes('css'))\n return 'vscode-icons:file-type-css';\n if (mime.includes('xml'))\n return 'vscode-icons:file-type-xml';\n if (mime.includes('yaml'))\n return 'vscode-icons:file-type-yaml';\n if (mime === 'application/x-sh' || mime === 'text/x-shellscript')\n return 'vscode-icons:file-type-shell';\n if (mime.includes('sql'))\n return 'vscode-icons:file-type-sql';\n if (mime === 'text/markdown' || mime === 'text/x-markdown')\n return 'vscode-icons:file-type-markdown';\n if (mime === 'text/x-python' || mime === 'application/x-python-code')\n return 'vscode-icons:file-type-python';\n if (mime === 'application/x-msdownload' ||\n mime === 'application/vnd.microsoft.portable-executable' ||\n mime === 'application/x-executable' ||\n mime === 'application/x-msdos-program')\n return 'vscode-icons:file-type-binary';\n if (mime.startsWith('text/') || mime.includes('document'))\n return 'vscode-icons:file-type-text';\n return null;\n}\n/**\n * 根据 MIME 类型(及可选文件名)返回文件类型图标(vscode-icons 彩色图标,全站统一)。\n * 提供 fileName 时优先按扩展名精确匹配(覆盖数百种类型),MIME 作兜底。\n */\nexport function getFileTypeIcon(mimeType?: string | null, iconSize = 15, fileName?: string | null) {\n let iconId: string | null = null;\n if (fileName) {\n const byName = getFileIcon(fileName);\n if (byName !== 'vscode-icons:default-file')\n iconId = byName;\n }\n if (!iconId && mimeType)\n iconId = getIconIdForMime(mimeType);\n return (<FileTypeIcon icon={iconId ?? 'vscode-icons:default-file'} size={iconSize}/>);\n}\n/** 判断文件是否支持预览;MIME 缺失或为通用二进制类型时可按文件名回退。 */\nexport function canPreviewFile(mimeType: string | null | undefined, fileName?: string | null): boolean {\n const resolvedMimeType = resolveFileMimeType(mimeType, fileName);\n if (!resolvedMimeType)\n return false;\n return (resolvedMimeType.startsWith('image/') ||\n resolvedMimeType.startsWith('audio/') ||\n resolvedMimeType.startsWith('video/') ||\n resolvedMimeType === 'application/pdf' ||\n isOfdFile(resolvedMimeType) ||\n isEmailFile(resolvedMimeType) ||\n isMindMapFile(resolvedMimeType) ||\n isDrawingFile(resolvedMimeType) ||\n isDataAssetFile(resolvedMimeType) ||\n isGeoFile(resolvedMimeType) ||\n isSpreadsheetFile(resolvedMimeType) ||\n isWordFile(resolvedMimeType) ||\n isPresentationFile(resolvedMimeType) ||\n isMarkdownFile(resolvedMimeType) ||\n isPlainTextFile(resolvedMimeType) ||\n isArchiveFile(resolvedMimeType) ||\n isJsonFile(resolvedMimeType) ||\n isSvgFile(resolvedMimeType) ||\n isCodeFile(resolvedMimeType));\n}\n/** 判断是否为 File Viewer Spreadsheet renderer 支持的表格。 */\nexport function isSpreadsheetFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return (mime === 'application/vnd.ms-excel' ||\n mime.startsWith('application/vnd.ms-excel.') ||\n mime.includes('spreadsheetml') ||\n mime === 'text/csv' ||\n mime === 'application/csv' ||\n mime === 'text/tab-separated-values' ||\n mime === 'application/vnd.oasis.opendocument.spreadsheet' ||\n mime === 'application/vnd.oasis.opendocument.spreadsheet-flat-xml' ||\n mime === 'application/vnd.apple.numbers' ||\n mime === 'application/x-iwork-numbers-sffnumbers');\n}\n/** 判断是否为 File Viewer Word renderer 支持的文本文档。 */\nexport function isWordFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return (mime === 'application/msword' ||\n mime.startsWith('application/vnd.ms-word.') ||\n mime.includes('wordprocessingml') ||\n mime === 'application/vnd.oasis.opendocument.text' ||\n mime === 'application/rtf' ||\n mime === 'application/x-rtf' ||\n mime === 'text/rtf');\n}\n/** 判断是否为 File Viewer Presentation/OpenDocument renderer 支持的演示文稿。 */\nexport function isPresentationFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return (mime === 'application/vnd.ms-powerpoint' ||\n mime.startsWith('application/vnd.ms-powerpoint.') ||\n mime.includes('presentationml') ||\n mime === 'application/vnd.oasis.opendocument.presentation');\n}\n/** 判断是否为 File Viewer OFD renderer 支持的国产版式文档。 */\nexport function isOfdFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return mime === 'application/ofd' || mime === 'application/vnd.ofd';\n}\nconst EMAIL_MIME_TYPES = new Set([\n 'message/rfc822',\n 'application/vnd.ms-outlook',\n 'application/mbox',\n 'application/x-mbox',\n]);\n/** 判断是否为 File Viewer Email renderer 支持的邮件文件。 */\nexport function isEmailFile(mimeType?: string | null): boolean {\n return !!mimeType && EMAIL_MIME_TYPES.has(mimeType.toLowerCase());\n}\n/** 判断是否为 File Viewer Mind Map renderer 支持的 XMind 文件。 */\nexport function isMindMapFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return mime === 'application/vnd.xmind.workbook' || mime === 'application/x-xmind';\n}\nconst DRAWING_MIME_TYPES = new Set([\n 'text/x-mermaid',\n 'text/mermaid',\n 'text/vnd.mermaid',\n 'application/x-mermaid',\n 'application/vnd.mermaid',\n 'application/vnd.jgraph.mxfile',\n 'application/x-drawio',\n 'application/vnd.excalidraw+json',\n 'text/vnd.plantuml',\n 'text/x-plantuml',\n]);\n/**\n * 判断是否为 File Viewer Drawing renderer 支持的图形文件。\n * 覆盖 Mermaid、draw.io、Excalidraw 与 PlantUML 四类,均在浏览器本地解析。\n */\nexport function isDrawingFile(mimeType?: string | null): boolean {\n return !!mimeType && DRAWING_MIME_TYPES.has(mimeType.toLowerCase());\n}\nconst DATA_ASSET_MIME_TYPES = new Set([\n // 字体\n 'font/ttf', 'font/otf', 'font/woff', 'font/woff2',\n 'application/x-font-ttf', 'application/x-font-otf', 'application/font-woff',\n 'application/font-woff2', 'application/vnd.ms-opentype',\n // 设计资产(PSD 的规范 MIME 以 image/ 开头,必须显式识别,否则会被当成普通图片)\n 'image/vnd.adobe.photoshop', 'image/x-photoshop', 'application/x-photoshop', 'application/photoshop',\n // 结构化数据\n // 不含 Avro:avsc 的浏览器入口是为 browserify 写的,模块加载期就执行\n // util.inherits(BlobReader, stream.Readable),而 Vite 会把 stream / util 外部化为空模块,\n // 导致 import 阶段直接抛错。要支持需给整个 web 包引入 Node 内置 polyfill,代价与收益不匹配。\n 'application/vnd.sqlite3', 'application/x-sqlite3',\n 'application/vnd.apache.parquet', 'application/x-parquet',\n 'application/wasm',\n]);\n/**\n * 判断是否为 File Viewer Data renderer 支持的数据 / 设计 / 字体资产。\n * 覆盖字体样张、PSD 图层、SQLite 表结构、Parquet 记录与 WASM 导入导出表(不含 Avro,原因见上方注释)。\n */\nexport function isDataAssetFile(mimeType?: string | null): boolean {\n return !!mimeType && DATA_ASSET_MIME_TYPES.has(mimeType.toLowerCase());\n}\n/**\n * 判断是否为「交给 Semi `ImagePreview` 展示」的图片。\n *\n * 不能直接用 `image/*` 前缀:PSD 的规范 MIME 是 `image/vnd.adobe.photoshop`,\n * 但它需要 Data renderer 解析图层,塞进 `<img>` 只会得到裂图。\n * HEIC/HEIF/TIFF 仍属于本类——它们由 `@/utils/image-decode` 转码后照常进图集。\n */\nexport function isGalleryImageFile(mimeType?: string | null, fileName?: string | null): boolean {\n const mime = resolveFileMimeType(mimeType, fileName);\n return !!mime && mime.startsWith('image/') && !isDataAssetFile(mime);\n}\nconst GEO_MIME_TYPES = new Set([\n 'application/geo+json',\n 'application/vnd.geo+json',\n 'application/vnd.google-earth.kml+xml',\n 'application/gpx+xml',\n 'application/x-gpx+xml',\n 'application/vnd.shp',\n 'application/x-esri-shape',\n]);\n/**\n * 判断是否为 File Viewer Geo renderer 支持的地理数据文件。\n * 覆盖 GeoJSON、KML、GPX 与 Shapefile,使用离线空底图渲染,不请求外部瓦片服务。\n */\nexport function isGeoFile(mimeType?: string | null): boolean {\n return !!mimeType && GEO_MIME_TYPES.has(mimeType.toLowerCase());\n}\n/** 判断是否为可预览的 Markdown 文件 */\nexport function isMarkdownFile(mimeType?: string | null): boolean {\n return mimeType === 'text/markdown' || mimeType === 'text/x-markdown';\n}\n/** 判断是否为可预览的纯文本文件(.txt) */\nexport function isPlainTextFile(mimeType?: string | null): boolean {\n return mimeType === 'text/plain';\n}\n/** 判断是否为可预览的 JSON 文件 */\nexport function isJsonFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return mime === 'application/json' || mime === 'text/json';\n}\n/** 判断是否为 SVG 图形文件 */\nexport function isSvgFile(mimeType?: string | null): boolean {\n return mimeType === 'image/svg+xml';\n}\n/**\n * 判断是否为可预览的代码/配置文件。\n * 概履: JS/TS、Python、CSS、HTML、XML、YAML、Shell、SQL 等常见文本格式。\n * JSON 并不包含在此(由 isJsonFile 单独处理)。\n */\nexport function isCodeFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return (mime === 'application/javascript' ||\n mime === 'text/javascript' ||\n mime === 'application/typescript' ||\n mime === 'text/typescript' ||\n mime === 'text/x-python' ||\n mime === 'application/x-python-code' ||\n mime === 'text/css' ||\n mime === 'text/html' ||\n mime === 'text/xml' ||\n mime === 'application/xml' ||\n mime === 'application/x-yaml' ||\n mime === 'text/yaml' ||\n mime === 'text/x-yaml' ||\n mime === 'application/x-sh' ||\n mime === 'text/x-shellscript' ||\n mime === 'application/sql' ||\n mime === 'text/x-sql');\n}\n/** 判断是否为可预览的 ZIP 压缩包 */\nexport function isZipFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return (mime === 'application/zip' ||\n mime === 'application/x-zip-compressed' ||\n mime === 'application/x-zip');\n}\nconst ARCHIVE_MIME_TYPES = new Set([\n 'application/vnd.rar',\n 'application/x-rar-compressed',\n 'application/x-7z-compressed',\n 'application/x-tar',\n 'application/x-gtar',\n 'application/gzip',\n 'application/x-gzip',\n 'application/x-bzip2',\n 'application/x-bzip-compressed-tar',\n 'application/x-xz',\n 'application/x-xz-compressed-tar',\n 'application/x-lzma',\n 'application/zstd',\n 'application/x-zstd-compressed-tar',\n 'application/vnd.ms-cab-compressed',\n 'application/x-archive',\n 'application/x-cpio',\n 'application/x-iso9660-image',\n 'application/x-xar',\n 'application/x-lzh-compressed',\n 'application/java-archive',\n 'application/vnd.android.package-archive',\n 'application/vnd.comicbook+zip',\n 'application/vnd.comicbook-rar',\n]);\n/** 判断是否为 File Viewer Archive renderer 支持的压缩包。 */\nexport function isArchiveFile(mimeType?: string | null): boolean {\n if (!mimeType)\n return false;\n const mime = mimeType.toLowerCase();\n return isZipFile(mime) || ARCHIVE_MIME_TYPES.has(mime);\n}\n/** Fetch managed bytes through their generated operation; object-storage links carry no credentials. */\nexport async function fetchProtectedFile(url: string): Promise<Blob> {\n const id = extractManagedFileId(url);\n if (id) {\n const operation = /\\/private-content(?:[?#]|$)/.test(url) ? filesPrivateContent : filesContent;\n return (await goTransport.download(operation, { params: { id } })).blob();\n }\n if (!/^https?:\\/\\//.test(url)) throw new Error('文件地址不是受支持的托管文件或存储直链');\n const response = await fetch(url, { credentials: 'omit' });\n if (!response.ok) throw new Error('文件读取失败');\n return response.blob();\n}\nconst MANAGED_FILE_CONTENT_URL = (() => {\n const [prefix] = filesContent.path.split('{id}');\n return new RegExp(`${escapeRegExp(prefix)}([0-9a-f-]{36})/(?:private-content|content)(?:[?#]|$)`, 'i');\n})();\nexport function extractManagedFileId(url: string): string | null {\n return MANAGED_FILE_CONTENT_URL.exec(url)?.[1] ?? null;\n}\n/** Prefer an authorized object-store URL and fall back to the native managed-file operation. */\nexport async function fetchManagedFileBlob(url: string): Promise<Blob> {\n const fileId = extractManagedFileId(url);\n if (fileId) {\n try {\n const { getFileAccessUrl } = await import('@fudanda/react-core/hooks/queries/files');\n const access = await getFileAccessUrl(fileId);\n if (access.url !== url) return await fetchProtectedFile(access.url);\n } catch { /* The same authorization applies to the managed-content fallback. */ }\n }\n return fetchProtectedFile(url);\n}\n","import type { ManagedFile } from '@fudanda/shared/platform';\nimport type { ResponsiveTableAction } from \"@/components/ResponsiveTableActions\";\nimport type { FilePreviewController } from \"@/hooks/useFilePreview\";\nimport { canPreviewFile } from \"@/utils/file-utils\";\nimport { confirmDelete } from \"@/utils/confirm\";\ninterface ManagedFileActionsOptions {\n preview: FilePreviewController;\n onDetail: (record: ManagedFile) => void;\n onCopyUrl: (record: ManagedFile) => void;\n onDelete: (record: ManagedFile) => void | Promise<unknown>;\n canDelete: boolean;\n}\n/** 文件表格操作列公共动作:预览 / 下载 / 详情 / 复制链接 / 删除 */\nexport function buildManagedFileActions(record: ManagedFile, { preview, onDetail, onCopyUrl, onDelete, canDelete }: ManagedFileActionsOptions): ResponsiveTableAction[] {\n const isPreviewable = canPreviewFile(record.mimeType, record.originalName);\n return [\n {\n key: 'preview',\n label: '预览',\n disabled: !isPreviewable,\n loading: preview.previewLoadingId === record.id,\n onClick: () => preview.handlePreview(record),\n },\n {\n key: 'download',\n label: '下载',\n loading: preview.downloadLoadingId === record.id,\n onClick: () => preview.handleDownload(record),\n },\n {\n key: 'detail',\n label: '详情',\n onClick: () => onDetail(record),\n },\n {\n key: 'copy-url',\n label: '复制链接',\n onClick: () => onCopyUrl(record),\n },\n {\n key: 'delete',\n label: '删除',\n danger: true,\n dividerBefore: true,\n hidden: !canDelete,\n onClick: () => {\n confirmDelete({\n title: '确认删除此文件?',\n content: '删除文件记录后,将同步尝试删除实际存储对象。',\n onOk: () => onDelete(record),\n });\n },\n },\n ];\n}\n","import { useState, useEffect, useCallback, lazy, Suspense, useMemo } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useQuery } from '@tanstack/react-query';\nimport { Modal, Spin, Toast, AudioPlayer, VideoPlayer, Typography } from \"@/ui\";\nimport { X } from 'lucide-react';\nimport { fileContract } from '@fudanda/react-core/lib/native-operations';\nimport { contractKey } from \"@fudanda/react-core/lib/contract-query\";\nimport { useThemeController } from \"@/providers/theme-controller\";\nimport { fetchManagedFileBlob, resolveFileMimeType, isSpreadsheetFile, isWordFile, isPresentationFile, isOfdFile, isEmailFile, isMindMapFile, isDrawingFile, isDataAssetFile, isGeoFile, isMarkdownFile, isPlainTextFile, isArchiveFile, isJsonFile, isSvgFile, isCodeFile, getFileTypeIcon } from \"@/utils/file-utils\";\nimport AppModal from \"@/components/AppModal\";\nimport type { CSSProperties, ReactNode } from 'react';\nimport \"@/components/FilePreviewModal/filePreview.css\";\n// @embedpdf 引擎 ~1MB,懒加载,避免经 FileAttachment 链进入首屏\nconst PDFPreviewPanel = lazy(() => import(\"@/components/PDFPreviewPanel\").then((m) => ({ default: m.PDFPreviewPanel })));\n// File Viewer 重型渲染器懒加载,避免影响首屏\nconst FileViewerPreviewPanel = lazy(() => import(\"@/components/FileViewerPreviewPanel\"));\n// react-markdown 懒加载\nconst MarkdownPreviewPanel = lazy(() => import(\"@/components/MarkdownPreviewPanel\"));\n// Semi JsonViewer 懒加载\nconst JsonPreviewPanel = lazy(() => import(\"@/components/JsonPreviewPanel\"));\n// Monaco Editor 懒加载(代码/纯文本文件预览)\nconst MonacoPreviewPanel = lazy(() => import(\"@/components/MonacoPreviewPanel\"));\ninterface FilePreviewModalProps {\n fileUrl: string;\n fileName?: string;\n mimeType?: string | null;\n visible: boolean;\n onClose: () => void;\n /** 遇到不支持预览的格式时触发,不传则组件内部静默关闭 */\n onFallback?: (fileUrl: string, fileName: string, mimeType: string) => void;\n style?: CSSProperties;\n}\ntype PreviewKind = 'spreadsheet' | 'word' | 'presentation' | 'ofd' | 'email' | 'mindmap' | 'drawing' | 'dataAsset' | 'geo' | 'archive' | 'markdown' | 'plainText' | 'json' | 'svg' | 'code' | 'pdf' | 'audio' | 'video';\ntype PreviewData = {\n kind: 'spreadsheet' | 'word' | 'presentation' | 'ofd' | 'email' | 'mindmap' | 'drawing' | 'dataAsset' | 'geo' | 'archive';\n file: File;\n} | {\n kind: 'markdown';\n text: string;\n} | {\n kind: 'plainText';\n text: string;\n} | {\n kind: 'json';\n text: string;\n} | {\n kind: 'svg';\n url: string;\n} | {\n kind: 'code';\n text: string;\n} | {\n kind: 'pdf';\n file: File;\n} | {\n kind: 'audio';\n url: string;\n} | {\n kind: 'video';\n url: string;\n};\nfunction revokePreviewUrl(data: PreviewData | undefined) {\n if (data?.kind === 'audio' || data?.kind === 'video' || data?.kind === 'svg') {\n URL.revokeObjectURL(data.url);\n }\n}\n/** 懒加载预览面板的统一弹窗外壳:AppModal + Suspense 加载兜底(各分支仅宽高不同) */\nfunction PreviewModalShell({ title, onCancel, fullscreen, onToggleFullscreen, width, viewportHeight, children }: Readonly<{\n title: ReactNode;\n onCancel: () => void;\n fullscreen: boolean;\n onToggleFullscreen: () => void;\n width: string;\n /** 非全屏时弹窗高度基准(如 '90vh') */\n viewportHeight: string;\n children: ReactNode;\n}>) {\n return (<AppModal visible onCancel={onCancel} title={title} footer={null} fullscreenable fullscreen={fullscreen} onToggleFullscreen={onToggleFullscreen} width={width} centered bodyStyle={{ padding: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden', height: fullscreen ? 'calc(100vh - 40px)' : `calc(${viewportHeight} - 40px)` }} keepDOM={false}>\n <Suspense fallback={<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }}>\n <Spin size=\"large\" tip=\"加载预览组件...\"/>\n </div>}>\n {children}\n </Suspense>\n </AppModal>);\n}\nexport default function FilePreviewModal({ fileUrl, fileName = '文件', mimeType, visible, onClose, onFallback, }: Readonly<FilePreviewModalProps>) {\n const [fullscreen, setFullscreen] = useState(false);\n const toggleFullscreen = useCallback(() => {\n setFullscreen(f => !f);\n }, []);\n const { isDark } = useThemeController();\n const resolvedMimeType = useMemo(() => resolveFileMimeType(mimeType, fileName), [fileName, mimeType]);\n const previewKind = useMemo<PreviewKind | 'unsupported' | 'image' | null>(() => {\n if (!resolvedMimeType)\n return null;\n const isImage = resolvedMimeType.startsWith('image/');\n const tsExtPattern = /\\.(ts|tsx)$/i;\n const isMpegTsAsCode = resolvedMimeType === 'video/mp2t' && tsExtPattern.test(fileName);\n if (isSpreadsheetFile(resolvedMimeType))\n return 'spreadsheet';\n if (isWordFile(resolvedMimeType))\n return 'word';\n if (isPresentationFile(resolvedMimeType))\n return 'presentation';\n if (isOfdFile(resolvedMimeType))\n return 'ofd';\n if (isEmailFile(resolvedMimeType))\n return 'email';\n if (isMindMapFile(resolvedMimeType))\n return 'mindmap';\n if (isDrawingFile(resolvedMimeType))\n return 'drawing';\n // 必须早于下方的 image/* 判定:PSD 的规范 MIME 是 image/vnd.adobe.photoshop\n if (isDataAssetFile(resolvedMimeType))\n return 'dataAsset';\n if (isGeoFile(resolvedMimeType))\n return 'geo';\n if (isMarkdownFile(resolvedMimeType))\n return 'markdown';\n if (isPlainTextFile(resolvedMimeType))\n return 'plainText';\n if (isArchiveFile(resolvedMimeType))\n return 'archive';\n if (isJsonFile(resolvedMimeType))\n return 'json';\n if (isSvgFile(resolvedMimeType))\n return 'svg';\n if (isCodeFile(resolvedMimeType) || isMpegTsAsCode)\n return 'code';\n if (resolvedMimeType === 'application/pdf')\n return 'pdf';\n if (resolvedMimeType.startsWith('audio/'))\n return 'audio';\n if (resolvedMimeType.startsWith('video/') && !isMpegTsAsCode)\n return 'video';\n if (isImage)\n return 'image';\n return 'unsupported';\n }, [fileName, resolvedMimeType]);\n // 非契约 blob 通道(任意文件地址经 fetchManagedFileBlob 取二进制并按类型加工),保留手写 useQuery,key 取文件内容契约 key 追加预览段\n const previewQuery = useQuery({\n queryKey: [...contractKey(fileContract.content), 'preview', visible, fileUrl, fileName, resolvedMimeType, previewKind],\n queryFn: async (): Promise<PreviewData> => {\n const blob = await fetchManagedFileBlob(fileUrl);\n if (previewKind === 'spreadsheet' || previewKind === 'word' || previewKind === 'presentation' || previewKind === 'ofd' || previewKind === 'email' || previewKind === 'mindmap' || previewKind === 'drawing' || previewKind === 'dataAsset' || previewKind === 'geo' || previewKind === 'archive') {\n return {\n kind: previewKind,\n file: new File([blob], fileName, { type: resolvedMimeType || blob.type }),\n };\n }\n if (previewKind === 'markdown')\n return { kind: 'markdown', text: await blob.text() };\n if (previewKind === 'plainText')\n return { kind: 'plainText', text: await blob.text() };\n if (previewKind === 'json')\n return { kind: 'json', text: await blob.text() };\n if (previewKind === 'svg')\n return { kind: 'svg', url: URL.createObjectURL(blob) };\n if (previewKind === 'code')\n return { kind: 'code', text: await blob.text() };\n if (previewKind === 'pdf')\n return { kind: 'pdf', file: new File([blob], fileName, { type: 'application/pdf' }) };\n if (previewKind === 'audio')\n return { kind: 'audio', url: URL.createObjectURL(blob) };\n if (previewKind === 'video')\n return { kind: 'video', url: URL.createObjectURL(blob) };\n throw new Error('文件加载失败');\n },\n enabled: visible && !!previewKind && previewKind !== 'unsupported' && previewKind !== 'image',\n staleTime: 0,\n gcTime: 0,\n });\n const previewData = previewQuery.data;\n useEffect(() => {\n return () => revokePreviewUrl(previewData);\n }, [previewData]);\n useEffect(() => {\n if (!visible) {\n setFullscreen(false);\n return;\n }\n if (!resolvedMimeType) {\n onClose();\n return;\n }\n if (previewKind === 'unsupported') {\n onFallback?.(fileUrl, fileName, resolvedMimeType);\n onClose();\n return;\n }\n if (previewKind === 'image') {\n onClose();\n }\n }, [fileName, fileUrl, onClose, onFallback, previewKind, resolvedMimeType, visible]);\n useEffect(() => {\n if (previewQuery.error) {\n Toast.error(previewQuery.error instanceof Error ? previewQuery.error.message : '文件加载失败');\n onClose();\n }\n }, [onClose, previewQuery.error]);\n const handleClose = () => {\n onClose();\n };\n /**\n * 预览弹窗标题:文件类型图标 + 文件名。\n * 由 AppModal 的 title prop 承载,统一在弹窗顶部展示。\n * PDF 除外(PDFPreviewPanel 有自己的完整标题栏,不使用 AppModal)。\n */\n const previewTitle: ReactNode = (<div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>\n {getFileTypeIcon(mimeType, 15, fileName)}\n <Typography.Text ellipsis={{ showTooltip: true }} style={{ fontSize: 13, fontWeight: 500, minWidth: 0 }}>\n {fileName}\n </Typography.Text>\n </div>);\n if (!visible)\n return null;\n if (previewQuery.isLoading) {\n return (<AppModal visible onCancel={handleClose} title={previewTitle} footer={null} fullscreenable={false} centered keepDOM={false} bodyStyle={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 40 }}>\n <Spin size=\"large\" tip=\"加载中...\"/>\n </AppModal>);\n }\n if (previewData?.kind === 'pdf') {\n return (<Modal visible onCancel={handleClose} title={null} footer={null} fullScreen={fullscreen} width=\"min(1100px, 92vw)\" centered bodyStyle={{ padding: 0, display: 'flex', overflow: 'hidden', height: fullscreen ? '100vh' : '88vh' }} closable={false} keepDOM={false}>\n <Suspense fallback={<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: '100%' }}>\n <Spin size=\"large\"/>\n </div>}>\n <PDFPreviewPanel file={previewData.file} onClose={handleClose} fullscreen={fullscreen} onToggleFullscreen={toggleFullscreen} style={{ width: '100%', borderLeft: 'none' }}/>\n </Suspense>\n </Modal>);\n }\n if (previewData?.kind === 'spreadsheet' || previewData?.kind === 'word' || previewData?.kind === 'presentation' || previewData?.kind === 'ofd' || previewData?.kind === 'email' || previewData?.kind === 'mindmap' || previewData?.kind === 'drawing' || previewData?.kind === 'dataAsset' || previewData?.kind === 'geo' || previewData?.kind === 'archive') {\n const isPresentation = previewData.kind === 'presentation';\n const isWide = previewData.kind === 'spreadsheet' || previewData.kind === 'archive' || previewData.kind === 'email' || previewData.kind === 'mindmap' || previewData.kind === 'drawing' || previewData.kind === 'dataAsset' || previewData.kind === 'geo' || isPresentation;\n return (<PreviewModalShell title={previewTitle} onCancel={handleClose} fullscreen={fullscreen} onToggleFullscreen={toggleFullscreen} width={isWide ? 'min(1200px, 94vw)' : 'min(960px, 92vw)'} viewportHeight=\"90vh\">\n <FileViewerPreviewPanel file={previewData.file} style={{ flex: 1 }}/>\n </PreviewModalShell>);\n }\n if (previewData?.kind === 'markdown') {\n const isRawText = previewData.text.startsWith('\\u0000PLAINTEXT\\u0000');\n const displayContent = isRawText ? previewData.text.slice('\\u0000PLAINTEXT\\u0000'.length) : previewData.text;\n return (<PreviewModalShell title={previewTitle} onCancel={handleClose} fullscreen={fullscreen} onToggleFullscreen={toggleFullscreen} width=\"min(900px, 92vw)\" viewportHeight=\"90vh\">\n <MarkdownPreviewPanel content={displayContent} rawText={isRawText} style={{ flex: 1, minHeight: 0 }}/>\n </PreviewModalShell>);\n }\n if (previewData?.kind === 'json') {\n return (<PreviewModalShell title={previewTitle} onCancel={handleClose} fullscreen={fullscreen} onToggleFullscreen={toggleFullscreen} width=\"min(900px, 92vw)\" viewportHeight=\"88vh\">\n <JsonPreviewPanel content={previewData.text} style={{ flex: 1, minHeight: 0 }}/>\n </PreviewModalShell>);\n }\n if (previewData?.kind === 'code' || previewData?.kind === 'plainText') {\n return (<PreviewModalShell title={previewTitle} onCancel={handleClose} fullscreen={fullscreen} onToggleFullscreen={toggleFullscreen} width=\"min(1100px, 92vw)\" viewportHeight=\"90vh\">\n <MonacoPreviewPanel content={previewData.text} fileName={fileName} style={{ flex: 1, minHeight: 0 }}/>\n </PreviewModalShell>);\n }\n if (previewData?.kind === 'svg') {\n return (<AppModal visible onCancel={handleClose} title={previewTitle} footer={null} fullscreenable fullscreen={fullscreen} onToggleFullscreen={toggleFullscreen} width=\"min(900px, 92vw)\" centered bodyStyle={{\n padding: 16,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n overflow: 'auto',\n height: fullscreen ? 'calc(100vh - 40px)' : 'calc(80vh - 40px)',\n }} keepDOM={false}>\n <img src={previewData.url} alt={fileName} style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain' }}/>\n </AppModal>);\n }\n if (previewData?.kind === 'audio') {\n // 音频固定在页面底部以播放条形式呈现,避免在窄弹窗内控件(播放键)被裁切\n return createPortal(<div className=\"arcbase-audio-dock\" role=\"region\" aria-label=\"音频播放器\">\n <div className=\"arcbase-audio-dock__inner\">\n <div className=\"arcbase-audio-dock__player\">\n <AudioPlayer className=\"arcbase-audio-preview\" audioUrl={{ src: previewData.url, title: fileName }} theme={isDark ? 'dark' : 'light'} autoPlay style={{ width: '100%' }}/>\n </div>\n <button type=\"button\" className=\"arcbase-audio-dock__close\" onClick={handleClose} aria-label=\"关闭音频播放器\">\n <X size={18}/>\n </button>\n </div>\n </div>, document.body);\n }\n if (previewData?.kind === 'video') {\n return (<AppModal visible onCancel={handleClose} title={previewTitle} footer={null} fullscreenable={false} width=\"min(960px, 92vw)\" centered bodyStyle={{ padding: 0, overflow: 'hidden', borderRadius: 'var(--arc-antd-border-radius-medium)' }} keepDOM={false}>\n <VideoPlayer src={previewData.url} theme={isDark ? 'dark' : 'light'} width=\"100%\" autoPlay={false} muted={false} volume={100} clickToPlay={true} defaultPlaybackRate={1} playbackRateList={[\n { label: '0.5x', value: 0.5 },\n { label: '1x', value: 1 },\n { label: '1.5x', value: 1.5 },\n { label: '2x', value: 2 },\n ]} style={{ borderRadius: 'var(--arc-antd-border-radius-medium)' }}/>\n </AppModal>);\n }\n return null;\n}\n","import { useEffect, useState } from 'react';\nimport { generateWatermarkTile } from \"@fudanda/react-core/components/watermark-tile\";\ninterface PreviewWatermarkProps {\n readonly content: string | string[] | null | undefined;\n readonly visible: boolean;\n}\n/**\n * 预览水印:覆盖整个视口且位于预览弹层之上(Semi Modal / ImagePreview 通过 portal 挂到 body,\n * 页面内的 Watermark 覆盖不到),只在预览打开时渲染,不拦截任何交互。\n */\nexport function PreviewWatermark({ content, visible }: PreviewWatermarkProps) {\n const lines = (Array.isArray(content) ? content : content ? [content] : []).filter(Boolean);\n const key = JSON.stringify(lines);\n const [style, setStyle] = useState<React.CSSProperties | null>(null);\n useEffect(() => {\n const lines = JSON.parse(key) as string[];\n if (!visible || lines.length === 0) {\n setStyle(null);\n return;\n }\n const isDark = document.body.getAttribute('theme-mode') === 'dark';\n const tile = generateWatermarkTile(lines, 13, 0.22, -22, 180, 110, isDark);\n if (!tile)\n return;\n setStyle({\n position: 'fixed', inset: 0, pointerEvents: 'none', userSelect: 'none', zIndex: 2100,\n backgroundImage: `url(${tile.dataUrl})`, backgroundRepeat: 'repeat', backgroundSize: `${tile.cssWidth}px ${tile.cssHeight}px`,\n });\n }, [key, visible]);\n if (!style)\n return null;\n return <div aria-hidden style={style} data-testid=\"preview-watermark\"/>;\n}\n","import { ImagePreview } from \"@/ui\";\nimport FilePreviewModal from \"@/components/FilePreviewModal/index\";\nimport { PreviewWatermark } from \"@/components/PreviewWatermark\";\nimport type { FilePreviewController } from \"@/hooks/useFilePreview\";\ninterface FilePreviewLayerProps {\n readonly preview: FilePreviewController;\n /** 预览打开时叠加的水印文本(空 = 不加水印) */\n readonly watermark?: string | string[] | null;\n}\n/** 图集预览 + 非图片文件预览弹层(与 useFilePreview 配套使用) */\nexport function FilePreviewLayer({ preview, watermark }: FilePreviewLayerProps) {\n return (<>\n <ImagePreview src={preview.previewSrcList} visible={preview.previewVisible} currentIndex={preview.previewCurrentIndex} onChange={preview.setPreviewCurrentIndex} onVisibleChange={(v) => {\n if (!v)\n preview.closeImagePreview();\n }} infinite/>\n <FilePreviewModal fileUrl={preview.filePreview?.url ?? ''} fileName={preview.filePreview?.name} mimeType={preview.filePreview?.mimeType} visible={!!preview.filePreview} onClose={preview.closeFilePreview}/>\n <PreviewWatermark content={watermark} visible={preview.previewVisible || !!preview.filePreview}/>\n </>);\n}\n","import type { ReactNode } from 'react';\nimport { Button, Descriptions, Space, Spin, Typography } from \"@/ui\";\nimport type { ManagedFile } from '@fudanda/shared/platform';\nimport { formatBytes } from '@fudanda/shared/core';\nimport { AppModal } from \"@/components/AppModal\";\nimport { formatDateTime } from \"@fudanda/react-core/utils/date\";\nimport '@/components/FileDetailModal.css';\nconst { Text } = Typography;\ninterface FileDetailModalProps {\n visible: boolean;\n /** 当前展示的文件(详情查询结果优先,缺省回退列表行) */\n file: ManagedFile | null;\n /** 详情查询进行中 */\n loading: boolean;\n /** 访问链接口径(文件中心 directUrl 优先;存储浏览器统一走代理地址),与页面「复制链接」保持一致 */\n resolveUrl: (file: ManagedFile) => string;\n onCopyUrl: (file: ManagedFile) => void;\n onClose: () => void;\n /** 基础信息之后的附加整行(如图片分辨率) */\n extraRows?: Array<{\n key: string;\n value: ReactNode;\n }>;\n}\n/** 托管文件详情弹窗:文件中心与存储服务文件浏览器共用 */\nexport function FileDetailModal({ visible, file, loading, resolveUrl, onCopyUrl, onClose, extraRows = [] }: Readonly<FileDetailModalProps>) {\n return (<AppModal title=\"文件详情\" visible={visible} onCancel={onClose} footer={<Space>\n <Button onClick={() => file && onCopyUrl(file)}>复制链接</Button>\n <Button type=\"primary\" onClick={onClose}>关闭</Button>\n </Space>} width={960}>\n <Spin spinning={loading} tip=\"加载中...\" size=\"small\">\n {file && (<Descriptions className=\"file-detail-descriptions\" column={{ xs: 1, sm: 1, md: 2 }} align=\"left\" size=\"medium\" data={[\n { key: '文件名', value: file.originalName, span: 'filled' },\n { key: '存储服务', value: file.storageName },\n { key: 'MIME 类型', value: file.mimeType || '—' },\n { key: '文件大小', value: formatBytes(file.size) },\n { key: '上传人', value: file.uploaderName || '—' },\n ...extraRows.map(row => ({ ...row, span: 'filled' as const })),\n { key: '对象键', value: <Text copyable style={{ fontSize: 12 }}>{file.objectKey}</Text>, span: 'filled' },\n { key: '访问链接', value: <Text copyable style={{ fontSize: 12 }}>{resolveUrl(file)}</Text>, span: 'filled' },\n { key: '上传时间', value: formatDateTime(file.createdAt), span: 'filled' },\n ]}/>)}\n </Spin>\n </AppModal>);\n}\nexport default FileDetailModal;\n","import { useRef, useState } from 'react';\nimport { Toast } from \"@/ui\";\nimport { canPreviewFile, fetchManagedFileBlob, isGalleryImageFile, resolveFileMimeType } from \"@/utils/file-utils\";\nimport { createDisplayableImageUrl } from \"@fudanda/react-core/utils/image-decode\";\n/** 预览/下载所需的最小文件形状:ManagedFile 天然满足;素材等业务实体映射这四个字段即可复用预览设施 */\nexport interface PreviewableFile {\n id: string;\n url: string;\n originalName: string;\n mimeType?: string | null;\n}\ninterface FilePreviewTarget {\n id: string;\n url: string;\n name: string;\n mimeType: string;\n}\n/**\n * 受保护文件的预览/下载共享逻辑(文件管理页与存储空间浏览器共用)。\n *\n * - 图片:进入图集预览(先加载点击图,其余后台渐进加载,会话号防止过期加载写入)\n * - 可预览的非图片(PDF 等):打开 FilePreviewModal\n * - 其他类型:拉取 blob 后新窗口打开\n *\n * 与 `<FilePreviewLayer preview={...} />` 配套使用。\n *\n * @param getImageFiles 返回当前列表中的图片文件(点击图片时构建图集用)\n */\nexport function useFilePreview(getImageFiles: () => PreviewableFile[]) {\n const [previewVisible, setPreviewVisible] = useState(false);\n const [previewSrcList, setPreviewSrcList] = useState<string[]>([]);\n const [previewCurrentIndex, setPreviewCurrentIndex] = useState(0);\n const [previewLoadingId, setPreviewLoadingId] = useState<string | null>(null);\n const [filePreview, setFilePreview] = useState<FilePreviewTarget | null>(null);\n const [downloadLoadingId, setDownloadLoadingId] = useState<string | null>(null);\n // previewBlobUrlsRef: 与图片列表索引对齐,记录已创建的 blob URL 便于统一释放\n const previewBlobUrlsRef = useRef<string[]>([]);\n // previewSessionRef: 每次新预览会话自增,用于取消过期的后台加载\n const previewSessionRef = useRef(0);\n const cleanupPreviewBlobs = () => {\n previewBlobUrlsRef.current.forEach((url) => globalThis.URL.revokeObjectURL(url));\n previewBlobUrlsRef.current = [];\n };\n const handlePreview = async (file: PreviewableFile) => {\n const resolvedMimeType = resolveFileMimeType(file.mimeType, file.originalName);\n const isImage = isGalleryImageFile(file.mimeType, file.originalName);\n const isPreviewable = canPreviewFile(file.mimeType, file.originalName);\n if (!isPreviewable && !isImage) {\n try {\n const blob = await fetchManagedFileBlob(file.url);\n const objectUrl = globalThis.URL.createObjectURL(blob);\n globalThis.open(objectUrl, '_blank', 'noopener,noreferrer');\n globalThis.setTimeout(() => globalThis.URL.revokeObjectURL(objectUrl), 60000);\n }\n catch (error) {\n Toast.error(error instanceof Error ? error.message : '预览文件失败');\n }\n return;\n }\n if (!isImage) {\n setPreviewLoadingId(file.id);\n try {\n setFilePreview({\n id: file.id,\n url: file.url,\n name: file.originalName,\n mimeType: resolvedMimeType ?? 'application/octet-stream',\n });\n }\n catch (error) {\n Toast.error(error instanceof Error ? error.message : '预览文件失败');\n }\n finally {\n setPreviewLoadingId(null);\n }\n return;\n }\n const imageFiles = getImageFiles().filter((f) => isGalleryImageFile(f.mimeType, f.originalName));\n const clickedIndex = imageFiles.findIndex((f) => f.id === file.id);\n setPreviewLoadingId(file.id);\n // 开启新预览会话\n previewSessionRef.current += 1;\n const mySession = previewSessionRef.current;\n try {\n cleanupPreviewBlobs();\n // 先用空占位初始化,保证数组索引稳定\n const initialUrls = imageFiles.map(() => '');\n previewBlobUrlsRef.current = [...initialUrls];\n // 优先加载被点击的图片 → 立即展示预览\n const clickedFile = imageFiles[clickedIndex];\n const clickedBlob = await fetchManagedFileBlob(clickedFile.url);\n if (previewSessionRef.current !== mySession)\n return; // 用户在加载完成前已关闭预览\n const clickedUrl = await createDisplayableImageUrl(clickedBlob, clickedFile.mimeType, clickedFile.originalName);\n if (previewSessionRef.current !== mySession) {\n globalThis.URL.revokeObjectURL(clickedUrl);\n return;\n }\n initialUrls[clickedIndex] = clickedUrl;\n previewBlobUrlsRef.current[clickedIndex] = clickedUrl;\n setPreviewSrcList([...initialUrls]);\n setPreviewCurrentIndex(Math.max(0, clickedIndex));\n setPreviewVisible(true);\n // 其余图片后台渐进加载(不阻塞)\n imageFiles.forEach(async (imgFile, i) => {\n if (i === clickedIndex)\n return;\n try {\n const blob = await fetchManagedFileBlob(imgFile.url);\n if (previewSessionRef.current !== mySession)\n return;\n const url = await createDisplayableImageUrl(blob, imgFile.mimeType, imgFile.originalName);\n if (previewSessionRef.current !== mySession) {\n globalThis.URL.revokeObjectURL(url);\n return;\n }\n previewBlobUrlsRef.current[i] = url;\n setPreviewSrcList((prev) => {\n const updated = [...prev];\n updated[i] = url;\n return updated;\n });\n }\n catch { /* 忽略单张失败 */ }\n });\n }\n catch (error) {\n Toast.error(error instanceof Error ? error.message : '预览图片失败');\n }\n finally {\n setPreviewLoadingId(null);\n }\n };\n const handleDownload = async (file: PreviewableFile) => {\n setDownloadLoadingId(file.id);\n try {\n const blob = await fetchManagedFileBlob(file.url);\n const objectUrl = globalThis.URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = objectUrl;\n link.download = file.originalName;\n link.click();\n globalThis.setTimeout(() => globalThis.URL.revokeObjectURL(objectUrl), 60000);\n }\n catch (error) {\n Toast.error(error instanceof Error ? error.message : '下载文件失败');\n }\n finally {\n setDownloadLoadingId(null);\n }\n };\n /** 关闭图集预览:使在途后台加载失效并释放 blob URL */\n const closeImagePreview = () => {\n previewSessionRef.current += 1;\n setPreviewVisible(false);\n cleanupPreviewBlobs();\n setPreviewSrcList([]);\n };\n const closeFilePreview = () => setFilePreview(null);\n /** 数据源切换时强制关闭预览(不清空图集列表,与原行为一致) */\n const resetPreview = () => {\n setFilePreview(null);\n setPreviewVisible(false);\n previewSessionRef.current += 1;\n };\n return {\n previewVisible,\n previewSrcList,\n previewCurrentIndex,\n setPreviewCurrentIndex,\n previewLoadingId,\n downloadLoadingId,\n filePreview,\n handlePreview,\n handleDownload,\n closeImagePreview,\n closeFilePreview,\n resetPreview,\n };\n}\nexport type FilePreviewController = ReturnType<typeof useFilePreview>;\n","import { cloneElement, useCallback, useRef } from 'react';\nimport type { CSSProperties, ReactElement } from 'react';\nimport { Dropdown } from \"@/ui\";\nexport interface CursorPoint {\n readonly x: number;\n readonly y: number;\n}\ninterface CursorContextDropdownProps {\n readonly point: CursorPoint;\n readonly contextKey: string | number;\n readonly render: ReactElement<{\n style?: CSSProperties;\n }>;\n readonly onClose: () => void;\n}\nconst viewportMenuStyle: CSSProperties = {\n maxHeight: 'calc(100vh - 16px)',\n overflowY: 'auto',\n};\nfunction withViewportMenuStyle(content: ReactElement<{\n style?: CSSProperties;\n}>): ReactElement<{\n style?: CSSProperties;\n}> {\n return cloneElement(content, {\n style: { ...content.props.style, ...viewportMenuStyle },\n });\n}\nfunction getPopupContainer(): HTMLElement {\n return document.body;\n}\n/** Semi Dropdown adapter for menus that must open at the pointer coordinates. */\nexport function CursorContextDropdown({ point, contextKey, render, onClose, }: Readonly<CursorContextDropdownProps>) {\n const rePosKey = `${contextKey}:${point.x}:${point.y}`;\n const closedKeyRef = useRef<string | null>(null);\n const closeOnce = useCallback(() => {\n if (closedKeyRef.current === rePosKey)\n return;\n closedKeyRef.current = rePosKey;\n onClose();\n }, [onClose, rePosKey]);\n return (<Dropdown visible trigger=\"click\" position=\"bottomLeft\" autoAdjustOverflow rePosKey={rePosKey} render={withViewportMenuStyle(render)} getPopupContainer={getPopupContainer} clickToHide closeOnEsc onClickOutSide={closeOnce} onVisibleChange={(visible) => {\n if (!visible)\n closeOnce();\n }}>\n <span aria-hidden=\"true\" style={{\n position: 'fixed',\n left: point.x,\n top: point.y,\n width: 1,\n height: 1,\n pointerEvents: 'none',\n }}/>\n </Dropdown>);\n}\n","import { useState } from 'react';\nimport { Checkbox, Dropdown, Spin, Tooltip } from \"@/ui\";\nimport type { ManagedFile } from '@fudanda/shared/platform';\nimport { getFileTypeIcon, canPreviewFile } from \"@/utils/file-utils\";\nimport { confirmAndDelete } from \"@/components/list-page/index\";\nimport { CursorContextDropdown } from \"@/components/CursorContextDropdown\";\nimport { formatBytes } from '@fudanda/shared/core';\nimport \"@/pages/system/files/FilesPage.css\";\nexport interface FileGridCardProps {\n file: ManagedFile;\n selected: boolean;\n canSelect: boolean;\n onSelect: (id: string, checked: boolean) => void;\n onPreview: (file: ManagedFile) => void;\n onDownload: (file: ManagedFile) => void;\n onDelete: (file: ManagedFile) => void;\n onDetail: (file: ManagedFile) => void;\n onCopyUrl: (file: ManagedFile) => void;\n canDelete: boolean;\n previewLoading: boolean;\n}\nexport function FileGridCard({ file, selected, canSelect, onSelect, onPreview, onDownload, onDelete, onDetail, onCopyUrl, canDelete, previewLoading, }: Readonly<FileGridCardProps>) {\n const [ctxPos, setCtxPos] = useState<{\n x: number;\n y: number;\n } | null>(null);\n const isPreviewable = canPreviewFile(file.mimeType, file.originalName);\n const ext = file.originalName.includes('.') ? file.originalName.split('.').pop()?.toUpperCase() : '';\n return (<>\n <div className={`files-grid-card${selected ? ' files-grid-card--selected' : ''}`} onContextMenu={(e) => {\n e.preventDefault();\n setCtxPos({ x: e.clientX, y: e.clientY });\n }}>\n <button type=\"button\" className=\"files-grid-card__preview-btn\" aria-label={isPreviewable ? `预览 ${file.originalName}` : file.originalName} style={{ cursor: isPreviewable ? 'pointer' : 'default' }} onClick={(e) => {\n if (canSelect && (e.ctrlKey || e.metaKey || e.shiftKey)) {\n onSelect(file.id, !selected);\n return;\n }\n if (isPreviewable)\n onPreview(file);\n }}/>\n {canSelect && (<div className=\"files-grid-card__checkbox\">\n <Checkbox checked={selected} onChange={() => onSelect(file.id, !selected)}/>\n </div>)}\n <div className=\"files-grid-card__media-wrap\">\n <div className=\"files-grid-card__media\">\n <span className=\"files-grid-card__icon\">\n {getFileTypeIcon(file.mimeType, 34, file.originalName)}\n </span>\n {ext && <span className=\"files-grid-card__type-badge\">{ext}</span>}\n {previewLoading && (<div className=\"files-grid-card__media-overlay\">\n <Spin />\n </div>)}\n </div>\n </div>\n <div className=\"files-grid-card__info\">\n <Tooltip content={file.originalName} position=\"top\">\n <div className=\"files-grid-card__name\">{file.originalName}</div>\n </Tooltip>\n <div className=\"files-grid-card__meta\">\n <span>{formatBytes(file.size)}</span>\n </div>\n </div>\n </div>\n {ctxPos && (<CursorContextDropdown point={ctxPos} contextKey={file.id} onClose={() => setCtxPos(null)} render={(<Dropdown.Menu>\n {isPreviewable && <Dropdown.Item onClick={() => onPreview(file)}>预览</Dropdown.Item>}\n <Dropdown.Item onClick={() => onDownload(file)}>下载</Dropdown.Item>\n <Dropdown.Item onClick={() => onDetail(file)}>详情</Dropdown.Item>\n <Dropdown.Item onClick={() => onCopyUrl(file)}>复制链接</Dropdown.Item>\n {canDelete && (<>\n <Dropdown.Divider />\n <Dropdown.Item type=\"danger\" onClick={() => {\n confirmAndDelete({\n title: '确认删除此文件?',\n content: '删除文件记录后,将同步尝试删除实际存储对象。',\n run: () => Promise.resolve(onDelete(file)),\n successMessage: null,\n });\n }}>删除</Dropdown.Item>\n </>)}\n </Dropdown.Menu>)}/>)}\n </>);\n}\n","/**\n * 表格「文件名」单元格统一组件\n *\n * 图标固定宽度、名称占满剩余列宽并在列边界省略,悬停 Tooltip 展示完整名称。\n * 需配合列定义 `ellipsis: { showTitle: false }` 使用(表格切为 fixed 布局并关闭原生 title)。\n *\n * - 默认按 mimeType/文件名推断托管文件图标;\n * - `icon` 可传入自定义图标(如 iconify 扩展名图标、文件夹图标);\n * - `onClick` 存在时整体渲染为可点击按钮(如文件夹进入目录)。\n */\nimport type { ReactNode } from 'react';\nimport { Tooltip } from \"@/ui\";\nimport { getFileTypeIcon } from \"@/utils/file-utils\";\nimport \"@/components/FileNameCell.css\";\nimport { escapeRegExp } from '@fudanda/shared/core';\ninterface FileNameCellProps {\n name: string;\n /** 自定义图标;缺省时按 mimeType/文件名推断 */\n icon?: ReactNode;\n mimeType?: string | null;\n /** 提供时整行渲染为可点击按钮 */\n onClick?: () => void;\n highlight?: string;\n}\nexport function FileNameCell({ name, icon, mimeType, onClick, highlight }: Readonly<FileNameCellProps>) {\n const parts = highlight ? name.split(new RegExp(`(${escapeRegExp(highlight)})`, 'gi')) : [name];\n const content = (<>\n <span className=\"file-name-cell__icon\">{icon ?? getFileTypeIcon(mimeType, 16, name)}</span>\n <Tooltip content={name}>\n <span className=\"file-name-cell__text\">{parts.map((part, index) => highlight && part.toLowerCase() === highlight.toLowerCase() ? <mark key={index}>{part}</mark> : part)}</span>\n </Tooltip>\n </>);\n if (onClick) {\n return (<button type=\"button\" className=\"file-name-cell file-name-cell--button\" onClick={onClick}>\n {content}\n </button>);\n }\n return <div className=\"file-name-cell\">{content}</div>;\n}\n","import { useEffect, useState } from 'react';\nimport { Button, Breadcrumb, Pagination, SideSheet, Space, Spin, Toast, Tooltip, Typography } from \"@/ui\";\nimport { Folder, ChevronLeft, ChevronRight, LayoutGrid, List as ListIcon } from 'lucide-react';\nimport type { FileStorageConfig, FolderEntry, ManagedFile } from '@fudanda/shared/platform';\nimport type { ColumnProps } from \"@/ui\";\nimport DateTimeText from \"@/components/DateTimeText\";\nimport { getFileFullUrl, canPreviewFile } from \"@/utils/file-utils\";\nimport { buildManagedFileActions } from \"@/utils/managed-file-actions\";\nimport { copyTextWithToast } from \"@/utils/clipboard\";\nimport { usePermission } from \"@fudanda/react-core/hooks/usePermission\";\nimport { usePagination } from \"@fudanda/react-core/hooks/usePagination\";\nimport { FilePreviewLayer } from \"@/components/FilePreviewLayer\";\nimport { FileDetailModal } from \"@/components/FileDetailModal\";\nimport { useFilePreview } from \"@/hooks/useFilePreview\";\nimport ConfigurableTable from \"@/components/ConfigurableTable\";\nimport { createOperationColumn } from \"@/components/ResponsiveTableActions\";\nimport { FileGridCard } from \"@/pages/system/files/components/FileGridCard\";\nimport { FileNameCell } from \"@/components/FileNameCell\";\nimport { useDeleteFiles, useFileDetail } from \"@fudanda/react-core/hooks/queries/files\";\nimport { useStorageBrowse } from \"@fudanda/react-core/hooks/queries/file-storage-configs\";\nimport { formatBytes } from '@fudanda/shared/core';\nimport \"@/pages/system/file-configs/StorageFileBrowser.css\";\nconst { Text } = Typography;\ninterface StorageFileBrowserProps {\n config: FileStorageConfig | null;\n onClose: () => void;\n}\nexport default function StorageFileBrowser({ config, onClose }: Readonly<StorageFileBrowserProps>) {\n const { hasPermission } = usePermission();\n const [viewMode, setViewMode] = useState<'list' | 'grid'>('list');\n const [currentPath, setCurrentPath] = useState('');\n const { page, setPage } = usePagination();\n const PAGE_SIZE = 50;\n const browseQuery = useStorageBrowse(config?.id, currentPath, !!config);\n const browseData = browseQuery.data ?? null;\n const loading = browseQuery.isFetching;\n const deleteMutation = useDeleteFiles();\n // Navigation history for back/forward\n const [historyStack, setHistoryStack] = useState<string[]>(['']);\n const [historyIndex, setHistoryIndex] = useState(0);\n const canGoBack = historyIndex > 0;\n const canGoForward = historyIndex < historyStack.length - 1;\n // Preview state\n const preview = useFilePreview(() => browseData?.files ?? []);\n const [detailFile, setDetailFile] = useState<ManagedFile | null>(null);\n const detailQuery = useFileDetail(detailFile?.id, !!detailFile);\n const displayedDetailFile = detailQuery.data ?? detailFile;\n const detailFileLoading = detailQuery.isFetching;\n // Reset state whenever the config changes (different storage or re-open)\n useEffect(() => {\n if (config) {\n setCurrentPath('');\n setViewMode('list');\n setPage(1);\n setHistoryStack(['']);\n setHistoryIndex(0);\n setDetailFile(null);\n preview.resetPreview();\n }\n // preview is recreated per render; reset only when the storage configuration changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [config, setPage]);\n const navigateTo = (path: string, pushHistory = true) => {\n setCurrentPath(path);\n setPage(1);\n if (pushHistory) {\n setHistoryStack((prev) => {\n const next = prev.slice(0, historyIndex + 1);\n next.push(path);\n return next;\n });\n setHistoryIndex((i) => i + 1);\n }\n };\n const navigateToFolder = (folder: FolderEntry) => navigateTo(folder.path);\n const navigateToPath = (path: string) => navigateTo(path);\n const goBack = () => {\n const newIndex = historyIndex - 1;\n const path = historyStack[newIndex] ?? '';\n setHistoryIndex(newIndex);\n setCurrentPath(path);\n setPage(1);\n };\n const goForward = () => {\n const newIndex = historyIndex + 1;\n const path = historyStack[newIndex] ?? '';\n setHistoryIndex(newIndex);\n setCurrentPath(path);\n setPage(1);\n };\n // Build breadcrumb segments from currentPath\n const breadcrumbSegments = currentPath ? currentPath.split('/') : [];\n const handleDelete = async (file: ManagedFile) => {\n await deleteMutation.mutateAsync([file.id]);\n Toast.success('文件已删除');\n void browseQuery.refetch();\n };\n const handleOpenDetail = (file: ManagedFile) => {\n setDetailFile(file);\n };\n const handleCopyUrl = async (file: ManagedFile) => {\n await copyTextWithToast(getFileFullUrl(file.url), { success: '链接已复制', error: '复制失败,请手动复制' });\n };\n const listColumns: ColumnProps<ManagedFile | FolderEntry>[] = [\n {\n title: '名称',\n key: 'name',\n ellipsis: { showTitle: false },\n render: (_: unknown, record: ManagedFile | FolderEntry) => {\n if (!('id' in record)) {\n // Folder row\n return (<FileNameCell name={record.name} icon={<Folder size={15} className=\"storage-browser__folder-icon\"/>} onClick={() => navigateToFolder(record)}/>);\n }\n // 与操作列“预览”一致:仅可预览类型渲染为可点击\n const previewable = canPreviewFile(record.mimeType, record.originalName);\n return <FileNameCell name={record.originalName} mimeType={record.mimeType} onClick={previewable ? () => void preview.handlePreview(record) : undefined}/>;\n },\n },\n {\n title: '大小',\n key: 'size',\n width: 110,\n align: 'right' as const,\n render: (_: unknown, record: ManagedFile | FolderEntry) => {\n if (!('id' in record))\n return <span className=\"table-cell-placeholder\">—</span>;\n return formatBytes(record.size);\n },\n },\n {\n title: '上传时间',\n key: 'createdAt',\n width: 180,\n render: (_: unknown, record: ManagedFile | FolderEntry) => {\n if (!('id' in record))\n return <span className=\"table-cell-placeholder\">—</span>;\n return <DateTimeText value={record.createdAt}/>;\n },\n },\n createOperationColumn<ManagedFile | FolderEntry>({\n width: 180,\n desktopInlineKeys: ['open', 'preview', 'download'],\n actions: (record) => {\n if (!('id' in record)) {\n return [\n {\n key: 'open',\n label: '打开',\n onClick: () => navigateToFolder(record),\n },\n ];\n }\n return buildManagedFileActions(record, {\n preview,\n onDetail: (file) => { void handleOpenDetail(file); },\n onCopyUrl: handleCopyUrl,\n onDelete: handleDelete,\n canDelete: hasPermission('system:file:delete'),\n });\n },\n }),\n ];\n const allItems: (ManagedFile | FolderEntry)[] = [\n ...(browseData?.folders ?? []),\n ...(browseData?.files ?? []),\n ];\n const totalCount = allItems.length;\n const pagedItems = allItems.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);\n return (<>\n <SideSheet title={config ? `文件浏览 · ${config.name}` : '文件浏览'} visible={!!config} onCancel={onClose} size=\"large\" bodyStyle={{ padding: 0, display: 'flex', flexDirection: 'column' }} headerStyle={{ borderBottom: '1px solid var(--arc-antd-color-border)' }}>\n {/* Toolbar: back/forward + breadcrumb + view mode toggle */}\n <div className=\"storage-browser__toolbar\">\n <Space spacing={4} style={{ flexShrink: 0 }}>\n <Button size=\"small\" theme=\"borderless\" type=\"tertiary\" icon={<ChevronLeft size={15}/>} disabled={!canGoBack} onClick={goBack}/>\n <Button size=\"small\" theme=\"borderless\" type=\"tertiary\" icon={<ChevronRight size={15}/>} disabled={!canGoForward} onClick={goForward}/>\n </Space>\n <Breadcrumb className=\"storage-browser__breadcrumb\">\n <Breadcrumb.Item onClick={() => navigateToPath('')} style={{ cursor: currentPath ? 'pointer' : 'default', color: currentPath ? 'var(--arc-antd-color-primary)' : undefined }}>\n 根目录\n </Breadcrumb.Item>\n {breadcrumbSegments.map((seg, idx) => {\n const segPath = breadcrumbSegments.slice(0, idx + 1).join('/');\n const isLast = idx === breadcrumbSegments.length - 1;\n return (<Breadcrumb.Item key={segPath} onClick={isLast ? undefined : () => navigateToPath(segPath)} style={{ cursor: isLast ? 'default' : 'pointer', color: isLast ? undefined : 'var(--arc-antd-color-primary)' }}>\n {seg}\n </Breadcrumb.Item>);\n })}\n </Breadcrumb>\n <Space spacing={0}>\n <Button size=\"small\" theme={viewMode === 'list' ? 'solid' : 'light'} type={viewMode === 'list' ? 'primary' : 'tertiary'} icon={<ListIcon size={14}/>} style={{ borderRadius: '4px 0 0 4px' }} onClick={() => setViewMode('list')}/>\n <Button size=\"small\" theme={viewMode === 'grid' ? 'solid' : 'light'} type={viewMode === 'grid' ? 'primary' : 'tertiary'} icon={<LayoutGrid size={14}/>} style={{ borderRadius: '0 4px 4px 0' }} onClick={() => setViewMode('grid')}/>\n </Space>\n </div>\n\n {/* Content area */}\n <div className=\"storage-browser__content\">\n <Spin spinning={loading} size=\"large\" wrapperClassName=\"storage-browser__spin\">\n {!loading && totalCount === 0 && (<div className=\"storage-browser__empty\">当前目录为空</div>)}\n\n {viewMode === 'list' && totalCount > 0 && (<ConfigurableTable bordered columns={listColumns} dataSource={pagedItems} rowKey={(record) => {\n if (!record)\n return '';\n return 'id' in record ? `file-${record.id}` : `folder-${record.path}`;\n }} loading={false} onRefresh={() => void browseQuery.refetch()} refreshLoading={loading} size=\"small\" pagination={false} onRow={(record) => {\n if (record && !('id' in record)) {\n return {\n onClick: () => navigateToFolder(record),\n style: { cursor: 'pointer' },\n };\n }\n return {};\n }}/>)}\n\n {viewMode === 'grid' && totalCount > 0 && (() => {\n const pagedFolders = pagedItems.filter((r): r is FolderEntry => !('id' in r));\n const pagedFiles = pagedItems.filter((r): r is ManagedFile => 'id' in r);\n return (<div className=\"storage-browser__grid-wrap\">\n {pagedFolders.length > 0 && (<div className=\"storage-browser__grid-section\">\n <div className=\"storage-browser__grid-section-label\">文件夹</div>\n <div className=\"storage-browser__folder-grid\">\n {pagedFolders.map((folder) => (<button key={folder.path} type=\"button\" className=\"storage-browser__folder-card\" onClick={() => navigateToFolder(folder)}>\n <Folder size={32} className=\"storage-browser__folder-card-icon\"/>\n <Tooltip content={folder.name} position=\"top\">\n <span className=\"storage-browser__folder-card-name\">{folder.name}</span>\n </Tooltip>\n </button>))}\n </div>\n </div>)}\n {pagedFiles.length > 0 && (<div className=\"storage-browser__grid-section\">\n {pagedFolders.length > 0 && (<div className=\"storage-browser__grid-section-label\">文件</div>)}\n <div className=\"storage-browser__file-grid\">\n {pagedFiles.map((file) => (<FileGridCard key={file.id} file={file} selected={false} canSelect={false} onSelect={() => { }} onPreview={preview.handlePreview} onDownload={preview.handleDownload} onDelete={handleDelete} onDetail={handleOpenDetail} onCopyUrl={handleCopyUrl} canDelete={hasPermission('system:file:delete')} previewLoading={preview.previewLoadingId === file.id}/>))}\n </div>\n </div>)}\n </div>);\n })()}\n </Spin>\n </div>\n\n {/* Footer: summary + pagination */}\n <div className=\"storage-browser__footer\">\n <Text type=\"secondary\" size=\"small\">\n {(browseData?.folders.length ?? 0) > 0 && `${browseData!.folders.length} 个文件夹`}\n {(browseData?.folders.length ?? 0) > 0 && (browseData?.files.length ?? 0) > 0 && ','}\n {(browseData?.files.length ?? 0) > 0 && `${browseData!.files.length} 个文件`}\n {totalCount === 0 && !loading && '空目录'}\n </Text>\n {totalCount > 0 && (<Pagination currentPage={page} pageSize={PAGE_SIZE} total={totalCount} onPageChange={setPage} size=\"small\" hoverShowPageSelect/>)}\n </div>\n </SideSheet>\n\n {/* Image / non-image file preview */}\n <FilePreviewLayer preview={preview}/>\n\n {/* File detail modal */}\n <FileDetailModal visible={!!detailFile} file={displayedDetailFile} loading={detailFileLoading} resolveUrl={(file) => getFileFullUrl(file.url)} onCopyUrl={(file) => void handleCopyUrl(file)} onClose={() => setDetailFile(null)}/>\n </>);\n}\n","import { IS_GO_FOUNDATION } from \"@fudanda/react-core/lib/foundation-mode\";\nimport { FormPasswordInput } from \"@/components/PasswordInput\";\nimport { useEffect, useState } from 'react';\nimport { Button, Col, Form, Radio, Row, Select, Switch, Tag, Toast, Typography } from \"@/ui\";\nimport { PlugZap } from 'lucide-react';\nimport { type CreateFileStorageConfigInput, type FileObjectAcl, type FileStorageConfig, type FileUrlStrategy } from '@fudanda/shared/platform';\nimport { fileStorageConfigContract } from '@fudanda/react-core/lib/native-operations';\nimport { FILE_OBJECT_ACL_LABELS, FILE_OBJECT_ACL_SUPPORT, FILE_STORAGE_PROVIDER_LABELS, FILE_STORAGE_PROVIDER_OPTIONS, FILE_URL_STRATEGY_LABELS, FILE_URL_STRATEGY_OPTIONS, PRESIGNED_EXPIRY_DEFAULT_SECONDS, PRESIGNED_EXPIRY_MAX_SECONDS, PRESIGNED_EXPIRY_MIN_SECONDS } from '@fudanda/shared/platform';\nimport type { ColumnProps } from \"@/ui\";\nimport { usePermission } from \"@fudanda/react-core/hooks/usePermission\";\nimport { useEditModal, formSaveMutation } from \"@/hooks/useEditModal\";\nimport ExportButton from \"@/components/ExportButton\";\nimport { dateTimeColumn, EMPTY_PLACEHOLDER, renderEllipsis } from \"@/utils/table-columns\";\nimport ConfigurableTable from \"@/components/ConfigurableTable\";\nimport { buildTableSearch, useStatusToggle, useCrudOperationColumn } from \"@/components/list-page/index\";\nimport { abortSubmit } from \"@fudanda/react-core/lib/abort-submit\";\nimport { runSubmitAction } from '../../../utils/submit-action';\nimport StorageFileBrowser from \"@/pages/system/file-configs/StorageFileBrowser\";\nimport { useDeleteFileStorageConfigs, useFileStorageConfigDetail, useFileStorageConfigList, useSaveFileStorageConfig, useSetDefaultFileStorageConfig, useTestFileStorageConfig } from \"@fudanda/react-core/hooks/queries/file-storage-configs\";\nimport { CreateButton } from \"@/components/toolbar-controls\";\nimport { useListPage } from \"@/hooks/useListPage\";\nimport { EditFormSheet } from \"@/components/EditFormModal\";\nimport \"@/pages/system/file-configs/FileStorageConfigsPanel.css\";\nconst { Text } = Typography;\ntype FileStorageConfigFormValues = Partial<Omit<CreateFileStorageConfigInput, 'presignedExpirySeconds'>> & { presignedExpirySeconds?: number };\ntype FileStorageProvider = FileStorageConfig['provider'];\n/** 支持对象级读写权限(canned ACL)的 provider */\nconst OBJECT_ACL_PROVIDERS: readonly FileStorageProvider[] = ['s3'];\nfunction normalizeOptional(value?: string): string {\n return value?.trim() ?? '';\n}\n/** 各 provider 公共的基础字段(objectAcl 与专属凭据字段由各分支自行补充) */\nfunction buildPayload(provider: FileStorageProvider, isDefault: boolean, values: FileStorageConfigFormValues): CreateFileStorageConfigInput {\n return fileStorageConfigContract.create.input.shape.body.parse({\n name: values.name?.trim() ?? '', status: values.status ?? 'enabled', isDefault,\n basePath: normalizeOptional(values.basePath), objectAcl: 'default', urlStrategy: 'proxy', publicBaseUrl: '',\n presignedExpirySeconds: PRESIGNED_EXPIRY_DEFAULT_SECONDS, provider,\n remark: normalizeOptional(values.remark),\n ...(provider === 'local' ? { localRootPath: normalizeOptional(values.localRootPath) } : {\n s3Region: normalizeOptional(values.s3Region), s3Endpoint: normalizeOptional(values.s3Endpoint),\n s3Bucket: normalizeOptional(values.s3Bucket), s3AccessKeyId: normalizeOptional(values.s3AccessKeyId),\n s3SecretAccessKey: normalizeOptional(values.s3SecretAccessKey), s3ForcePathStyle: values.s3ForcePathStyle ?? false,\n }),\n });\n}\nfunction getStorageSummary(config: FileStorageConfig) {\n return config.provider === 'local' ? config.localRootPath || EMPTY_PLACEHOLDER : [config.s3Bucket, config.s3Region].filter(Boolean).join(' / ') || EMPTY_PLACEHOLDER;\n}\nexport default function FileStorageConfigsPanel() {\n const { hasPermission } = usePermission();\n const page = useListPage({\n contract: fileStorageConfigContract,\n useList: useFileStorageConfigList,\n });\n const { tableProps, filterQuery } = page;\n const [formProvider, setFormProvider] = useState<FileStorageProvider>('local');\n const [formIsDefault, setFormIsDefault] = useState(false);\n const [browsingConfig, setBrowsingConfig] = useState<FileStorageConfig | null>(null);\n const saveMutation = useSaveFileStorageConfig();\n const statusMutation = useSaveFileStorageConfig();\n const modal = useEditModal<FileStorageConfig, FileStorageConfigFormValues, CreateFileStorageConfigInput>({\n entityName: '文件配置',\n save: formSaveMutation(saveMutation, { create: fileStorageConfigContract.create.input.shape.body, update: fileStorageConfigContract.update.input.shape.body }),\n useDetail: useFileStorageConfigDetail,\n defaults: {\n name: '',\n provider: 'local',\n status: 'enabled',\n isDefault: false,\n basePath: '',\n objectAcl: 'default',\n urlStrategy: 'proxy',\n publicBaseUrl: '',\n presignedExpirySeconds: PRESIGNED_EXPIRY_DEFAULT_SECONDS,\n localRootPath: 'storage/local',\n remark: '',\n },\n // 密钥字段为 write-only:接口不返回,表单一律以空值起始,留空提交即沿用原值\n toValues: (config) => ({\n name: config.name, status: config.status, isDefault: config.isDefault, provider: config.provider,\n basePath: config.basePath, objectAcl: 'default', urlStrategy: 'proxy', publicBaseUrl: '',\n presignedExpirySeconds: PRESIGNED_EXPIRY_DEFAULT_SECONDS, localRootPath: config.localRootPath ?? '',\n s3Region: config.s3Region ?? '', s3Endpoint: config.s3Endpoint ?? '', s3Bucket: config.s3Bucket ?? '',\n s3AccessKeyId: config.s3AccessKeyId ?? '', s3SecretAccessKey: '', s3ForcePathStyle: config.s3ForcePathStyle ?? false,\n remark: config.remark ?? '',\n }),\n beforeSave: (values) => buildPayload(formProvider, formIsDefault, values),\n successMessage: ({ isEdit }) => isEdit ? '文件服务配置已更新' : '文件服务配置已创建',\n labelWidth: 150,\n });\n const deleteMutation = useDeleteFileStorageConfigs();\n const setDefaultMutation = useSetDefaultFileStorageConfig();\n const testMutation = useTestFileStorageConfig();\n const modalTestLoading = testMutation.isPending && !testMutation.variables?.id;\n const testingConfigId = testMutation.isPending ? (testMutation.variables?.id ?? null) : null;\n useEffect(() => {\n if (!modal.editing)\n return;\n setFormProvider(modal.editing.provider);\n setFormIsDefault(modal.editing.isDefault);\n }, [modal.editing]);\n const openCreate = () => {\n setFormProvider('local');\n setFormIsDefault(false);\n modal.openCreate();\n };\n const openEdit = (config: FileStorageConfig) => {\n setFormProvider(config.provider);\n setFormIsDefault(config.isDefault);\n modal.openEdit(config);\n };\n const handleModalTest = async () => {\n let values;\n try {\n values = await modal.formApi.current!.validate();\n }\n catch {\n return;\n }\n if (!values)\n return;\n const payload = buildPayload(formProvider, formIsDefault, values);\n await testMutation.mutateAsync(modal.editing ? { id: modal.editing.id, values: payload } : { values: payload });\n Toast.success('存储连接测试通过');\n };\n const handleSetDefault = async (config: FileStorageConfig) => {\n await setDefaultMutation.mutateAsync({ params: { id: config.id } });\n Toast.success('默认文件服务已更新');\n };\n const handleTestSaved = async (config: FileStorageConfig) => {\n await testMutation.mutateAsync({ id: config.id, values: {} });\n Toast.success('存储连接测试通过');\n };\n const status = useStatusToggle<FileStorageConfig>({\n toggle: async (config, enabled) => {\n if (!enabled && config.isDefault) {\n Toast.warning('默认配置不能禁用,请先将其他配置设为默认');\n abortSubmit('default-config-cannot-be-disabled');\n }\n await statusMutation.mutateAsync({ id: config.id, values: buildPayload(config.provider, config.isDefault, { ...config, status: enabled ? 'enabled' : 'disabled' }) });\n },\n confirmDisable: (config) => {\n if (config.isDefault) {\n return null;\n }\n return {\n danger: true,\n title: `确认禁用「${config.name}」?`,\n okText: '确认禁用',\n };\n },\n disabled: !hasPermission('system:file:config:update'),\n messages: { disabled: '已禁用' },\n });\n const operationColumn = useCrudOperationColumn<FileStorageConfig>({\n permission: 'system:file:config',\n edit: openEdit,\n remove: deleteMutation,\n title: '确认删除此文件服务配置?',\n content: '若已绑定文件记录,后端会阻止删除。',\n successMessage: '文件服务配置已删除',\n disabled: { remove: (record) => record.isDefault },\n extra: (record) => [\n {\n key: 'browse',\n label: '浏览',\n hidden: !hasPermission('system:file:list'),\n onClick: () => setBrowsingConfig(record),\n },\n {\n key: 'default',\n label: '设为默认',\n hidden: !hasPermission('system:file:config:default'),\n disabled: record.isDefault || record.status !== 'enabled',\n onClick: () => handleSetDefault(record),\n },\n ],\n extraBetween: (record) => [\n {\n key: 'test',\n label: '测试连接',\n loading: testingConfigId === record.id,\n hidden: !hasPermission('system:file:config'),\n onClick: () => { void handleTestSaved(record); },\n },\n ],\n width: 180,\n desktopInlineKeys: ['browse', 'edit'],\n });\n const columns: ColumnProps<FileStorageConfig>[] = [\n {\n title: '配置名称',\n dataIndex: 'name',\n minWidth: 180,\n render: renderEllipsis,\n },\n {\n title: '存储类型',\n dataIndex: 'provider',\n width: 120,\n render: (provider: FileStorageProvider) => {\n // 文案统一来自 @fudanda/shared;Tag 色为本页特化\n const colorMap: Record<FileStorageProvider, 'blue' | 'orange' | 'purple' | 'teal' | 'red' | 'cyan' | 'indigo' | 'violet' | 'green'> = {\n local: 'blue', s3: 'purple',\n };\n return <Tag color={colorMap[provider]} size=\"small\">{FILE_STORAGE_PROVIDER_LABELS[provider]}</Tag>;\n },\n },\n {\n title: '默认服务',\n dataIndex: 'isDefault',\n width: 110,\n align: 'center',\n render: (isDefault: boolean) => isDefault ? <Tag color=\"green\" size=\"small\">默认</Tag> : <span className=\"table-cell-placeholder\">—</span>,\n },\n {\n title: '存储信息',\n key: 'storageSummary',\n dataIndex: 'storageSummary',\n width: 180,\n render: (_: unknown, record: FileStorageConfig) => {\n const labelMap = { local: '目录', s3: 'Bucket' };\n const label = labelMap[record.provider] ?? 'Bucket';\n const summary = getStorageSummary(record);\n return renderEllipsis(`${label}: ${summary}`);\n },\n },\n {\n title: '基础路径',\n dataIndex: 'basePath',\n width: 160,\n render: (value?: string) => renderEllipsis(value),\n },\n {\n title: '读写权限',\n dataIndex: 'objectAcl',\n width: 120,\n align: 'center',\n render: (value: FileObjectAcl | undefined, record: FileStorageConfig) => {\n if (!OBJECT_ACL_PROVIDERS.includes(record.provider))\n return <span className=\"table-cell-placeholder\">—</span>;\n const colorMap: Record<FileObjectAcl, 'grey' | 'blue' | 'orange' | 'red'> = {\n 'default': 'grey',\n };\n const acl = value ?? 'default';\n return <Tag color={colorMap[acl]} size=\"small\">{FILE_OBJECT_ACL_LABELS[acl]}</Tag>;\n },\n },\n {\n title: '访问策略',\n dataIndex: 'urlStrategy',\n width: 120,\n align: 'center',\n render: (value: FileUrlStrategy | undefined) => {\n const strategy = value ?? 'proxy';\n const colorMap: Record<FileUrlStrategy, 'grey' | 'green' | 'purple'> = {\n proxy: 'grey',\n };\n return <Tag color={colorMap[strategy]} size=\"small\">{FILE_URL_STRATEGY_LABELS[strategy]}</Tag>;\n },\n },\n dateTimeColumn('更新时间', 'updatedAt'),\n status.column(),\n operationColumn,\n ];\n return (<div className=\"storage-configs-panel\">\n\n <div className=\"storage-configs-tip\" style={{ marginBottom: 0, marginTop: -4 }}>\n <Text type=\"secondary\">当前支持多文件服务配置,但上传时会优先走\"默认文件服务\"。切换默认服务不会影响历史文件记录。</Text>\n </div>\n\n <ConfigurableTable<FileStorageConfig> searchConfig={buildTableSearch({\n page: (page),\n filters: (['status', ['startTime', 'endTime']]),\n create: (<CreateButton permission=\"system:file:config:create\" onClick={openCreate}/>),\n actions: ((<ExportButton entity=\"system.file-storage-configs\" query={filterQuery}/>)),\n filterTitle: \"文件配置筛选\",\n actionTitle: \"文件配置操作\"\n })} columns={columns} {...tableProps}/>\n\n <EditFormSheet modal={modal} width={780} footer={(<div style={{ display: 'flex', justifyContent: 'space-between', gap: 8 }}> <Button icon={<PlugZap size={14}/>} loading={modalTestLoading} disabled={modal.detailLoading} onClick={() => void handleModalTest()}> 测试连接 </Button> <div style={{ display: 'flex', gap: 8 }}> <Button type=\"tertiary\" onClick={modal.close}>取消</Button> <Button type=\"primary\" theme=\"solid\" loading={modal.modalProps.okButtonProps.loading} disabled={modal.modalProps.okButtonProps.disabled} onClick={() => void runSubmitAction(modal.modalProps.onOk)}> 保存 </Button> </div> </div>)}>\n <Form.Section text=\"基础信息\">\n <Row gutter={16}>\n <Col span={12}>\n <Form.Input field=\"name\" label=\"配置名称\" placeholder=\"请输入配置名称\" rules={[{ required: true, message: '请输入配置名称' }]}/>\n </Col>\n <Col span={12}>\n <Form.Select field=\"provider\" label=\"存储类型\" style={{ width: '100%' }} optionList={FILE_STORAGE_PROVIDER_OPTIONS.filter(option => ['local', 's3'].includes(option.value))} onChange={(value) => {\n if (value !== 'local' && value !== 's3') return;\n const next = value;\n setFormProvider(next);\n const currentAcl = modal.formApi.current?.getValue('objectAcl') as FileObjectAcl | undefined;\n if (currentAcl && !(FILE_OBJECT_ACL_SUPPORT[next] ?? []).includes(currentAcl)) {\n modal.formApi.current?.setValue('objectAcl', 'default');\n }\n }} placeholder=\"请选择存储类型\"/>\n </Col>\n </Row>\n <Row gutter={16}>\n <Col span={12}>\n <Form.Select field=\"status\" label=\"状态\" style={{ width: '100%' }} placeholder=\"请选择状态\">\n <Select.Option value=\"enabled\">启用</Select.Option>\n <Select.Option value=\"disabled\">禁用</Select.Option>\n </Form.Select>\n </Col>\n {(formProvider === 's3') && <Col span={12}>\n <Form.Input field=\"basePath\" label=\"基础路径\" placeholder=\"例如 uploads / images\"/>\n </Col>}\n </Row>\n <Form.Slot label=\"设为默认服务\">\n <Switch checked={formIsDefault} onChange={setFormIsDefault}/>\n </Form.Slot>\n <Row gutter={16}>\n <Col span={24}>\n <Form.Input field=\"remark\" label=\"备注\" placeholder=\"选填,说明该文件服务的用途\"/>\n </Col>\n </Row>\n </Form.Section>\n\n <Form.Section text={`${FILE_STORAGE_PROVIDER_LABELS[formProvider]} 连接配置`}>\n {formProvider === 'local' && (<Row gutter={16}>\n <Col span={24}>\n <Form.Input field=\"localRootPath\" label=\"存储目录\" placeholder=\"例如 storage/local 或 D:/uploads\" rules={[{ required: true, message: '请输入本地磁盘存储目录' }]}/>\n </Col>\n </Row>)}\n\n\n\n {formProvider === 's3' && (<>\n <Row gutter={16}>\n <Col span={12}>\n <Form.Input field=\"s3Region\" label=\"Region\" placeholder=\"请输入 S3 Region\" rules={[{ required: true, message: '请输入 S3 Region' }]}/>\n </Col>\n <Col span={12}>\n <Form.Input field=\"s3Bucket\" label=\"Bucket\" placeholder=\"请输入 S3 Bucket\" rules={[{ required: true, message: '请输入 S3 Bucket' }]}/>\n </Col>\n </Row>\n <Row gutter={16}>\n <Col span={24}>\n <Form.Input field=\"s3Endpoint\" label=\"Endpoint\" placeholder=\"可选,兼容 S3 自定义存储\"/>\n </Col>\n </Row>\n <Row gutter={16}>\n <Col span={24}>\n <Form.Input field=\"s3AccessKeyId\" label=\"Access Key ID\" placeholder=\"请输入 Access Key ID\" rules={[{ required: true, message: '请输入 Access Key ID' }]}/>\n </Col>\n </Row>\n <Row gutter={16}>\n <Col span={24}>\n <FormPasswordInput field=\"s3SecretAccessKey\" label=\"Secret Access Key\" placeholder={modal.isEdit ? '留空表示不修改' : '请输入 Secret Access Key'} rules={modal.isEdit ? [] : [{ required: true, message: '请输入 Secret Access Key' }]}/>\n </Col>\n </Row>\n <Form.Checkbox field=\"s3ForcePathStyle\" noLabel>强制路径样式(MinIO / Ceph 等兼容当需开启)</Form.Checkbox>\n </>)}\n\n\n\n\n\n\n\n\n\n\n\n\n </Form.Section>\n\n <Form.Section text=\"访问与权限\">\n {OBJECT_ACL_PROVIDERS.includes(formProvider) && (<Form.RadioGroup field=\"objectAcl\" label=\"读写权限\" type=\"button\" extraText={formProvider === 's3'\n ? '上传文件将按此权限设置对象 ACL。注意:AWS S3 新建桶默认禁用 ACL(Bucket owner enforced),启用前请先在桶设置中开启;MinIO / Cloudflare R2 不支持对象 ACL,请保持「继承 Bucket」。'\n : '上传文件将按此权限设置对象 ACL;「继承 Bucket」表示不单独指定、跟随 Bucket 权限。公共读 / 公共读写存在数据泄露风险,请谨慎选择。'}>\n {(['default'] as const).map((acl) => (<Radio key={acl} value={acl}>{FILE_OBJECT_ACL_LABELS[acl]}</Radio>))}\n </Form.RadioGroup>)}\n\n <Row gutter={16}>\n <Col span={24}>\n <Form.Select field=\"urlStrategy\" disabled={IS_GO_FOUNDATION} label=\"访问策略\" style={{ width: '100%' }} extraText=\"代理:文件流量经过服务端(兜底);公开直链:返回永久直连地址,要求对象可公开读;临时签名:按需签发限时直连地址,适合私有文件(本地磁盘 / SFTP 不支持)\">\n {(FILE_URL_STRATEGY_OPTIONS.filter(option => option.value === 'proxy')).map((opt) => (<Select.Option key={opt.value} value={opt.value}>{opt.label}</Select.Option>))}\n </Form.Select>\n </Col>\n </Row>\n <Row gutter={16}>\n <Col span={24}>\n <Form.InputNumber field=\"presignedExpirySeconds\" disabled={IS_GO_FOUNDATION} label=\"签名有效期(秒)\" style={{ width: '100%' }} min={PRESIGNED_EXPIRY_MIN_SECONDS} max={PRESIGNED_EXPIRY_MAX_SECONDS} extraText=\"仅临时签名策略生效;修改只影响新签发的链接\"/>\n </Col>\n </Row>\n <Row gutter={16}>\n <Col span={24}>\n <Form.Input field=\"publicBaseUrl\" disabled={IS_GO_FOUNDATION} label=\"访问域名(CDN)\" placeholder=\"可选,例如 https://cdn.example.com,公开直链优先使用该域名\"/>\n </Col>\n </Row>\n </Form.Section>\n </EditFormSheet>\n\n <StorageFileBrowser config={browsingConfig} onClose={() => setBrowsingConfig(null)}/>\n </div>);\n}\n","import { filesList, filesBatchDownload } from '@fudanda/shared/api/files';\nimport { goTransport } from '@fudanda/react-core/lib/go-transport';\nimport { useEffect, useRef, useState } from 'react';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { AppModal } from \"@/components/AppModal\";\nimport { FileDetailModal } from \"@/components/FileDetailModal\";\nimport { Button, Checkbox, List, Pagination, Progress, Space, Toast, Tooltip, Typography } from \"@/ui\";\nimport { Plus, FolderDown, LayoutGrid, List as ListIcon, CheckCircle2, XCircle, X } from 'lucide-react';\nimport type { ManagedFile } from '@fudanda/shared/platform';\nimport { FILE_STORAGE_PROVIDER_OPTIONS, FILE_TYPE_FILTER_OPTIONS } from '@fudanda/shared/platform';\n\n\nimport type { ColumnProps } from \"@/ui\";\nimport { formatDateTimeRangeValuesForApi } from \"@fudanda/react-core/utils/date\";\nimport { downloadBlob } from \"@fudanda/react-core/utils/download\";\nimport { getFileTypeIcon, fetchManagedFileBlob, getFileFullUrl, canPreviewFile } from \"@/utils/file-utils\";\nimport { buildManagedFileActions } from \"@/utils/managed-file-actions\";\nimport { chunkedUpload, CHUNKED_UPLOAD_CANCELLED } from \"@fudanda/react-core/utils/chunked-upload\";\nimport { FilePreviewLayer } from \"@/components/FilePreviewLayer\";\nimport { useFilePreview } from \"@/hooks/useFilePreview\";\nimport { FileGridCard } from \"@/pages/system/files/components/FileGridCard\";\nimport { FileNameCell } from \"@/components/FileNameCell\";\nimport { usePermission } from \"@fudanda/react-core/hooks/usePermission\";\nimport { dateTimeColumn, renderEllipsis } from \"@/utils/table-columns\";\nimport { usePreferences } from \"@fudanda/react-core/hooks/usePreferences\";\nimport { usePagination } from \"@fudanda/react-core/hooks/usePagination\";\nimport ConfigurableTable from \"@/components/ConfigurableTable\";\nimport { createOperationColumn } from \"@/components/ResponsiveTableActions\";\nimport { useDefaultFileStorageConfig } from \"@fudanda/react-core/hooks/queries/file-storage-configs\";\nimport { fileKeys, invalidateAfterFilesAdded, useChunkUploadThreshold, useDeleteFiles, useFileDetail, useFileList, useUploadFile } from \"@fudanda/react-core/hooks/queries/files\";\nimport { useListSearch } from \"@fudanda/react-core/hooks/useListSearch\";\nimport { BatchDeleteButton } from \"@/components/toolbar-controls\";\nimport { confirmAndDelete, buildTableSearch, TableSearchPanel, listTableProps } from \"@/components/list-page/index\";\nimport { copyTextWithToast } from \"@/utils/clipboard\";\nimport \"@/pages/system/files/FilesPage.css\";\nimport { formatBytes } from '@fudanda/shared/core';\nimport { DateRangeFilter, FilterSelect, KeywordInput } from \"@/components/search-filters\";\nimport { useFilterQuery } from \"@fudanda/react-core/hooks/useFilterQuery\";\nconst { Text } = Typography;\ninterface UploadItem {\n uid: string;\n name: string;\n size: number;\n progress: number;\n status: 'pending' | 'uploading' | 'success' | 'error' | 'cancelled';\n errorMsg?: string;\n}\nconst UPLOAD_ACTIVE_STATUSES: ReadonlyArray<UploadItem['status']> = ['pending', 'uploading'];\nconst isUploadFinished = (item: UploadItem) => !UPLOAD_ACTIVE_STATUSES.includes(item.status);\nconst FILE_GRID_PAGE_SIZE = 60;\nconst FILE_GRID_PAGE_SIZE_OPTIONS = [60, 120, 240];\nfunction getProgressStroke(status: UploadItem['status']): string | undefined {\n if (status === 'success')\n return 'var(--arc-antd-color-success)';\n if (status === 'error')\n return 'var(--arc-antd-color-danger)';\n if (status === 'cancelled')\n return 'var(--arc-antd-color-disabled-text)';\n return undefined;\n}\nfunction uploadSingleFile(file: File, uid: string, signal: AbortSignal, chunkThreshold: number, setItems: React.Dispatch<React.SetStateAction<UploadItem[]>>, uploadFile: (files: Blob[], onProgress: (percent: number) => void, signal: AbortSignal) => Promise<unknown>) {\n const updateItem = (updater: (item: UploadItem) => UploadItem) => setItems(prev => prev.map(item => item.uid === uid ? updater(item) : item));\n const onDone = () => updateItem(item => ({ ...item, progress: 100, status: 'success' }));\n const onFail = (err: unknown) => updateItem(item => signal.aborted\n ? { ...item, status: 'cancelled' }\n : { ...item, status: 'error', errorMsg: err instanceof Error ? err.message : '上传失败' });\n updateItem(item => ({ ...item, status: 'uploading' }));\n // 超过运行时设置的阈值走分片上传 + 断点续传\n if (file.size > chunkThreshold) {\n chunkedUpload(file, {\n signal,\n onProgress: (percent) => updateItem(item => ({ ...item, progress: percent })),\n }).then(onDone).catch(onFail);\n return;\n }\n void uploadFile([file], (percent) => updateItem(item => ({ ...item, progress: percent })), signal)\n .then(onDone).catch(onFail);\n}\n/** 加载图片并返回其分辨率,失败时返回 null;优先用 directUrl 直挂(免 blob fetch/CORS) */\nasync function loadImageResolution(file: {\n url: string;\n directUrl?: string | null;\n}): Promise<{\n width: number;\n height: number;\n} | null> {\n try {\n let src: string;\n const external = file.directUrl ?? (/^https?:\\/\\//.test(file.url) ? file.url : null);\n if (external) {\n src = external;\n }\n else {\n const blob = await fetchManagedFileBlob(file.url);\n src = URL.createObjectURL(blob);\n }\n return await new Promise((resolve) => {\n const img = new Image();\n const cleanup = () => {\n if (!external)\n URL.revokeObjectURL(src);\n };\n img.onload = () => { cleanup(); resolve({ width: img.naturalWidth, height: img.naturalHeight }); };\n img.onerror = () => { cleanup(); resolve(null); };\n img.src = src;\n });\n }\n catch {\n return null;\n }\n}\nexport default function FileListPanel({ active }: { active: boolean }) {\n const queryClient = useQueryClient();\n const { hasPermission } = usePermission();\n const { preferences, setPreferences, canEditPreference } = usePreferences();\n interface SearchParams {\n keyword: string;\n provider?: string;\n fileType?: string;\n timeRange: [\n Date,\n Date\n ] | null;\n }\n const defaultSearchParams: SearchParams = { keyword: '', provider: undefined, fileType: undefined, timeRange: null };\n const fileInputRef = useRef<HTMLInputElement | null>(null);\n const [uploadItems, setUploadItems] = useState<UploadItem[]>([]);\n const [uploadProgressVisible, setUploadProgressVisible] = useState(false);\n const uploadControllersRef = useRef(new Map<string, AbortController>());\n const { bind, bindKeyword, submittedParams, handleSearch, handleReset, } = useListSearch<SearchParams>({ defaults: defaultSearchParams, listKey: fileKeys.lists });\n const viewMode = preferences.filesViewMode ?? 'list';\n const listPagination = usePagination({ resetKey: viewMode });\n const gridPagination = usePagination({\n pageSize: FILE_GRID_PAGE_SIZE,\n pageSizeOpts: FILE_GRID_PAGE_SIZE_OPTIONS,\n resetKey: viewMode,\n });\n const { page, pageSize, setPage, setPageSize, buildPagination } = viewMode === 'grid' ? gridPagination : listPagination;\n const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);\n const [batchDownloadLoading, setBatchDownloadLoading] = useState(false);\n const [detailFile, setDetailFile] = useState<ManagedFile | null>(null);\n const [imageResolution, setImageResolution] = useState<{\n width: number;\n height: number;\n } | null>(null);\n const defaultConfigQuery = useDefaultFileStorageConfig();\n const defaultConfig = defaultConfigQuery.data?.config ?? null;\n // 已提交筛选 → 契约查询参数:只映射一次\n const [startDate, endDate] = formatDateTimeRangeValuesForApi(submittedParams.timeRange);\n const filterQuery = useFilterQuery({\n keyword: submittedParams.keyword,\n provider: filesList.query.shape.provider.safeParse(submittedParams.provider).data,\n fileType: filesList.query.shape.fileType.safeParse(submittedParams.fileType).data,\n startDate, endDate,\n });\n const listQuery = useFileList({ page, pageSize, ...filterQuery }, active);\n const data = listQuery.data ?? null;\n const preview = useFilePreview(() => data?.list ?? []);\n const uploadFileMutation = useUploadFile();\n const chunkThreshold = useChunkUploadThreshold();\n const deleteMutation = useDeleteFiles();\n const batchDeleteMutation = useDeleteFiles();\n const detailQuery = useFileDetail(detailFile?.id, !!detailFile);\n const displayedDetailFile = detailQuery.data ?? detailFile;\n const detailFileLoading = detailQuery.isFetching;\n const toggleViewMode = (mode: 'list' | 'grid') => {\n if (!canEditPreference('filesViewMode'))\n return;\n setPreferences({ filesViewMode: mode });\n };\n const handleGridSelect = (id: string, checked: boolean) => {\n setSelectedRowKeys((prev) => checked ? [...prev, id] : prev.filter((k) => k !== id));\n };\n const handleGridSelectAll = (checked: boolean) => {\n const currentPageIds = (data?.list ?? []).map((f) => f.id);\n if (checked) {\n setSelectedRowKeys((prev) => {\n const next = [...prev];\n for (const id of currentPageIds) {\n if (!next.includes(id))\n next.push(id);\n }\n return next;\n });\n }\n else {\n setSelectedRowKeys((prev) => prev.filter((k) => !currentPageIds.includes(k)));\n }\n };\n const handleOpenDetail = (file: ManagedFile) => {\n setDetailFile(file);\n setImageResolution(null);\n };\n useEffect(() => {\n const file = detailQuery.data;\n if (file?.mimeType?.startsWith('image/')) {\n void loadImageResolution(file).then((r) => {\n if (r)\n setImageResolution(r);\n });\n }\n }, [detailQuery.data]);\n useEffect(() => {\n if (uploadProgressVisible && uploadItems.length > 0 && uploadItems.every(isUploadFinished)) {\n const successCount = uploadItems.filter(item => item.status === 'success').length;\n const timer = setTimeout(() => {\n setUploadProgressVisible(false);\n if (successCount > 0) {\n Toast.success(successCount > 1 ? `成功上传 ${successCount} 个文件` : '文件上传成功');\n setPage(1);\n invalidateAfterFilesAdded(queryClient);\n }\n }, 1000);\n return () => clearTimeout(timer);\n }\n }, [uploadItems, uploadProgressVisible, queryClient, setPage]);\n const handlePickFile = () => {\n fileInputRef.current?.click();\n };\n const handleUpload = (event: React.ChangeEvent<HTMLInputElement>) => {\n const files = Array.from(event.target.files ?? []);\n event.target.value = '';\n if (files.length === 0)\n return;\n const items: UploadItem[] = files.map((f, i) => ({ uid: `${f.name}-${Date.now()}-${i}`, name: f.name, size: f.size, progress: 0, status: 'pending' as const }));\n uploadControllersRef.current.clear();\n setUploadItems(items);\n setUploadProgressVisible(true);\n for (const [i, file] of files.entries()) {\n const controller = new AbortController();\n uploadControllersRef.current.set(items[i].uid, controller);\n uploadSingleFile(file, items[i].uid, controller.signal, chunkThreshold, setUploadItems, (files, onProgress, signal) => uploadFileMutation.mutateAsync({ files, onProgress, signal }));\n }\n };\n /** 用户显式取消:带原因中止,分片上传据此通知服务端释放会话,不再续传 */\n const handleCancelUpload = (uid: string) => {\n uploadControllersRef.current.get(uid)?.abort(CHUNKED_UPLOAD_CANCELLED);\n };\n const handleDelete = async (file: ManagedFile) => {\n await deleteMutation.mutateAsync([file.id]);\n Toast.success('文件已删除');\n };\n const handleBatchDelete = () => {\n confirmAndDelete({\n title: `确认删除选中的 ${selectedRowKeys.length} 个文件?`,\n content: '删除后将同步尝试删除实际存储对象,无法恢复。',\n run: () => batchDeleteMutation.mutateAsync(selectedRowKeys),\n successMessage: '批量删除成功',\n onDeleted: () => setSelectedRowKeys([]),\n });\n };\n const handleBatchDownload = async () => {\n if (selectedRowKeys.length === 0)\n return;\n setBatchDownloadLoading(true);\n try {\n const response = await goTransport.download(filesBatchDownload, { body: { ids: selectedRowKeys } });\n const blob = await response.blob();\n if (!blob)\n return;\n downloadBlob(blob, `files_${Date.now()}.zip`);\n Toast.success(`已打包 ${selectedRowKeys.length} 个文件`);\n }\n catch {\n Toast.error('批量下载失败');\n }\n finally {\n setBatchDownloadLoading(false);\n }\n };\n const handleCopyUrl = async (file: ManagedFile) => {\n await copyTextWithToast(file.directUrl ?? getFileFullUrl(file.url), { success: '链接已复制', error: '复制失败,请手动复制' });\n };\n const columns: ColumnProps<ManagedFile>[] = [\n {\n title: '文件名',\n dataIndex: 'originalName',\n minWidth: 220,\n ellipsis: { showTitle: false },\n render: (name: string, record: ManagedFile) => {\n // 与操作列“预览”一致:仅可预览类型渲染为可点击\n const previewable = canPreviewFile(record.mimeType, name);\n return (<FileNameCell name={name} mimeType={record.mimeType} onClick={previewable ? () => void preview.handlePreview(record) : undefined}/>);\n },\n },\n {\n title: '来源服务',\n dataIndex: 'storageName',\n width: 120,\n ellipsis: true,\n render: (_: string, record: ManagedFile) => renderEllipsis(record.storageName),\n },\n {\n title: 'MIME 类型',\n dataIndex: 'mimeType',\n width: 160,\n ellipsis: true,\n render: renderEllipsis,\n },\n {\n title: '大小',\n dataIndex: 'size',\n width: 100,\n align: 'right' as const,\n render: (size: number) => formatBytes(size),\n },\n dateTimeColumn('上传时间', 'createdAt'),\n {\n title: '上传人',\n dataIndex: 'uploaderName',\n width: 100,\n ellipsis: true,\n render: renderEllipsis,\n },\n createOperationColumn<ManagedFile>({\n width: 180,\n desktopInlineKeys: ['preview', 'download'],\n actions: (record) => buildManagedFileActions(record, {\n preview,\n onDetail: (file) => { void handleOpenDetail(file); },\n onCopyUrl: handleCopyUrl,\n onDelete: handleDelete,\n canDelete: hasPermission('system:file:delete'),\n }),\n }),\n ];\n const fileTableSearch = buildTableSearch({\n keyword: ((<KeywordInput placeholder=\"搜索文件名 / 对象键 / 文件服务\" {...bindKeyword('keyword')} style={{ width: 'min(280px, 100%)' }}/>)),\n filters: ((<>\n <FilterSelect placeholder=\"全部存储类型\" items={FILE_STORAGE_PROVIDER_OPTIONS.filter(option => ['local', 's3'].includes(option.value))} {...bind('provider')} width={140}/>\n <FilterSelect placeholder=\"全部文件类型\" items={FILE_TYPE_FILTER_OPTIONS} {...bind('fileType')} width={140}/>\n <DateRangeFilter searchLabel=\"上传时间\" type=\"dateTimeRange\" {...bind('timeRange')}/>\n </>)),\n onSearch: (handleSearch),\n onReset: (handleReset),\n create: (hasPermission('system:file:upload') && (<Button type=\"primary\" icon={<Plus size={14}/>} loading={uploadProgressVisible && uploadItems.some(item => item.status === 'uploading' || item.status === 'pending')} disabled={!defaultConfig} onClick={handlePickFile}>\n 上传文件\n </Button>)),\n actions: (selectedRowKeys.length > 0 ? (<>\n <Button type=\"tertiary\" theme=\"light\" icon={<FolderDown size={14}/>} loading={batchDownloadLoading} onClick={handleBatchDownload}>\n 批量下载 ({selectedRowKeys.length})\n </Button>\n {hasPermission('system:file:delete') && (<BatchDeleteButton count={selectedRowKeys.length} loading={batchDeleteMutation.isPending} onClick={handleBatchDelete}/>)}\n <Button type=\"tertiary\" theme=\"light\" icon={<X size={12}/>} onClick={() => setSelectedRowKeys([])}>\n 取消选择\n </Button>\n </>) : null),\n filterTitle: \"文件筛选\",\n actionTitle: \"文件操作\"\n });\n // Keep upload queues and search state alive when another section is shown.\n if (!active) return null;\n return (<>\n {viewMode === 'grid' && <TableSearchPanel config={fileTableSearch}/>}\n {/* 隐藏的文件选择器放在工具栏之外:桌面 / 移动端菜单会各渲染一份 actions,ref 只能指向唯一实例 */}\n <input ref={fileInputRef} type=\"file\" hidden multiple onChange={handleUpload}/>\n\n <div className=\"files-default-tip\" style={{ padding: '8px 0' }}>\n <Space>\n <Text strong>默认文件服务:</Text>\n {defaultConfig ? (<Text>{defaultConfig.name}</Text>) : (<Text type=\"danger\">未配置默认文件服务,请先前往\"文件配置\"设置。</Text>)}\n </Space>\n {canEditPreference('filesViewMode') && <Space spacing={0}>\n <Button size=\"small\" theme={viewMode === 'list' ? 'solid' : 'light'} type={viewMode === 'list' ? 'primary' : 'tertiary'} icon={<ListIcon size={14}/>} style={{ borderRadius: '4px 0 0 4px' }} onClick={() => toggleViewMode('list')}/>\n <Button size=\"small\" theme={viewMode === 'grid' ? 'solid' : 'light'} type={viewMode === 'grid' ? 'primary' : 'tertiary'} icon={<LayoutGrid size={14}/>} style={{ borderRadius: '0 4px 4px 0' }} onClick={() => toggleViewMode('grid')}/>\n </Space>}\n </div>\n\n <AppModal title=\"上传进度\" visible={uploadProgressVisible} onCancel={() => setUploadProgressVisible(false)} footer={uploadItems.every(isUploadFinished)\n ? <Button type=\"primary\" onClick={() => setUploadProgressVisible(false)}>关闭</Button>\n : null} width={480} keepDOM={false}>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 14, padding: '4px 0' }}>\n {uploadItems.map((item) => (<div key={item.uid}>\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>\n <Space spacing={6} style={{ overflow: 'hidden', flex: 1, minWidth: 0 }}>\n <span style={{ display: 'flex', alignItems: 'center', flexShrink: 0 }}>\n {getFileTypeIcon(undefined, 14, item.name)}\n </span>\n <Typography.Text ellipsis={{ showTooltip: true }} style={{ fontSize: 13, flex: 1, minWidth: 0 }}>\n {item.name}\n </Typography.Text>\n </Space>\n <Space spacing={6} style={{ flexShrink: 0, marginLeft: 8 }}>\n <Typography.Text type=\"tertiary\" size=\"small\">{formatBytes(item.size)}</Typography.Text>\n {item.status === 'uploading' && (<Typography.Text size=\"small\">{item.progress}%</Typography.Text>)}\n {item.status === 'success' && (<CheckCircle2 size={14} color=\"var(--arc-antd-color-success)\"/>)}\n {item.status === 'error' && (<Tooltip content={item.errorMsg}>\n <XCircle size={14} color=\"var(--arc-antd-color-danger)\"/>\n </Tooltip>)}\n {item.status === 'cancelled' && (<Typography.Text type=\"tertiary\" size=\"small\">已取消</Typography.Text>)}\n {!isUploadFinished(item) && (<Tooltip content=\"取消上传\">\n <Button theme=\"borderless\" type=\"tertiary\" size=\"small\" icon={<X size={14}/>} aria-label={`取消上传 ${item.name}`} onClick={() => handleCancelUpload(item.uid)}/>\n </Tooltip>)}\n </Space>\n </div>\n <Progress percent={item.progress} type=\"line\" size=\"small\" stroke={getProgressStroke(item.status)} showInfo={false} style={{ margin: 0 }}/>\n </div>))}\n </div>\n </AppModal>\n\n <FilePreviewLayer preview={preview}/>\n\n <FileDetailModal visible={!!detailFile} file={displayedDetailFile} loading={detailFileLoading} resolveUrl={(file) => file.directUrl ?? getFileFullUrl(file.url)} onCopyUrl={(file) => void handleCopyUrl(file)} onClose={() => { setDetailFile(null); setImageResolution(null); }} extraRows={imageResolution ? [{ key: '分辨率', value: `${imageResolution.width} × ${imageResolution.height} px` }] : []}/>\n\n {viewMode === 'list' ? (<ConfigurableTable<ManagedFile> searchConfig={fileTableSearch} columns={columns} empty=\"暂无文件记录\" {...listTableProps(listQuery, {\n pagination: buildPagination,\n rowSelection: hasPermission('system:file:delete') ? {\n selectedRowKeys,\n onChange: (keys) => setSelectedRowKeys((keys ?? []).map(String)),\n } : undefined,\n })}/>) : (<>\n {hasPermission('system:file:delete') && (data?.list ?? []).length > 0 && (() => {\n const currentPageIds = (data?.list ?? []).map((f) => f.id);\n const selectedOnPage = currentPageIds.filter((id) => selectedRowKeys.includes(id));\n const allSelected = selectedOnPage.length === currentPageIds.length;\n const someSelected = selectedOnPage.length > 0 && !allSelected;\n return (<div className=\"files-grid-select-bar\" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>\n <Checkbox checked={allSelected} indeterminate={someSelected} onChange={() => handleGridSelectAll(!allSelected)}>\n <span style={{ fontSize: 14, color: 'var(--arc-antd-color-text-0)' }}>\n 全选当前页\n {selectedOnPage.length > 0 && (<span style={{ marginLeft: 4, color: 'var(--arc-antd-color-text-2)', fontWeight: 400 }}>\n ({selectedOnPage.length}/{currentPageIds.length})\n </span>)}\n </span>\n </Checkbox>\n {selectedRowKeys.length > 0 && (<Button size=\"small\" theme=\"light\" type=\"tertiary\" icon={<X size={12}/>} onClick={() => setSelectedRowKeys([])}>\n 已选 {selectedRowKeys.length} 项,取消选择\n </Button>)}\n </div>);\n })()}\n <List grid={{\n gutter: [6, 8],\n xs: 8,\n sm: 6,\n md: 4,\n lg: 3,\n xl: 2,\n xxl: 2,\n }} dataSource={data?.list ?? []} loading={listQuery.isFetching} split={false} emptyContent={<div className=\"files-grid-empty\">暂无文件记录</div>} renderItem={(file) => (<List.Item key={file.id} style={{ padding: 0, height: '100%' }}>\n <FileGridCard file={file} selected={selectedRowKeys.includes(file.id)} canSelect={hasPermission('system:file:delete')} onSelect={handleGridSelect} onPreview={preview.handlePreview} onDownload={preview.handleDownload} onDelete={handleDelete} onDetail={handleOpenDetail} onCopyUrl={handleCopyUrl} canDelete={hasPermission('system:file:delete')} previewLoading={preview.previewLoadingId === file.id}/>\n </List.Item>)}/>\n {(data?.total ?? 0) > 0 && (<div className=\"files-grid-pagination\">\n <Pagination currentPage={page} pageSize={pageSize} total={data?.total ?? 0} onPageChange={(p) => { setPage(p); }} onPageSizeChange={(size) => { setPageSize(size); setPage(1); }} showSizeChanger showTotal pageSizeOpts={FILE_GRID_PAGE_SIZE_OPTIONS}/>\n </div>)}\n </>)}\n </>);\n}\n","import { CartesianChartSpecTransformer } from \"../cartesian\";\n\nimport { setDefaultCrosshairForCartesianChart } from \"../util\";\n\nexport class LineChartSpecTransformer extends CartesianChartSpecTransformer {\n _getDefaultSeriesSpec(spec) {\n var _a;\n const seriesSpec = super._getDefaultSeriesSpec(spec, [ \"point\", \"line\", \"activePoint\", \"sampling\", \"samplingFactor\", \"pointDis\", \"pointDisMul\", \"markOverlap\", \"lineLabel\" ]);\n return seriesSpec.seriesMark = null !== (_a = spec.seriesMark) && void 0 !== _a ? _a : \"line\", \n seriesSpec;\n }\n transformSpec(spec) {\n super.transformSpec(spec), setDefaultCrosshairForCartesianChart(spec);\n }\n}\n//# sourceMappingURL=line-transformer.js.map\n","import { SeriesTypeEnum } from \"../../series/interface/type\";\n\nimport { registerLineSeries } from \"../../series/line/line\";\n\nimport { Factory } from \"../../core/factory\";\n\nimport { LineChartSpecTransformer } from \"./line-transformer\";\n\nimport { BaseChart } from \"../base\";\n\nimport { StackChartMixin } from \"../stack\";\n\nimport { mixin } from \"@visactor/vutils\";\n\nimport { registerDimensionHover } from \"../../interaction/triggers/dimension-hover\";\n\nimport { registerDimensionEvents } from \"../../event/events\";\n\nimport { getCartesianDimensionInfo, getDimensionInfoByValue } from \"../../event/events/dimension/util/cartesian\";\n\nimport { getCartesianCrosshairRect } from \"../../component/crosshair/utils/cartesian\";\n\nimport { registerDimensionTooltipProcessor } from \"../../component/tooltip/processor/dimension-tooltip\";\n\nimport { registerMarkTooltipProcessor } from \"../../component/tooltip/processor/mark-tooltip\";\n\nimport { registerGroupTooltipProcessor } from \"../../component/tooltip/processor/group-tooltip\";\n\nexport class LineChart extends BaseChart {\n constructor() {\n super(...arguments), this.transformerConstructor = LineChartSpecTransformer, this.type = \"line\", \n this.seriesType = SeriesTypeEnum.line;\n }\n _setModelOption() {\n this._modelOption.getDimensionInfo = getCartesianDimensionInfo, this._modelOption.getDimensionInfoByValue = getDimensionInfoByValue, \n this._modelOption.getRectByDimensionData = getCartesianCrosshairRect;\n }\n}\n\nLineChart.type = \"line\", LineChart.seriesType = SeriesTypeEnum.line, LineChart.transformerConstructor = LineChartSpecTransformer, \nmixin(LineChart, StackChartMixin);\n\nexport const registerLineChart = () => {\n registerDimensionTooltipProcessor(), registerMarkTooltipProcessor(), registerGroupTooltipProcessor(), \n registerDimensionEvents(), registerDimensionHover(), registerLineSeries(), Factory.registerChart(LineChart.type, LineChart);\n};\n//# sourceMappingURL=line.js.map\n","import { VChart, registerLineChart, registerLabel } from \"@visactor/vchart\";\n\nimport { createChart } from \"./BaseChart\";\n\nimport { registers } from \"./registers/cartesian\";\n\nexport const LineChart = createChart(\"LineChart\", {\n type: \"line\",\n vchartConstructor: VChart\n}, [ registerLineChart, registerLabel, ...registers ]);\n//# sourceMappingURL=LineChart.js.map","import type { CSSProperties } from 'react';\nimport \"@/components/data-viz/bars.css\";\n/** 排名、占比、分布等相对数据条;仅作视觉编码,数值必须由相邻文本表达。 */\nexport interface DataBarProps {\n value: number;\n max: number;\n minPercent?: number;\n color?: string;\n fillOpacity?: number;\n track?: string;\n height?: number;\n radius?: CSSProperties['borderRadius'];\n title?: string;\n className?: string;\n style?: CSSProperties;\n}\nexport function DataBar({ value, max, minPercent = 0, color, fillOpacity, track, height = 8, radius, title, className, style, }: Readonly<DataBarProps>) {\n const ratio = Number.isFinite(value) && Number.isFinite(max) && max > 0\n ? value / max\n : 0;\n const percent = Math.min(100, Math.max(value > 0 ? minPercent : 0, ratio * 100));\n const classes = ['zx-data-bar', className].filter(Boolean).join(' ');\n const barStyle = {\n '--zx-bar-height': `${height}px`,\n '--zx-bar-scale': percent / 100,\n ...(color ? { '--zx-bar-stroke': color } : {}),\n ...(fillOpacity !== undefined ? { '--zx-bar-opacity': fillOpacity } : {}),\n ...(track ? { '--zx-bar-track': track } : {}),\n ...(radius !== undefined ? { '--zx-bar-radius': typeof radius === 'number' ? `${radius}px` : radius } : {}),\n ...style,\n } as CSSProperties;\n return (<div className={classes} style={barStyle} title={title} aria-hidden=\"true\">\n <span className=\"zx-data-bar__fill\"/>\n </div>);\n}\nexport default DataBar;\n","import React, { useMemo } from 'react';\nimport { Spin } from \"@/ui\";\nimport { BarChart, LineChart, chartOptions, makeBarSpec, makeLineSpec, useChartPalette, StatCard, StatGrid } from \"@/components/charts/index\";\nimport { FileImage, Video, Music, FileText, File } from 'lucide-react';\nimport { useFileStats } from \"@fudanda/react-core/hooks/queries/files\";\nimport { FILE_STORAGE_PROVIDER_LABELS } from '@fudanda/shared/platform';\nimport { DataBar } from \"@/components/data-viz/DataBar\";\nimport { EMPTY_PLACEHOLDER } from \"@/utils/table-columns\";\nimport { formatBytes } from '@fudanda/shared/core';\nconst PROVIDER_LABELS: Record<string, string> = FILE_STORAGE_PROVIDER_LABELS;\nconst PROVIDER_COLORS = [\n '#3b82f6', '#10b981', '#f59e0b', '#ef4444',\n '#8b5cf6', '#06b6d4', '#f97316', '#84cc16', '#ec4899',\n];\nconst FILE_TYPE_CONFIG = [\n { type: 'image', label: '图片', Icon: FileImage, color: '#3b82f6', bgColor: 'rgba(59,130,246,0.12)' },\n { type: 'video', label: '视频', Icon: Video, color: '#8b5cf6', bgColor: 'rgba(139,92,246,0.12)' },\n { type: 'audio', label: '音频', Icon: Music, color: '#f59e0b', bgColor: 'rgba(245,158,11,0.12)' },\n { type: 'document', label: '文档', Icon: FileText, color: '#10b981', bgColor: 'rgba(16,185,129,0.12)' },\n { type: 'other', label: '其他', Icon: File, color: '#6b7280', bgColor: 'rgba(107,114,128,0.12)' },\n] as const;\nconst sectionTitleStyle: React.CSSProperties = {\n fontSize: 14,\n fontWeight: 600,\n color: 'var(--arc-antd-color-text-0)',\n marginBottom: 12,\n};\nexport default function FileStatsPanel() {\n const palette = useChartPalette();\n const statsQuery = useFileStats();\n const stats = statsQuery.data ?? null;\n const summary = stats?.summary;\n const totalFiles = summary?.totalFiles ?? 0;\n const providerData = useMemo(() => (stats?.providerStats ?? []).map((p, i) => ({\n ...p,\n providerLabel: PROVIDER_LABELS[p.provider] ?? p.provider,\n fill: PROVIDER_COLORS[i % PROVIDER_COLORS.length],\n })), [stats]);\n const uploaderData = useMemo(() => (stats?.uploaderStats ?? []).map((u) => ({ ...u, sizeLabel: formatBytes(u.size) })), [stats]);\n const providerSpec = useMemo(() => makeBarSpec({\n data: providerData,\n xField: 'providerLabel',\n series: [{ field: 'count', name: '文件数', color: '#3b82f6' }],\n palette,\n horizontal: true,\n categoryAxisWidth: 80,\n colorByDatum: (d) => String(d?.fill),\n tooltip: { value: (v) => `${v} 个文件` },\n }), [palette, providerData]);\n const monthlySpec = useMemo(() => makeLineSpec({\n data: stats?.monthlyStats ?? [],\n xField: 'month',\n series: [{ field: 'count', name: '新增文件', color: '#3b82f6' }],\n palette,\n point: true,\n tooltip: { value: (v) => `${v} 个` },\n }), [palette, stats]);\n const sizeRangeSpec = useMemo(() => makeBarSpec({\n data: stats?.sizeRangeStats ?? [],\n xField: 'range',\n series: [{ field: 'count', name: '文件数', color: '#10b981' }],\n palette,\n tooltip: { value: (v) => `${v} 个` },\n }), [palette, stats]);\n const uploaderSpec = useMemo(() => makeBarSpec({\n data: uploaderData,\n xField: 'username',\n series: [{ field: 'count', name: '文件数', color: '#8b5cf6' }],\n palette,\n horizontal: true,\n categoryAxisWidth: 80,\n tooltip: { value: (v) => `${v} 个文件` },\n }), [palette, uploaderData]);\n return (<Spin spinning={statsQuery.isFetching}>\n <div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>\n\n {/* 汇总卡片 */}\n <StatGrid>\n <StatCard title=\"文件总数\" value={totalFiles > 0 ? totalFiles.toLocaleString() : EMPTY_PLACEHOLDER}/>\n <StatCard title=\"占用空间\" value={summary ? formatBytes(summary.totalSize) : EMPTY_PLACEHOLDER}/>\n <StatCard title=\"今日新增\" value={summary?.todayCount == null ? EMPTY_PLACEHOLDER : summary.todayCount.toLocaleString()} sub=\"今日共上传\"/>\n <StatCard title=\"本月新增\" value={summary?.thisMonthCount == null ? EMPTY_PLACEHOLDER : summary.thisMonthCount.toLocaleString()} sub=\"本月共上传\"/>\n </StatGrid>\n\n {/* 文件类型卡片 */}\n <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(min(160px, 100%), 1fr))', gap: 12 }}>\n {FILE_TYPE_CONFIG.map(({ type, label, Icon, color, bgColor }) => {\n const stat = stats?.typeStats.find(t => t.type === type);\n const count = stat?.count ?? 0;\n const size = stat?.size ?? 0;\n const percent = totalFiles > 0 ? (count / totalFiles) * 100 : 0;\n return (<div key={type} className=\"zx-panel\" style={{ padding: 16, display: 'flex', flexDirection: 'column', gap: 10 }}>\n {/* 图标 + 右侧内容 */}\n <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>\n <div style={{ width: 38, height: 38, borderRadius: 'var(--arc-antd-border-radius-large)', background: bgColor, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>\n <Icon size={19} color={color}/>\n </div>\n <div style={{ flex: 1, minWidth: 0 }}>\n {/* 类型名 + 数量同行 */}\n <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 4 }}>\n <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--arc-antd-color-text-0)' }}>{label}</span>\n <span style={{ fontSize: 18, fontWeight: 700, color: 'var(--arc-antd-color-text-0)', flexShrink: 0 }}>{count.toLocaleString()}</span>\n </div>\n <div style={{ fontSize: 11, color: 'var(--arc-antd-color-text-2)', marginTop: 3 }}>{formatBytes(size)}</div>\n </div>\n </div>\n {/* 进度条 */}\n <div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 5 }}>\n <span style={{ fontSize: 11, color: 'var(--arc-antd-color-text-2)' }}>占比</span>\n <span style={{ fontSize: 11, fontWeight: 600, color }}>{percent.toFixed(1)}%</span>\n </div>\n <DataBar value={percent} max={100} color={color} height={5}/>\n </div>\n </div>);\n })}\n </div>\n\n {/* 存储类型分布 + 月度上传趋势 */}\n <div className=\"chart-grid\">\n <div className=\"zx-panel\">\n <div style={sectionTitleStyle}>存储类型分布</div>\n <BarChart {...providerSpec} options={chartOptions} height={220}/>\n </div>\n <div className=\"zx-panel\">\n <div style={sectionTitleStyle}>月度上传趋势(近 12 个月)</div>\n <LineChart {...monthlySpec} options={chartOptions} height={220}/>\n </div>\n </div>\n\n {/* 文件大小分布 + Top 上传人 */}\n <div className=\"chart-grid\">\n <div className=\"zx-panel\">\n <div style={sectionTitleStyle}>文件大小分布</div>\n <BarChart {...sizeRangeSpec} options={chartOptions} height={220}/>\n </div>\n {stats && stats.uploaderStats.length > 0 && (<div className=\"zx-panel\">\n <div style={sectionTitleStyle}>Top 上传人(按文件数)</div>\n <BarChart {...uploaderSpec} options={chartOptions} height={220}/>\n </div>)}\n </div>\n\n </div>\n </Spin>);\n}\n","import { useMemo } from 'react';\nimport { BarChart3, Files, HardDriveUpload } from 'lucide-react';\nimport { Empty } from '@/ui';\nimport { MasterDetailLayout } from '@/components/MasterDetailLayout';\nimport { NavListPanel } from '@/components/NavListPanel';\nimport { SectionNavigation } from '@/components/SectionNavigation';\nimport { useSectionSelection } from '@/hooks/useSectionSelection';\nimport { usePermission } from '@fudanda/react-core/hooks/usePermission';\nimport FileStorageConfigsPanel from '../file-configs/FileStorageConfigsPanel';\nimport FileListPanel from './FileListPanel';\nimport FileStatsPanel from './FileStatsPanel';\n\nconst SECTIONS = [\n { key: 'config', title: '文件配置', description: '本地存储与对象存储服务', icon: HardDriveUpload },\n { key: 'list', title: '文件列表', description: '上传、查看和管理文件', icon: Files },\n { key: 'stats', title: '统计分析', description: '存储用量与文件分布', icon: BarChart3 },\n] as const;\n\nexport default function FilesPage() {\n const { hasPermission } = usePermission();\n const canConfigure = hasPermission('system:file:config');\n const canList = hasPermission('system:file:list');\n const sections = useMemo(() => SECTIONS.filter(section => section.key === 'config' ? canConfigure : canList), [canConfigure, canList]);\n const navigation = useSectionSelection(sections.map(section => section.key), canList ? 'list' : 'config');\n const activeSection = navigation.active;\n if (sections.length === 0) return <Empty description=\"无访问权限\" />;\n return <div className=\"page-container page-container--stretch\">\n <MasterDetailLayout defaultSize={260} minSize={200} maxSize={400} persistKey=\"file-management\"\n style={{ flex: 1, minHeight: 0, overflow: 'hidden' }} showDetail={activeSection !== null}\n onBack={navigation.back} onResponsiveChange={navigation.responsiveChange}\n master={<NavListPanel title=\"文件管理\" rawBody>\n <SectionNavigation sections={sections} active={activeSection} onSelect={navigation.select} />\n </NavListPanel>}\n detail={<MasterDetailLayout.Body padding={activeSection === 'config' ? 0 : 16}\n scroll={activeSection === 'stats' ? 'auto' : 'hidden'} style={{ display: 'flex', flexDirection: 'column' }}>\n {canList && <FileListPanel active={activeSection === 'list'} />}\n {canConfigure && activeSection === 'config' && <FileStorageConfigsPanel />}\n {canList && activeSection === 'stats' && <FileStatsPanel />}\n </MasterDetailLayout.Body>}\n />\n </div>;\n}\n"]}