seeder-resources-view 1.3.9 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { memo, useMemo, useCallback, useState, useEffect, useRef, forwardRef, useContext, createContext } from 'react';
2
- import { Spin, List, Dropdown, Checkbox, Image, Typography, Form, Modal, Button, Cascader, Progress, Popover, Input, Flex, Popconfirm, Empty, ConfigProvider, Tree, App, Space } from 'antd';
3
- import { FolderOpenOutlined, FolderOutlined, ExclamationCircleFilled, SearchOutlined, CloudUploadOutlined } from '@ant-design/icons';
2
+ import { Spin, List, Dropdown, Checkbox, Image, Typography, Form, Modal, Button, Cascader, Progress, App, Flex, Input, Space, Popover, Popconfirm, Empty, ConfigProvider, Tree } from 'antd';
3
+ import { ExclamationCircleFilled, SearchOutlined, CloudUploadOutlined, FolderOpenOutlined, FolderOutlined } from '@ant-design/icons';
4
4
  import axios from 'axios';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
 
@@ -17555,6 +17555,466 @@ const UploadProgress = _ref => {
17555
17555
  };
17556
17556
  var UploadProgress$1 = /*#__PURE__*/memo(UploadProgress);
17557
17557
 
17558
+ const createUrlBuilder = function () {
17559
+ let config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
17560
+ const {
17561
+ baseUrl = '',
17562
+ isDev = false,
17563
+ devBaseUrl = '',
17564
+ isBrowser = false
17565
+ } = config;
17566
+ return path => {
17567
+ if (!path) return '';
17568
+
17569
+ // 如果传入的是完整 URL,直接返回
17570
+ if (/^https?:\/\//.test(path)) {
17571
+ return path;
17572
+ }
17573
+
17574
+ // 去除 path 开头可能的多余斜杠
17575
+ const cleanPath = path.replace(/^\/+/, '');
17576
+
17577
+ // 开发环境使用 devBaseUrl,否则使用 baseUrl
17578
+ const host = isDev ? devBaseUrl : baseUrl;
17579
+
17580
+ // 如果没有配置 baseUrl,则根据环境返回相对路径或完整路径
17581
+ if (!host) {
17582
+ return isBrowser ? "/".concat(cleanPath) : cleanPath;
17583
+ }
17584
+ return host ? "".concat(host, "/").concat(cleanPath) : "/".concat(cleanPath);
17585
+ };
17586
+ };
17587
+
17588
+ const pathUtils = {
17589
+ replaceRoot: path => path.replace(/^root$|^root\//, '')
17590
+ };
17591
+ const ResourcesView = _ref => {
17592
+ let {
17593
+ apiConfig = {},
17594
+ features = {},
17595
+ className = '',
17596
+ style = {},
17597
+ renderTreeHeader,
17598
+ renderGridHeader,
17599
+ withRootNode = true,
17600
+ acceptFileTypes = "video/*,.mxf,application/mxf,video/mxf",
17601
+ uploadTimeout = 60 * 60 * 1000,
17602
+ searchPlaceholder = "Search ...",
17603
+ refreshSignal = 0,
17604
+ // 新增:外部刷新信号,数值变化时触发刷新
17605
+ treeMode = 'mode1' // 新增:控制使用哪种模式的hook
17606
+ } = _ref;
17607
+ const {
17608
+ message,
17609
+ modal
17610
+ } = App.useApp();
17611
+
17612
+ // 合并默认配置和传入配置
17613
+ const mergedApiConfig = useMemo(() => _objectSpread2({
17614
+ upload: '/api/dvr/media/source/upload',
17615
+ download: '/download'
17616
+ }, apiConfig), [apiConfig]);
17617
+ const mergedFeatures = useMemo(() => _objectSpread2({
17618
+ upload: true,
17619
+ download: true,
17620
+ copy: true,
17621
+ delete: true,
17622
+ search: true,
17623
+ batchOperations: true
17624
+ }, features), [features]);
17625
+
17626
+ // 根据treeMode选择不同的hook
17627
+ const useTreeHook = useMemo(() => {
17628
+ if (treeMode === 'mode2') {
17629
+ // 录像机资源管理页面
17630
+ return require('./hooks/useDirectoryTree2').useDirectoryTree;
17631
+ }
17632
+ return require('./hooks/useDirectoryTree').useDirectoryTree;
17633
+ }, [treeMode]);
17634
+ const {
17635
+ directoryTree,
17636
+ contents,
17637
+ currentPath,
17638
+ loading,
17639
+ originTreeData,
17640
+ updateFileContents,
17641
+ removeFile,
17642
+ refreshTreeData
17643
+ } = useTreeHook({
17644
+ getFolderData: mergedApiConfig.getFolderData,
17645
+ createFolder: mergedApiConfig.createFolder,
17646
+ removeFolderFile: mergedApiConfig.removeFolderFile,
17647
+ renameFolderFile: mergedApiConfig.renameFolderFile,
17648
+ batchOperations: mergedFeatures.batchOperations,
17649
+ withRootNode
17650
+ });
17651
+
17652
+ // 监听外部刷新信号变化
17653
+ useEffect(() => {
17654
+ if (refreshTreeData && refreshSignal > 0) {
17655
+ refreshTreeData();
17656
+ }
17657
+ }, [refreshSignal, refreshTreeData]);
17658
+ const [showProgress, setShowProgress] = useState(false);
17659
+ const [percent, setPercent] = useState(0);
17660
+ const inputRef = useRef(null);
17661
+ const uploadController = useRef(new AbortController());
17662
+ const [modalVisible, setModalVisible] = useState(false);
17663
+ // 处理批量操作
17664
+ const [selectedItems, setSelectedItems] = useState([]);
17665
+ const [indeterminate, setIndeterminate] = useState(false);
17666
+ const [checkAll, setCheckAll] = useState(false);
17667
+ const [batchLoading, setBatchLoading] = useState(false);
17668
+ const [keyword, setKeyword] = useState('');
17669
+ const [debouncedKeyword, setDebouncedKeyword] = useState('');
17670
+
17671
+ // 创建防抖函数(延迟 300ms)
17672
+ const debouncedSearch = useMemo(() => lodashExports.debounce(searchValue => {
17673
+ setDebouncedKeyword(searchValue);
17674
+ }, 300), []);
17675
+
17676
+ // 监听 keyword 变化并触发防抖
17677
+ useEffect(() => {
17678
+ debouncedSearch(keyword);
17679
+ return () => debouncedSearch.cancel(); // 组件卸载时取消防抖
17680
+ }, [keyword, debouncedSearch]);
17681
+
17682
+ // 过滤内容
17683
+ const filteredContents = useMemo(() => {
17684
+ if (!debouncedKeyword) return contents;
17685
+ return contents.filter(item => item.name.toLowerCase().includes(debouncedKeyword.toLowerCase()));
17686
+ }, [contents, debouncedKeyword]);
17687
+
17688
+ // 在组件卸载时取消上传
17689
+ useEffect(() => {
17690
+ return () => {
17691
+ var _uploadController$cur;
17692
+ return (_uploadController$cur = uploadController.current) === null || _uploadController$cur === void 0 ? void 0 : _uploadController$cur.abort();
17693
+ };
17694
+ }, []);
17695
+
17696
+ // 重置批量状态的效果
17697
+ useEffect(() => {
17698
+ setSelectedItems([]);
17699
+ setIndeterminate(false);
17700
+ setCheckAll(false);
17701
+ }, [currentPath]);
17702
+
17703
+ // 上传相关函数
17704
+ const onTriggerUpload = useCallback(() => {
17705
+ var _inputRef$current;
17706
+ (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.click();
17707
+ }, []);
17708
+ const onUploadFile = async event => {
17709
+ const files = event.target.files;
17710
+ if (!(files !== null && files !== void 0 && files.length)) return;
17711
+
17712
+ // 初始化上传状态
17713
+ setShowProgress(true);
17714
+ setPercent(0);
17715
+ try {
17716
+ await uploadFiles(files, pathUtils.replaceRoot(currentPath));
17717
+ // 延迟2s刷新数据
17718
+ // eslint-disable-next-line no-promise-executor-return
17719
+ await new Promise(resolve => setTimeout(resolve, 2000));
17720
+ await updateFileContents();
17721
+ } catch (error) {
17722
+ if (axios.isCancel(error)) {
17723
+ console.log('Upload canceled');
17724
+ } else if (error.response) {
17725
+ var _error$response$data;
17726
+ console.error("Upload failed: ".concat(((_error$response$data = error.response.data) === null || _error$response$data === void 0 ? void 0 : _error$response$data.message) || error.response.status));
17727
+ } else {
17728
+ console.error("Upload error: ".concat(error.message));
17729
+ }
17730
+ } finally {
17731
+ resetUploadState();
17732
+ }
17733
+ };
17734
+ const uploadFiles = async (files, folder) => {
17735
+ // 计算所有文件的总大小
17736
+ const totalSize = calculateTotalSize(files);
17737
+ // 存储每个文件的上一次 loaded 值(避免重复计算)
17738
+ const previousLoadedMap = new Map();
17739
+ let uploadedSize = 0; // 记录已上传的字节数
17740
+
17741
+ const uploadTasks = Array.from(files).map(file => uploadSingleFile(file, folder, previousLoadedMap, newLoaded => {
17742
+ uploadedSize += newLoaded;
17743
+ updateProgress(uploadedSize, totalSize);
17744
+ }));
17745
+ await Promise.all(uploadTasks);
17746
+ };
17747
+
17748
+ // 计算总文件大小
17749
+ const calculateTotalSize = files => Array.from(files).reduce((sum, file) => sum + file.size, 0);
17750
+
17751
+ // 上传单个文件
17752
+ const uploadSingleFile = (file, folder, previousLoadedMap, onProgress) => {
17753
+ const formData = new FormData();
17754
+ formData.append('file', file);
17755
+ formData.append('folder', folder);
17756
+ return axios.post(mergedApiConfig.upload, formData, {
17757
+ timeout: uploadTimeout,
17758
+ headers: {
17759
+ 'Content-Type': 'multipart/form-data'
17760
+ },
17761
+ // signal: uploadController.current.signal,
17762
+ onUploadProgress: progressEvent => {
17763
+ if (progressEvent.lengthComputable) {
17764
+ // 获取当前文件的上一次 loaded 值(默认 0)
17765
+ const previousLoaded = previousLoadedMap.get(file.name) || 0;
17766
+ // 计算新增的字节数(避免重复累加)
17767
+ const newLoaded = progressEvent.loaded - previousLoaded;
17768
+ // 更新当前文件的 loaded 值
17769
+ previousLoadedMap.set(file.name, progressEvent.loaded);
17770
+ onProgress(newLoaded);
17771
+ }
17772
+ }
17773
+ });
17774
+ };
17775
+ const updateProgress = (uploadedSize, totalSize) => {
17776
+ const totalPercent = Math.round(uploadedSize / totalSize * 100);
17777
+ setPercent(totalPercent);
17778
+ };
17779
+
17780
+ // 重置上传状态
17781
+ const resetUploadState = () => {
17782
+ setShowProgress(false);
17783
+ setPercent(0);
17784
+ if (inputRef.current) inputRef.current.value = "";
17785
+ };
17786
+
17787
+ // 文件操作函数
17788
+ const downloadFile = async url => {
17789
+ try {
17790
+ const isDev = process.env.NODE_ENV === 'development';
17791
+ const baseUrl = isDev ? process.env.API_HOST // 开发环境默认值
17792
+ : window.location.origin; // 生产环境用当前域名
17793
+
17794
+ const link = document.createElement('a');
17795
+ link.href = "".concat(baseUrl).concat(mergedApiConfig.download, "/").concat(url);
17796
+ // link.download = filename;
17797
+ link.style.display = 'none';
17798
+ document.body.appendChild(link);
17799
+ link.click();
17800
+ document.body.removeChild(link);
17801
+ } catch (error) {
17802
+ console.error("Download failed:", error);
17803
+ }
17804
+ };
17805
+ const copyFile = () => {
17806
+ setModalVisible(true);
17807
+ };
17808
+ const onContextMenu = (key, item) => {
17809
+ if (key === 'del') removeFile(item.url);
17810
+ if (key === 'download') downloadFile(item.url);
17811
+ if (key === 'copy') copyFile(item.url);
17812
+ };
17813
+ const handleBatchRemove = useCallback(async () => {
17814
+ if (!selectedItems.length) return;
17815
+ modal.confirm({
17816
+ icon: /*#__PURE__*/jsx(ExclamationCircleFilled, {}),
17817
+ title: "Are you sure you want to delete ".concat(selectedItems.length, " files?"),
17818
+ cancelText: "No",
17819
+ okText: "Yes",
17820
+ onOk: async () => {
17821
+ try {
17822
+ // 批量处理所有删除请求
17823
+ await mergedApiConfig.removeFolderFile({
17824
+ paths: selectedItems.map(itemUrl => ({
17825
+ path: itemUrl
17826
+ }))
17827
+ });
17828
+
17829
+ // 更新状态
17830
+ setSelectedItems([]);
17831
+ setIndeterminate(false);
17832
+ setCheckAll(false);
17833
+ await updateFileContents(); // 刷新文件列表
17834
+ } catch (error) {
17835
+ console.error('Batch deletion error:', error);
17836
+ }
17837
+ }
17838
+ });
17839
+ }, [selectedItems, mergedApiConfig, updateFileContents]);
17840
+ const handleBatchDownload = useCallback(async () => {
17841
+ if (!selectedItems.length) return;
17842
+ setBatchLoading(true);
17843
+ try {
17844
+ // 逐个下载文件
17845
+ for (let i = 0; i < selectedItems.length; i++) {
17846
+ const url = selectedItems[i];
17847
+ downloadFile(url);
17848
+
17849
+ // 添加延迟避免浏览器并发限制(建议 500-1000ms)
17850
+ if (i < selectedItems.length - 1) {
17851
+ // eslint-disable-next-line no-promise-executor-return
17852
+ await new Promise(resolve => setTimeout(resolve, 500));
17853
+ }
17854
+ }
17855
+ } catch (error) {
17856
+ console.error('Batch download error:', error);
17857
+ } finally {
17858
+ setBatchLoading(false);
17859
+ }
17860
+ }, [selectedItems, downloadFile]);
17861
+ const handleCopyConfirm = useCallback(async newPath => {
17862
+ setBatchLoading(true);
17863
+ try {
17864
+ // 批量处理所有拷贝请求
17865
+ await mergedApiConfig.copyFile({
17866
+ old_paths: selectedItems.map(itemUrl => ({
17867
+ old_path: itemUrl
17868
+ })),
17869
+ new_path: newPath
17870
+ });
17871
+
17872
+ // 更新状态
17873
+ setSelectedItems([]);
17874
+ setIndeterminate(false);
17875
+ setCheckAll(false);
17876
+ message.success('Success');
17877
+ setModalVisible(false);
17878
+
17879
+ // 延迟2s刷新数据
17880
+ // eslint-disable-next-line no-promise-executor-return
17881
+ await new Promise(resolve => setTimeout(resolve, 2000));
17882
+ await updateFileContents();
17883
+ } catch (error) {
17884
+ console.error("Batch copy error:", error);
17885
+ } finally {
17886
+ setBatchLoading(false);
17887
+ }
17888
+ }, [selectedItems, mergedApiConfig, updateFileContents]);
17889
+
17890
+ // 处理单个项目的选择变化
17891
+ const handleSelectChange = (item, checked) => {
17892
+ const newSelectedItems = checked ? [...selectedItems, item.url] : selectedItems.filter(url => url !== item.url);
17893
+ setSelectedItems(newSelectedItems);
17894
+ setIndeterminate(!!newSelectedItems.length && newSelectedItems.length !== filteredContents.length);
17895
+ setCheckAll(newSelectedItems.length === filteredContents.length);
17896
+ };
17897
+
17898
+ // 处理全选/取消全选
17899
+ const handleSelectAll = e => {
17900
+ const checked = e.target.checked;
17901
+ setSelectedItems(checked ? filteredContents.map(item => item.url) : []);
17902
+ setIndeterminate(false);
17903
+ setCheckAll(checked);
17904
+ };
17905
+
17906
+ // 搜索框变化处理
17907
+ const handleSearchChange = e => {
17908
+ setKeyword(e.target.value);
17909
+ };
17910
+
17911
+ // 创建 URL 构建器
17912
+ const getFullUrl = useMemo(() => {
17913
+ return createUrlBuilder({
17914
+ baseUrl: apiConfig.baseUrl,
17915
+ devBaseUrl: apiConfig.devBaseUrl,
17916
+ isDev: process.env.NODE_ENV === 'development',
17917
+ isBrowser: typeof window !== 'undefined'
17918
+ });
17919
+ }, [apiConfig.baseUrl, apiConfig.devBaseUrl]);
17920
+ return /*#__PURE__*/jsxs("div", {
17921
+ className: "resources-view ".concat(className),
17922
+ style: style,
17923
+ children: [/*#__PURE__*/jsxs("div", {
17924
+ className: "content-container",
17925
+ children: [/*#__PURE__*/jsxs("div", {
17926
+ className: "directory-tree",
17927
+ children: [renderTreeHeader ? renderTreeHeader() : /*#__PURE__*/jsx("div", {
17928
+ className: "search-bar"
17929
+ }), directoryTree]
17930
+ }), /*#__PURE__*/jsxs("div", {
17931
+ className: "folder-contents",
17932
+ children: [/*#__PURE__*/jsxs(Flex, {
17933
+ justify: "center",
17934
+ align: "center",
17935
+ className: "search-bar",
17936
+ children: [mergedFeatures.search && /*#__PURE__*/jsx(Input, {
17937
+ value: keyword,
17938
+ onChange: handleSearchChange,
17939
+ placeholder: searchPlaceholder,
17940
+ prefix: /*#__PURE__*/jsx(SearchOutlined, {}),
17941
+ allowClear: true,
17942
+ className: "search-input"
17943
+ }), mergedFeatures.upload && /*#__PURE__*/jsxs(Fragment, {
17944
+ children: [/*#__PURE__*/jsx(CloudUploadOutlined, {
17945
+ className: "upload-icon",
17946
+ title: "upload",
17947
+ onClick: onTriggerUpload
17948
+ }), /*#__PURE__*/jsx("input", {
17949
+ ref: inputRef,
17950
+ type: "file",
17951
+ onChange: onUploadFile,
17952
+ className: "hidden",
17953
+ accept: acceptFileTypes,
17954
+ multiple: true
17955
+ })]
17956
+ })]
17957
+ }), /*#__PURE__*/jsxs("div", {
17958
+ className: "media-grid-container",
17959
+ onContextMenu: e => e.preventDefault(),
17960
+ children: [renderGridHeader ? renderGridHeader() : filteredContents.length > 0 && mergedFeatures.batchOperations && /*#__PURE__*/jsx("div", {
17961
+ className: "batch-operations",
17962
+ children: /*#__PURE__*/jsxs(Space, {
17963
+ size: "middle",
17964
+ children: [/*#__PURE__*/jsx(Checkbox, {
17965
+ indeterminate: indeterminate,
17966
+ checked: checkAll,
17967
+ onChange: handleSelectAll,
17968
+ children: "Select All"
17969
+ }), /*#__PURE__*/jsx(Button, {
17970
+ type: "primary",
17971
+ disabled: !selectedItems.length,
17972
+ onClick: () => setModalVisible(true),
17973
+ children: "Copy"
17974
+ }), /*#__PURE__*/jsx(Button, {
17975
+ className: "btn-gray",
17976
+ disabled: !selectedItems.length,
17977
+ onClick: handleBatchRemove,
17978
+ children: "Delete"
17979
+ }), /*#__PURE__*/jsx(Button, {
17980
+ className: "btn-gray",
17981
+ disabled: !selectedItems.length,
17982
+ onClick: handleBatchDownload,
17983
+ children: "Download"
17984
+ })]
17985
+ })
17986
+ }), /*#__PURE__*/jsx(MediaGrid$1, {
17987
+ items: filteredContents,
17988
+ loading: loading,
17989
+ onContextMenu: mergedFeatures.delete || mergedFeatures.download || mergedFeatures.copy ? onContextMenu : null,
17990
+ selectedKeys: selectedItems,
17991
+ showCheckbox: mergedFeatures.batchOperations,
17992
+ onSelectChange: mergedFeatures.batchOperations ? handleSelectChange : null,
17993
+ gridConfig: mergedFeatures.batchOperations ? {
17994
+ gutter: 0,
17995
+ column: 6
17996
+ } : {
17997
+ gutter: 24,
17998
+ column: 6
17999
+ },
18000
+ features: mergedFeatures,
18001
+ getFullUrl: getFullUrl
18002
+ })]
18003
+ })]
18004
+ })]
18005
+ }), showProgress && /*#__PURE__*/jsx(UploadProgress$1, {
18006
+ percent: percent
18007
+ }), /*#__PURE__*/jsx(SelectFolderPathModal$1, {
18008
+ open: modalVisible,
18009
+ directoryTree: originTreeData,
18010
+ batchLoading: batchLoading,
18011
+ onClose: () => setModalVisible(false),
18012
+ onOk: handleCopyConfirm
18013
+ })]
18014
+ });
18015
+ };
18016
+ var ResourcesView$1 = ResourcesView;
18017
+
17558
18018
  const PopoverContent = /*#__PURE__*/forwardRef((_ref, ref) => {
17559
18019
  let {
17560
18020
  onClose,
@@ -17724,7 +18184,14 @@ const TreeTitleNode = _ref2 => {
17724
18184
  title: "Confirm deletion?",
17725
18185
  onConfirm: () => handleDel(nodeData),
17726
18186
  okText: "Yes",
17727
- cancelText: "No",
18187
+ cancelText: "No"
18188
+ // okButtonProps={{
18189
+ // size: "middle" // 增大确认按钮
18190
+ // }}
18191
+ // cancelButtonProps={{
18192
+ // size: "middle" // 增大取消按钮
18193
+ // }}
18194
+ ,
17728
18195
  children: /*#__PURE__*/jsx("div", {
17729
18196
  className: "px-1",
17730
18197
  title: "Delete",
@@ -17830,29 +18297,6 @@ const buildDirectoryTree = function (data) {
17830
18297
  return build(processedData);
17831
18298
  };
17832
18299
 
17833
- /**
17834
- * 查找树中指定key的节点
17835
- * @param {Array} treeData - 树数据
17836
- * @param {string} targetKey - 要查找的key
17837
- * @returns {Array} 包含匹配节点的数组
17838
- */
17839
- const findTreeNode = (treeData, targetKey) => {
17840
- if (!Array.isArray(treeData) || typeof targetKey !== 'string') return [];
17841
- const result = [];
17842
- const stack = [...treeData];
17843
- while (stack.length) {
17844
- const node = stack.pop();
17845
- if (node.key === targetKey) {
17846
- result.push(node);
17847
- // 如果只需要第一个匹配项,可以在这里break
17848
- }
17849
- if (Array.isArray(node.children)) {
17850
- stack.push(...node.children);
17851
- }
17852
- }
17853
- return result;
17854
- };
17855
-
17856
18300
  /**
17857
18301
  * 获取树中所有节点的keys
17858
18302
  * @param {Array} treeData - 树数据
@@ -17865,831 +18309,571 @@ const getAllNodeKeys = treeData => {
17865
18309
  while (stack.length) {
17866
18310
  const node = stack.pop();
17867
18311
  if (node.key !== null) {
17868
- keys.push(node.key);
17869
- }
17870
- if (Array.isArray(node.children)) {
17871
- stack.push(...node.children);
17872
- }
17873
- }
17874
- return keys;
17875
- };
17876
-
17877
- /**
17878
- * 根据 URL 查找树节点
17879
- * @param {Array} treeData - 树数据
17880
- * @param {string} url - 要查找的 URL
17881
- * @param {Object} options - 配置选项
17882
- * @param {boolean} options.exactMatch - 是否精确匹配(默认true)
17883
- * @param {boolean} options.returnFirst - 是否返回第一个匹配项(默认false,返回所有匹配项)
17884
- * @returns {Array} 包含匹配节点的数组
17885
- */
17886
- const findTreeNodeByUrl = function (treeData, url) {
17887
- let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
17888
- const {
17889
- exactMatch = true,
17890
- returnFirst = false
17891
- } = options;
17892
- if (!Array.isArray(treeData) || typeof url !== 'string' || !url.trim()) {
17893
- return [];
17894
- }
17895
- const result = [];
17896
- const stack = [...treeData];
17897
- while (stack.length) {
17898
- const node = stack.pop();
17899
-
17900
- // 检查当前节点是否匹配
17901
- if (node.url) {
17902
- const isMatch = exactMatch ? node.url === url : node.url.includes(url);
17903
- if (isMatch) {
17904
- result.push(node);
17905
- if (returnFirst) {
17906
- return result; // 找到第一个匹配项就结束
17907
- }
17908
- }
17909
- }
17910
-
17911
- // 递归处理子节点
17912
- if (Array.isArray(node.children)) {
17913
- stack.push(...node.children);
17914
- }
17915
- }
17916
- return result;
17917
- };
17918
-
17919
- const useDirectoryTree = _ref => {
17920
- let {
17921
- getFolderData,
17922
- createFolder,
17923
- removeFolderFile,
17924
- renameFolderFile,
17925
- mediaType,
17926
- isEditable = true,
17927
- batchOperations = true,
17928
- withRootNode = true,
17929
- height = 828,
17930
- theme = {
17931
- components: {
17932
- Tree: {
17933
- titleHeight: 30
17934
- }
17935
- }
17936
- },
17937
- treeStyle = {},
17938
- // Tree 组件样式
17939
- wrapperStyle = {
17940
- paddingTop: 16
17941
- },
17942
- // 包装器样式
17943
- expandable = true,
17944
- // 新增:是否允许展开/折叠
17945
- onSelectDir,
17946
- // 新增:选择目录回调
17947
- selectedDirPath = null // 新增:外部传入的选中目录名
17948
- } = _ref;
17949
- const [treeState, setTreeState] = useState({
17950
- data: [],
17951
- selectedKeys: [],
17952
- expandedKeys: [],
17953
- currentPath: "",
17954
- contents: [],
17955
- loading: false
17956
- });
17957
- const [originTreeData, setOriginTreeData] = useState([]);
17958
- const updateTreeState = partialState => {
17959
- setTreeState(prev => _objectSpread2(_objectSpread2({}, prev), partialState));
17960
- };
17961
-
17962
- // 错误处理
17963
- const handleError = (error, operation) => {
17964
- console.error("".concat(operation, " ERROR"), error);
17965
- // 可以添加通知等统一错误处理
17966
- };
17967
-
17968
- // 路径处理工具
17969
- const pathUtils = {
17970
- replaceRoot: path => path.replace(/^root\//, ''),
17971
- getNewPath: (node, newTitle) => {
17972
- const arr = node.path.split('/');
17973
- arr[arr.length - 1] = newTitle;
17974
- return arr.join('/');
17975
- },
17976
- isSamePath: (path1, path2) => {
17977
- return pathUtils.replaceRoot(path1) === pathUtils.replaceRoot(path2);
17978
- }
17979
- };
17980
-
17981
- // 节点查找工具
17982
- const nodeUtils = {
17983
- findNode: (treeData, key) => {
17984
- return findTreeNode(treeData, key);
17985
- },
17986
- findNodeByUrl: (treeData, url) => {
17987
- return findTreeNodeByUrl(treeData, url);
17988
- }
17989
- };
17990
-
17991
- // 获取文件夹数据 文件夹名称为空时返回根目录数据
17992
- const fetchFolderData = useCallback(async function () {
17993
- let initialization = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
17994
- try {
17995
- // 构建请求参数
17996
- const requestParams = {
17997
- folder: ""
17998
- };
17999
- if (mediaType) {
18000
- requestParams.media_type = mediaType;
18001
- }
18002
- const data = await getFolderData(requestParams);
18003
- if (!(data !== null && data !== void 0 && data.directoryTree)) {
18004
- return null;
18005
- }
18006
- setOriginTreeData(data.directoryTree);
18007
-
18008
- // 递归生成treenodes
18009
- const treeNodes = buildDirectoryTree(data.directoryTree, {
18010
- withRoot: withRootNode
18011
- });
18012
- if (!treeNodes.length) {
18013
- return null;
18014
- }
18015
-
18016
- // 确定选中的节点
18017
- let selectedNode = null;
18018
-
18019
- // 情况1:selectedDirPath 未传(undefined/null)
18020
- if (selectedDirPath === undefined || selectedDirPath === null) {
18021
- var _selectedNode;
18022
- selectedNode = treeNodes[0];
18023
- console.log('No path provided, using first node:', (_selectedNode = selectedNode) === null || _selectedNode === void 0 ? void 0 : _selectedNode.title);
18024
- }
18025
- // 情况2:selectedDirPath 为空字符串
18026
- else if (selectedDirPath.trim() === '') {
18027
- // 不设置 selectedNode,保持为 null
18028
- // console.log('Empty path provided, not setting default node');
18029
- }
18030
- // 情况3:selectedDirPath 有值
18031
- else {
18032
- try {
18033
- const foundNodes = nodeUtils.findNodeByUrl(treeNodes, selectedDirPath);
18034
- selectedNode = (foundNodes === null || foundNodes === void 0 ? void 0 : foundNodes[0]) || null;
18035
- if (!selectedNode) {
18036
- console.warn("No node found for path: \"".concat(selectedDirPath, "\""));
18037
- }
18038
- } catch (error) {
18039
- console.error('Error finding node by URL:', error);
18040
- selectedNode = null;
18041
- }
18042
- }
18043
- const newState = _objectSpread2({
18044
- data: treeNodes
18045
- }, initialization && _objectSpread2({
18046
- expandedKeys: getAllNodeKeys(treeNodes)
18047
- }, selectedNode ? {
18048
- selectedKeys: [selectedNode.key].filter(Boolean),
18049
- currentPath: selectedNode.path || '',
18050
- contents: selectedNode.contents || []
18051
- } : {
18052
- selectedKeys: [],
18053
- currentPath: '',
18054
- contents: []
18055
- }));
18056
- updateTreeState(newState);
18057
- return treeNodes;
18058
- } catch (error) {
18059
- handleError(error, 'GET FOLDER DATA');
18060
- }
18061
- }, [mediaType, withRootNode]);
18062
-
18063
- // 初始化数据
18064
- useEffect(() => {
18065
- fetchFolderData(true);
18066
- }, [fetchFolderData]);
18067
-
18068
- // 创建文件夹
18069
- const handleCreate = useCallback(async (node, newTitle) => {
18070
- if (!(newTitle !== null && newTitle !== void 0 && newTitle.trim())) return false;
18071
- try {
18072
- var _parentNode$;
18073
- const path = pathUtils.replaceRoot("".concat(node.path, "/").concat(newTitle));
18074
- await createFolder({
18075
- path
18076
- });
18077
- const newTreeData = await fetchFolderData();
18078
-
18079
- // 找到新增节点的父节点
18080
- const parentNode = nodeUtils.findNode(newTreeData, node.key);
18081
- if (parentNode !== null && parentNode !== void 0 && (_parentNode$ = parentNode[0]) !== null && _parentNode$ !== void 0 && _parentNode$.children) {
18082
- const expectedPath = "".concat(node.path, "/").concat(newTitle);
18083
- // 通过 path 找到新增节点,得到新增节点的key
18084
- const addedNode = parentNode[0].children.find(ch => pathUtils.isSamePath(ch.path, expectedPath));
18085
- if (addedNode) {
18086
- updateTreeState({
18087
- selectedKeys: [addedNode.key],
18088
- expandedKeys: [...treeState.expandedKeys, addedNode.key, parentNode[0].key],
18089
- currentPath: addedNode.path,
18090
- contents: []
18091
- });
18092
- }
18093
- }
18094
- ;
18095
- } catch (error) {
18096
- handleError(error, 'CREATE FOLDER');
18097
- }
18098
- }, [fetchFolderData, treeState.expandedKeys]);
18099
-
18100
- // 删除文件/文件夹
18101
- const handleRemove = useCallback(async node => {
18102
- if (!node.path) return;
18103
- try {
18104
- batchOperations ? await removeFolderFile({
18105
- paths: [{
18106
- path: pathUtils.replaceRoot(node.path)
18107
- }]
18108
- }) : await removeFolderFile({
18109
- path: pathUtils.replaceRoot(node.path)
18110
- });
18111
- // 如果删除的是当前选中节点,则重新初始化
18112
- const shouldReinitialize = treeState.selectedKeys[0] === node.key;
18113
- await fetchFolderData(shouldReinitialize);
18114
- } catch (error) {
18115
- handleError(error, 'REMOVE FOLDER/FILE');
18116
- }
18117
- }, [fetchFolderData, treeState.selectedKeys]);
18118
-
18119
- // 修改文件/文件夹
18120
- const handleRename = useCallback(async node => {
18121
- const newPath = pathUtils.getNewPath(node, node.title);
18122
- if (pathUtils.isSamePath(node.path, newPath)) return false;
18123
- try {
18124
- await renameFolderFile({
18125
- old_path: pathUtils.replaceRoot(node.path),
18126
- new_path: pathUtils.replaceRoot(newPath)
18127
- });
18128
- await fetchFolderData();
18129
- } catch (error) {
18130
- handleError(error, 'RENAME FOLDER/FILE');
18131
- }
18132
- }, [fetchFolderData]);
18133
-
18134
- // 选择节点
18135
- const onSelect = useCallback(async (keys, info) => {
18136
- // 如果选中了 root 节点,并且有传 onSelectDir 回调,则直接返回
18137
- if (info.node.path === 'root' && onSelectDir) return;
18138
- if (!keys.length || keys[0] === treeState.selectedKeys[0] && pathUtils.isSamePath(info.node.path, treeState.currentPath)) return;
18139
- updateTreeState({
18140
- selectedKeys: keys,
18141
- currentPath: info.node.path,
18142
- loading: true
18143
- });
18144
-
18145
- // 触发 onSelectDir 回调(如果存在)
18146
- if (onSelectDir) {
18147
- onSelectDir(info.node.rawData);
18148
- }
18149
- try {
18150
- // 模拟延迟加载
18151
- // eslint-disable-next-line no-promise-executor-return
18152
- await new Promise(resolve => setTimeout(resolve, 300));
18153
- updateTreeState({
18154
- contents: info.node.contents,
18155
- loading: false
18156
- });
18157
- } catch (error) {
18158
- handleError(error, 'SELECT NODE');
18159
- updateTreeState({
18160
- loading: false
18161
- });
18312
+ keys.push(node.key);
18162
18313
  }
18163
- }, [treeState.selectedKeys, treeState.currentPath]);
18314
+ if (Array.isArray(node.children)) {
18315
+ stack.push(...node.children);
18316
+ }
18317
+ }
18318
+ return keys;
18319
+ };
18164
18320
 
18165
- // 展开节点
18166
- const onExpand = useCallback(keys => {
18167
- updateTreeState({
18168
- expandedKeys: keys
18169
- });
18170
- }, []);
18321
+ /**
18322
+ * 根据 URL 查找树节点
18323
+ * @param {Array} treeData - 树数据
18324
+ * @param {string} url - 要查找的 URL
18325
+ * @param {Object} options - 配置选项
18326
+ * @param {boolean} options.exactMatch - 是否精确匹配(默认true)
18327
+ * @param {boolean} options.returnFirst - 是否返回第一个匹配项(默认false,返回所有匹配项)
18328
+ * @returns {Array} 包含匹配节点的数组
18329
+ */
18330
+ const findTreeNodeByUrl = function (treeData, url) {
18331
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
18332
+ const {
18333
+ exactMatch = true,
18334
+ returnFirst = false
18335
+ } = options;
18336
+ if (!Array.isArray(treeData) || typeof url !== 'string' || !url.trim()) {
18337
+ return [];
18338
+ }
18339
+ const result = [];
18340
+ const stack = [...treeData];
18341
+ while (stack.length) {
18342
+ const node = stack.pop();
18171
18343
 
18172
- // 刷新树数据
18173
- const refreshTreeData = useCallback(async () => {
18174
- try {
18175
- const treeNodes = await fetchFolderData();
18176
-
18177
- // 查找当前选中节点是否还在新数据中
18178
- const currentKey = treeState.selectedKeys[0];
18179
- let shouldUpdateContents = false;
18180
- if (currentKey && treeNodes) {
18181
- const foundNode = nodeUtils.findNode(treeNodes, currentKey);
18182
- if (foundNode !== null && foundNode !== void 0 && foundNode[0]) {
18183
- shouldUpdateContents = true;
18184
- updateTreeState({
18185
- contents: foundNode[0].contents || []
18186
- });
18187
- } else {
18188
- // 如果当前选中节点不存在了,清除选中状态
18189
- updateTreeState({
18190
- selectedKeys: [],
18191
- currentPath: '',
18192
- contents: []
18193
- });
18344
+ // 检查当前节点是否匹配
18345
+ if (node.url) {
18346
+ const isMatch = exactMatch ? node.url === url : node.url.includes(url);
18347
+ if (isMatch) {
18348
+ result.push(node);
18349
+ if (returnFirst) {
18350
+ return result; // 找到第一个匹配项就结束
18194
18351
  }
18195
18352
  }
18196
- } catch (error) {
18197
- handleError(error, 'REFRESH TREE DATA');
18198
- return null;
18199
- }
18200
- }, [fetchFolderData, treeState.selectedKeys]);
18201
-
18202
- // 上传右侧文件或删除右侧文件 成功后的回调函数
18203
- const updateFileContents = useCallback(async () => {
18204
- // 找到当前选中的节点,更新当前节点的 contents
18205
- const newTreeData = await fetchFolderData();
18206
- const selectedNode = nodeUtils.findNode(newTreeData, treeState.selectedKeys[0]);
18207
- if (selectedNode !== null && selectedNode !== void 0 && selectedNode[0]) {
18208
- updateTreeState({
18209
- contents: selectedNode[0].contents
18210
- });
18211
18353
  }
18212
- }, [fetchFolderData, treeState.selectedKeys]);
18213
18354
 
18214
- // 删除文件
18215
- const removeFile = useCallback(async url => {
18216
- try {
18217
- await removeFolderFile({
18218
- path: url
18219
- });
18220
- await updateFileContents();
18221
- } catch (error) {
18222
- handleError(error, 'REMOVE FILE');
18223
- }
18224
- }, [updateFileContents]);
18225
- const MemoizedTree = useMemo(() => {
18226
- var _treeState$data;
18227
- if (!((_treeState$data = treeState.data) !== null && _treeState$data !== void 0 && _treeState$data.length)) {
18228
- return /*#__PURE__*/jsx(Empty, {
18229
- image: Empty.PRESENTED_IMAGE_SIMPLE
18230
- });
18355
+ // 递归处理子节点
18356
+ if (Array.isArray(node.children)) {
18357
+ stack.push(...node.children);
18231
18358
  }
18232
- return /*#__PURE__*/jsx("div", {
18233
- style: wrapperStyle,
18234
- children: /*#__PURE__*/jsx(ConfigProvider, {
18235
- theme: theme,
18236
- children: /*#__PURE__*/jsx(Tree, {
18237
- blockNode: true,
18238
- showIcon: true,
18239
- selectedKeys: treeState.selectedKeys,
18240
- expandedKeys: treeState.expandedKeys,
18241
- onSelect: onSelect,
18242
- onExpand: expandable ? onExpand : undefined // 不允许展开时不绑定事件
18243
- ,
18244
- treeData: treeState.data,
18245
- titleRender: nodeData => /*#__PURE__*/jsx(TreeTitle$1, {
18246
- title: nodeData.title,
18247
- nodeData: nodeData,
18248
- isEditable: isEditable,
18249
- handleSave: handleRename,
18250
- handleDel: handleRemove,
18251
- handleAdd: handleCreate
18252
- }),
18253
- height: height,
18254
- style: treeStyle
18255
- })
18256
- })
18257
- }, "folder-directory");
18258
- }, [treeState.data, treeState.selectedKeys, treeState.expandedKeys]);
18259
- return {
18260
- directoryTree: MemoizedTree,
18261
- contents: treeState.contents,
18262
- currentPath: treeState.currentPath,
18263
- loading: treeState.loading,
18264
- originTreeData,
18265
- updateFileContents,
18266
- removeFile,
18267
- refreshTreeData,
18268
- // 暴露刷新方法
18269
- fetchFolderData: refreshTreeData // 也可以保留别名
18270
- };
18359
+ }
18360
+ return result;
18271
18361
  };
18272
18362
 
18273
- const createUrlBuilder = function () {
18274
- let config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
18363
+ /**
18364
+ * 通过路径查找树节点
18365
+ * @param {Array} treeData - 树数据
18366
+ * @param {string} targetPath - 要查找的路径
18367
+ * @param {Object} options - 配置选项
18368
+ * @param {boolean} options.exactMatch - 是否精确匹配(默认true)
18369
+ * @param {boolean} options.returnFirst - 是否返回第一个匹配项(默认false,返回所有匹配项)
18370
+ * @returns {Array} 包含匹配节点的数组
18371
+ */
18372
+ const findTreeNodeByPath = function (treeData, targetPath) {
18373
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
18275
18374
  const {
18276
- baseUrl = '',
18277
- isDev = false,
18278
- devBaseUrl = '',
18279
- isBrowser = false
18280
- } = config;
18281
- return path => {
18282
- if (!path) return '';
18375
+ exactMatch = true,
18376
+ returnFirst = false
18377
+ } = options;
18378
+ if (!Array.isArray(treeData) || typeof targetPath !== 'string' || !targetPath.trim()) {
18379
+ return [];
18380
+ }
18381
+ const result = [];
18382
+ const stack = [...treeData];
18383
+ while (stack.length) {
18384
+ const node = stack.pop();
18283
18385
 
18284
- // 如果传入的是完整 URL,直接返回
18285
- if (/^https?:\/\//.test(path)) {
18286
- return path;
18386
+ // 检查当前节点是否匹配 - 目录有 path 属性
18387
+ if (node.path) {
18388
+ const isMatch = exactMatch ? node.path === targetPath : node.path.includes(targetPath);
18389
+ if (isMatch) {
18390
+ result.push(node);
18391
+ if (returnFirst) {
18392
+ return result; // 找到第一个匹配项就结束
18393
+ }
18394
+ }
18287
18395
  }
18288
18396
 
18289
- // 去除 path 开头可能的多余斜杠
18290
- const cleanPath = path.replace(/^\/+/, '');
18291
-
18292
- // 开发环境使用 devBaseUrl,否则使用 baseUrl
18293
- const host = isDev ? devBaseUrl : baseUrl;
18294
-
18295
- // 如果没有配置 baseUrl,则根据环境返回相对路径或完整路径
18296
- if (!host) {
18297
- return isBrowser ? "/".concat(cleanPath) : cleanPath;
18397
+ // 递归处理子节点
18398
+ if (Array.isArray(node.children)) {
18399
+ stack.push(...node.children);
18298
18400
  }
18299
- return host ? "".concat(host, "/").concat(cleanPath) : "/".concat(cleanPath);
18300
- };
18401
+ }
18402
+ return result;
18301
18403
  };
18302
18404
 
18303
- const pathUtils = {
18304
- replaceRoot: path => path.replace(/^root$|^root\//, '')
18305
- };
18306
- const ResourcesView = _ref => {
18405
+ const useDirectoryTree = _ref => {
18307
18406
  let {
18308
- apiConfig = {},
18309
- features = {},
18310
- className = '',
18311
- style = {},
18312
- renderTreeHeader,
18313
- renderGridHeader,
18407
+ getFolderData,
18408
+ createFolder,
18409
+ removeFolderFile,
18410
+ renameFolderFile,
18411
+ mediaType,
18412
+ isEditable = true,
18413
+ batchOperations = true,
18314
18414
  withRootNode = true,
18315
- acceptFileTypes = "video/*,.mxf,application/mxf,video/mxf",
18316
- uploadTimeout = 60 * 60 * 1000,
18317
- searchPlaceholder = "Search ...",
18318
- refreshSignal = 0 // 外部刷新信号,数值变化时触发刷新
18415
+ height = 828,
18416
+ theme = {
18417
+ components: {
18418
+ Tree: {
18419
+ titleHeight: 30
18420
+ }
18421
+ }
18422
+ },
18423
+ treeStyle = {},
18424
+ // Tree 组件样式
18425
+ wrapperStyle = {
18426
+ paddingTop: 16
18427
+ },
18428
+ // 包装器样式
18429
+ expandable = true,
18430
+ // 新增:是否允许展开/折叠
18431
+ onSelectDir,
18432
+ // 新增:选择目录回调
18433
+ selectedDirPath = null // 新增:外部传入的选中目录名
18319
18434
  } = _ref;
18320
- const {
18321
- message,
18322
- modal
18323
- } = App.useApp();
18324
-
18325
- // 合并默认配置和传入配置
18326
- const mergedApiConfig = useMemo(() => _objectSpread2({
18327
- upload: '/api/dvr/media/source/upload',
18328
- download: '/download'
18329
- }, apiConfig), [apiConfig]);
18330
- const mergedFeatures = useMemo(() => _objectSpread2({
18331
- upload: true,
18332
- download: true,
18333
- copy: true,
18334
- delete: true,
18335
- search: true,
18336
- batchOperations: true
18337
- }, features), [features]);
18338
- const {
18339
- directoryTree,
18340
- contents,
18341
- currentPath,
18342
- loading,
18343
- originTreeData,
18344
- updateFileContents,
18345
- removeFile,
18346
- refreshTreeData
18347
- } = useDirectoryTree({
18348
- getFolderData: mergedApiConfig.getFolderData,
18349
- createFolder: mergedApiConfig.createFolder,
18350
- removeFolderFile: mergedApiConfig.removeFolderFile,
18351
- renameFolderFile: mergedApiConfig.renameFolderFile,
18352
- batchOperations: mergedFeatures.batchOperations,
18353
- withRootNode
18435
+ const [treeState, setTreeState] = useState({
18436
+ data: [],
18437
+ // 树结构数据
18438
+ selectedKeys: [],
18439
+ // 选中节点keys
18440
+ expandedKeys: [],
18441
+ // 展开节点keys
18442
+ currentPath: "",
18443
+ // 当前选中路径
18444
+ contents: [],
18445
+ // 当前选中节点的文件列表
18446
+ loading: false // 加载状态
18354
18447
  });
18448
+ const [originTreeData, setOriginTreeData] = useState([]);
18449
+ const updateTreeState = partialState => {
18450
+ setTreeState(prev => _objectSpread2(_objectSpread2({}, prev), partialState));
18451
+ };
18452
+
18453
+ // 错误处理
18454
+ const handleError = (error, operation) => {
18455
+ console.error("".concat(operation, " ERROR"), error);
18456
+ // 可以添加通知等统一错误处理
18457
+ };
18355
18458
 
18356
- // 监听外部刷新信号变化
18357
- useEffect(() => {
18358
- if (refreshTreeData && refreshSignal > 0) {
18359
- refreshTreeData();
18459
+ // 路径处理工具
18460
+ const pathUtils = {
18461
+ replaceRoot: path => path.replace(/^root\//, ''),
18462
+ getNewPath: (node, newTitle) => {
18463
+ const arr = node.path.split('/');
18464
+ arr[arr.length - 1] = newTitle;
18465
+ return arr.join('/');
18466
+ },
18467
+ isSamePath: (path1, path2) => {
18468
+ return pathUtils.replaceRoot(path1) === pathUtils.replaceRoot(path2);
18469
+ },
18470
+ getParentPath: path => {
18471
+ if (!path) return '';
18472
+ const parts = path.split('/');
18473
+ parts.pop(); // 移除最后一部分
18474
+ return parts.join('/') || '';
18360
18475
  }
18361
- }, [refreshSignal, refreshTreeData]);
18362
- const [showProgress, setShowProgress] = useState(false);
18363
- const [percent, setPercent] = useState(0);
18364
- const inputRef = useRef(null);
18365
- const uploadController = useRef(new AbortController());
18366
- const [modalVisible, setModalVisible] = useState(false);
18367
- // 处理批量操作
18368
- const [selectedItems, setSelectedItems] = useState([]);
18369
- const [indeterminate, setIndeterminate] = useState(false);
18370
- const [checkAll, setCheckAll] = useState(false);
18371
- const [batchLoading, setBatchLoading] = useState(false);
18372
- const [keyword, setKeyword] = useState('');
18373
- const [debouncedKeyword, setDebouncedKeyword] = useState('');
18476
+ };
18374
18477
 
18375
- // 创建防抖函数(延迟 300ms)
18376
- const debouncedSearch = useMemo(() => lodashExports.debounce(searchValue => {
18377
- setDebouncedKeyword(searchValue);
18378
- }, 300), []);
18478
+ // 根据路径确定选中的节点
18479
+ const determineSelectedNode = useCallback((treeNodes, path) => {
18480
+ if (treeNodes.length === 0) return null;
18379
18481
 
18380
- // 监听 keyword 变化并触发防抖
18381
- useEffect(() => {
18382
- debouncedSearch(keyword);
18383
- return () => debouncedSearch.cancel(); // 组件卸载时取消防抖
18384
- }, [keyword, debouncedSearch]);
18482
+ // 情况1:未传路径,使用第一个节点
18483
+ if (path === undefined || path === null) {
18484
+ return treeNodes[0];
18485
+ }
18385
18486
 
18386
- // 过滤内容
18387
- const filteredContents = useMemo(() => {
18388
- if (!debouncedKeyword) return contents;
18389
- return contents.filter(item => item.name.toLowerCase().includes(debouncedKeyword.toLowerCase()));
18390
- }, [contents, debouncedKeyword]);
18487
+ // 情况2:空字符串,不选中任何节点
18488
+ if (path.trim() === '') {
18489
+ return null;
18490
+ }
18391
18491
 
18392
- // 在组件卸载时取消上传
18393
- useEffect(() => {
18394
- return () => {
18395
- var _uploadController$cur;
18396
- return (_uploadController$cur = uploadController.current) === null || _uploadController$cur === void 0 ? void 0 : _uploadController$cur.abort();
18397
- };
18492
+ // 情况3:有值,查找对应节点
18493
+ try {
18494
+ const foundNodes = findTreeNodeByUrl(treeNodes, path);
18495
+ return (foundNodes === null || foundNodes === void 0 ? void 0 : foundNodes[0]) || null;
18496
+ } catch (error) {
18497
+ console.error('Error finding node by URL:', error);
18498
+ return null;
18499
+ }
18398
18500
  }, []);
18399
18501
 
18400
- // 重置批量状态的效果
18502
+ // 获取树结构(目录层级)
18503
+ const getTreeStructure = useCallback(async folder => {
18504
+ try {
18505
+ // 构建请求参数
18506
+ const requestParams = {
18507
+ folder: folder
18508
+ };
18509
+ if (mediaType) {
18510
+ requestParams.media_type = mediaType;
18511
+ }
18512
+ const data = await getFolderData(requestParams);
18513
+ if (!(data !== null && data !== void 0 && data.directoryTree)) {
18514
+ return null;
18515
+ }
18516
+
18517
+ // 只在初始化时保存原始数据
18518
+ if (folder === '') {
18519
+ setOriginTreeData(data.directoryTree);
18520
+ }
18521
+ return buildDirectoryTree(data.directoryTree, {
18522
+ withRoot: withRootNode
18523
+ });
18524
+ } catch (error) {
18525
+ handleError(error, 'GET TREE STRUCTURE');
18526
+ return null;
18527
+ }
18528
+ }, [mediaType, withRootNode]);
18529
+
18530
+ // 初始化树结构
18531
+ const initTreeData = useCallback(async () => {
18532
+ try {
18533
+ updateTreeState({
18534
+ loading: true
18535
+ });
18536
+ const treeNodes = await getTreeStructure('');
18537
+ if (!(treeNodes !== null && treeNodes !== void 0 && treeNodes.length)) {
18538
+ updateTreeState({
18539
+ data: [],
18540
+ selectedKeys: [],
18541
+ currentPath: '',
18542
+ contents: [],
18543
+ loading: false
18544
+ });
18545
+ return;
18546
+ }
18547
+
18548
+ // 确定选中的节点
18549
+ let selectedNode = determineSelectedNode(treeNodes, selectedDirPath);
18550
+
18551
+ // 获取初始选中节点的contents
18552
+ let initialContents = [];
18553
+ if (selectedNode) {
18554
+ initialContents = selectedNode.contents;
18555
+ }
18556
+ updateTreeState({
18557
+ data: treeNodes,
18558
+ selectedKeys: selectedNode ? [selectedNode.key] : [],
18559
+ expandedKeys: getAllNodeKeys(treeNodes),
18560
+ currentPath: (selectedNode === null || selectedNode === void 0 ? void 0 : selectedNode.path) || '',
18561
+ contents: initialContents,
18562
+ loading: false
18563
+ });
18564
+ } catch (error) {
18565
+ handleError(error, 'INIT TREE DATA');
18566
+ updateTreeState({
18567
+ loading: false
18568
+ });
18569
+ }
18570
+ }, [getTreeStructure, selectedDirPath]);
18571
+
18572
+ // 初始化数据
18401
18573
  useEffect(() => {
18402
- setSelectedItems([]);
18403
- setIndeterminate(false);
18404
- setCheckAll(false);
18405
- }, [currentPath]);
18574
+ initTreeData();
18575
+ }, [initTreeData]);
18406
18576
 
18407
- // 上传相关函数
18408
- const onTriggerUpload = useCallback(() => {
18409
- var _inputRef$current;
18410
- (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.click();
18411
- }, []);
18412
- const onUploadFile = async event => {
18413
- const files = event.target.files;
18414
- if (!(files !== null && files !== void 0 && files.length)) return;
18577
+ // 选择节点
18578
+ const onSelect = useCallback(async (keys, info) => {
18579
+ // 如果选中了 root 节点,并且有传 onSelectDir 回调,则直接返回
18580
+ if (info.node.path === 'root' && onSelectDir) return;
18581
+ if (!keys.length || keys[0] === treeState.selectedKeys[0] && pathUtils.isSamePath(info.node.path, treeState.currentPath)) {
18582
+ return;
18583
+ }
18584
+ updateTreeState({
18585
+ selectedKeys: keys,
18586
+ currentPath: info.node.path,
18587
+ loading: true
18588
+ });
18415
18589
 
18416
- // 初始化上传状态
18417
- setShowProgress(true);
18418
- setPercent(0);
18590
+ // 触发 onSelectDir 回调(如果存在)
18591
+ if (onSelectDir) {
18592
+ onSelectDir(info.node.rawData);
18593
+ }
18419
18594
  try {
18420
- await uploadFiles(files, pathUtils.replaceRoot(currentPath));
18421
- // 延迟2s刷新数据
18422
- await new Promise(resolve => setTimeout(resolve, 2000));
18423
- await updateFileContents();
18595
+ // 模拟延迟加载
18596
+ // eslint-disable-next-line no-promise-executor-return
18597
+ await new Promise(resolve => setTimeout(resolve, 300));
18598
+ updateTreeState({
18599
+ contents: info.node.contents,
18600
+ loading: false
18601
+ });
18424
18602
  } catch (error) {
18425
- if (axios.isCancel(error)) {
18426
- console.log('Upload canceled');
18427
- } else if (error.response) {
18428
- var _error$response$data;
18429
- console.error("Upload failed: ".concat(((_error$response$data = error.response.data) === null || _error$response$data === void 0 ? void 0 : _error$response$data.message) || error.response.status));
18430
- } else {
18431
- console.error("Upload error: ".concat(error.message));
18432
- }
18433
- } finally {
18434
- resetUploadState();
18603
+ handleError(error, 'SELECT NODE');
18604
+ updateTreeState({
18605
+ loading: false
18606
+ });
18435
18607
  }
18436
- };
18437
- const uploadFiles = async (files, folder) => {
18438
- // 计算所有文件的总大小
18439
- const totalSize = calculateTotalSize(files);
18440
- // 存储每个文件的上一次 loaded 值(避免重复计算)
18441
- const previousLoadedMap = new Map();
18442
- let uploadedSize = 0; // 记录已上传的字节数
18608
+ }, [treeState.selectedKeys, treeState.currentPath]);
18443
18609
 
18444
- const uploadTasks = Array.from(files).map(file => uploadSingleFile(file, folder, previousLoadedMap, newLoaded => {
18445
- uploadedSize += newLoaded;
18446
- updateProgress(uploadedSize, totalSize);
18447
- }));
18448
- await Promise.all(uploadTasks);
18449
- };
18610
+ // 展开节点
18611
+ const onExpand = useCallback(keys => {
18612
+ updateTreeState({
18613
+ expandedKeys: keys
18614
+ });
18615
+ }, []);
18450
18616
 
18451
- // 计算总文件大小
18452
- const calculateTotalSize = files => Array.from(files).reduce((sum, file) => sum + file.size, 0);
18617
+ // 创建文件夹
18618
+ const handleCreate = useCallback(async (node, newTitle) => {
18619
+ if (!(newTitle !== null && newTitle !== void 0 && newTitle.trim())) return false;
18620
+ try {
18621
+ const path = pathUtils.replaceRoot("".concat(node.path, "/").concat(newTitle));
18622
+ await createFolder({
18623
+ path
18624
+ });
18453
18625
 
18454
- // 上传单个文件
18455
- const uploadSingleFile = (file, folder, previousLoadedMap, onProgress) => {
18456
- const formData = new FormData();
18457
- formData.append('file', file);
18458
- formData.append('folder', folder);
18459
- return axios.post(mergedApiConfig.upload, formData, {
18460
- timeout: uploadTimeout,
18461
- headers: {
18462
- 'Content-Type': 'multipart/form-data'
18463
- },
18464
- // signal: uploadController.current.signal,
18465
- onUploadProgress: progressEvent => {
18466
- if (progressEvent.lengthComputable) {
18467
- // 获取当前文件的上一次 loaded 值(默认 0)
18468
- const previousLoaded = previousLoadedMap.get(file.name) || 0;
18469
- // 计算新增的字节数(避免重复累加)
18470
- const newLoaded = progressEvent.loaded - previousLoaded;
18471
- // 更新当前文件的 loaded 值
18472
- previousLoadedMap.set(file.name, progressEvent.loaded);
18473
- onProgress(newLoaded);
18474
- }
18626
+ // 刷新树结构
18627
+ const newTreeData = await getTreeStructure('');
18628
+ if (!newTreeData) return;
18629
+
18630
+ // 查找新增的节点
18631
+ const newPath = "".concat(node.path, "/").concat(newTitle);
18632
+ const foundNodes = findTreeNodeByPath(newTreeData, newPath, {
18633
+ exactMatch: true,
18634
+ returnFirst: true
18635
+ });
18636
+ const addedNode = foundNodes[0];
18637
+ if (addedNode) {
18638
+ updateTreeState({
18639
+ data: newTreeData,
18640
+ selectedKeys: [addedNode.key],
18641
+ expandedKeys: [...treeState.expandedKeys, addedNode.key, node.key],
18642
+ currentPath: addedNode.path,
18643
+ contents: []
18644
+ });
18475
18645
  }
18476
- });
18477
- };
18478
- const updateProgress = (uploadedSize, totalSize) => {
18479
- const totalPercent = Math.round(uploadedSize / totalSize * 100);
18480
- setPercent(totalPercent);
18481
- };
18646
+ } catch (error) {
18647
+ handleError(error, 'CREATE FOLDER');
18648
+ }
18649
+ }, [getTreeStructure, treeState.expandedKeys]);
18482
18650
 
18483
- // 重置上传状态
18484
- const resetUploadState = () => {
18485
- setShowProgress(false);
18486
- setPercent(0);
18487
- if (inputRef.current) inputRef.current.value = "";
18488
- };
18651
+ // 删除文件/文件夹
18652
+ const handleRemove = useCallback(async node => {
18653
+ if (!node.path) return;
18654
+ try {
18655
+ // 执行删除
18656
+ batchOperations ? await removeFolderFile({
18657
+ paths: [{
18658
+ path: pathUtils.replaceRoot(node.path)
18659
+ }]
18660
+ }) : await removeFolderFile({
18661
+ path: pathUtils.replaceRoot(node.path)
18662
+ });
18489
18663
 
18490
- // 文件操作函数
18491
- const downloadFile = async url => {
18664
+ // 检查是否删除的是当前选中的节点
18665
+ const isSelectedNode = treeState.selectedKeys[0] === node.key;
18666
+ const isCurrentPath = pathUtils.isSamePath(treeState.currentPath, node.path);
18667
+ if (isSelectedNode || isCurrentPath) {
18668
+ // 查找父节点
18669
+ const parentPath = pathUtils.getParentPath(node.path);
18670
+ if (parentPath) {
18671
+ // 刷新树结构
18672
+ const newTreeData = await getTreeStructure('');
18673
+ if (!newTreeData) return;
18674
+
18675
+ // 尝试找到父节点
18676
+ const foundParentNodes = findTreeNodeByPath(newTreeData, parentPath, {
18677
+ exactMatch: true,
18678
+ returnFirst: true
18679
+ });
18680
+ const parentNode = foundParentNodes[0];
18681
+ if (parentNode) {
18682
+ // 选中父节点
18683
+ updateTreeState({
18684
+ data: newTreeData,
18685
+ selectedKeys: [parentNode.key],
18686
+ currentPath: parentNode.path,
18687
+ contents: parentNode.contents
18688
+ });
18689
+ } else {
18690
+ // 找不到父节点,选中第一个根节点
18691
+ const firstNode = newTreeData[0];
18692
+ if (firstNode) {
18693
+ updateTreeState({
18694
+ data: newTreeData,
18695
+ selectedKeys: [firstNode.key],
18696
+ currentPath: firstNode.path,
18697
+ contents: firstNode.contents
18698
+ });
18699
+ } else {
18700
+ // 树为空
18701
+ updateTreeState({
18702
+ data: newTreeData,
18703
+ selectedKeys: [],
18704
+ currentPath: '',
18705
+ contents: []
18706
+ });
18707
+ }
18708
+ }
18709
+ } else {
18710
+ // 删除的是根节点,重新初始化
18711
+ await initTreeData();
18712
+ }
18713
+ } else {
18714
+ // 删除的不是当前选中节点,只刷新树结构
18715
+ const newTreeData = await getTreeStructure('');
18716
+ if (newTreeData) {
18717
+ updateTreeState({
18718
+ data: newTreeData
18719
+ });
18720
+ }
18721
+ }
18722
+ } catch (error) {
18723
+ handleError(error, 'REMOVE FOLDER/FILE');
18724
+ }
18725
+ }, [getTreeStructure, initTreeData, treeState.selectedKeys, treeState.currentPath]);
18726
+
18727
+ // 重命名
18728
+ const handleRename = useCallback(async node => {
18729
+ const newPath = pathUtils.getNewPath(node, node.title);
18730
+ if (pathUtils.isSamePath(node.path, newPath)) return false;
18492
18731
  try {
18493
- const isDev = process.env.NODE_ENV === 'development';
18494
- const baseUrl = isDev ? process.env.API_HOST // 开发环境默认值
18495
- : window.location.origin; // 生产环境用当前域名
18732
+ await renameFolderFile({
18733
+ old_path: pathUtils.replaceRoot(node.path),
18734
+ new_path: pathUtils.replaceRoot(newPath)
18735
+ });
18496
18736
 
18497
- const link = document.createElement('a');
18498
- link.href = "".concat(baseUrl).concat(mergedApiConfig.download, "/").concat(url);
18499
- // link.download = filename;
18500
- link.style.display = 'none';
18501
- document.body.appendChild(link);
18502
- link.click();
18503
- document.body.removeChild(link);
18737
+ // 刷新树结构
18738
+ const newTreeData = await getTreeStructure('');
18739
+ if (newTreeData) {
18740
+ updateTreeState({
18741
+ data: newTreeData
18742
+ });
18743
+ }
18504
18744
  } catch (error) {
18505
- console.error("Download failed:", error);
18745
+ handleError(error, 'RENAME FOLDER/FILE');
18506
18746
  }
18507
- };
18508
- const copyFile = () => {
18509
- setModalVisible(true);
18510
- };
18511
- const onContextMenu = (key, item) => {
18512
- if (key === 'del') removeFile(item.url);
18513
- if (key === 'download') downloadFile(item.url);
18514
- if (key === 'copy') copyFile(item.url);
18515
- };
18516
- const handleBatchRemove = useCallback(async () => {
18517
- if (!selectedItems.length) return;
18518
- modal.confirm({
18519
- icon: /*#__PURE__*/jsx(ExclamationCircleFilled, {}),
18520
- title: "Are you sure you want to delete ".concat(selectedItems.length, " files?"),
18521
- cancelText: "No",
18522
- okText: "Yes",
18523
- onOk: async () => {
18524
- try {
18525
- // 批量处理所有删除请求
18526
- await mergedApiConfig.removeFolderFile({
18527
- paths: selectedItems.map(itemUrl => ({
18528
- path: itemUrl
18529
- }))
18530
- });
18747
+ }, [getTreeStructure]);
18531
18748
 
18532
- // 更新状态
18533
- setSelectedItems([]);
18534
- setIndeterminate(false);
18535
- setCheckAll(false);
18536
- await updateFileContents(); // 刷新文件列表
18537
- } catch (error) {
18538
- console.error('Batch deletion error:', error);
18749
+ // 刷新树数据
18750
+ const refreshTreeData = useCallback(async () => {
18751
+ try {
18752
+ const newTreeData = await getTreeStructure('');
18753
+ if (!newTreeData) return;
18754
+
18755
+ // 保持当前选中状态
18756
+ const currentPath = treeState.currentPath;
18757
+ if (currentPath) {
18758
+ const foundNodes = findTreeNodeByPath(newTreeData, currentPath, {
18759
+ exactMatch: true,
18760
+ returnFirst: true
18761
+ });
18762
+ const currentNode = foundNodes[0];
18763
+ if (currentNode) {
18764
+ updateTreeState({
18765
+ data: newTreeData,
18766
+ selectedKeys: [currentNode.key],
18767
+ contents: currentNode.contents
18768
+ });
18769
+ } else {
18770
+ // 当前节点不存在,选中第一个节点
18771
+ const firstNode = newTreeData[0];
18772
+ if (firstNode) {
18773
+ updateTreeState({
18774
+ data: newTreeData,
18775
+ selectedKeys: [firstNode.key],
18776
+ currentPath: firstNode.path,
18777
+ contents: firstNode.contents
18778
+ });
18779
+ } else {
18780
+ updateTreeState({
18781
+ data: newTreeData,
18782
+ selectedKeys: [],
18783
+ currentPath: '',
18784
+ contents: []
18785
+ });
18786
+ }
18539
18787
  }
18788
+ } else {
18789
+ updateTreeState({
18790
+ data: newTreeData
18791
+ });
18540
18792
  }
18541
- });
18542
- }, [selectedItems, mergedApiConfig, updateFileContents]);
18543
- const handleCopyConfirm = useCallback(async newPath => {
18544
- setBatchLoading(true);
18793
+ } catch (error) {
18794
+ handleError(error, 'REFRESH TREE DATA');
18795
+ }
18796
+ }, [getTreeStructure, treeState.currentPath]);
18797
+
18798
+ // 更新文件contents (上传右侧文件或删除右侧文件 成功后的回调函数)
18799
+ const updateFileContents = useCallback(async () => {
18800
+ if (!treeState.currentPath) return;
18545
18801
  try {
18546
- // 批量处理所有拷贝请求
18547
- await mergedApiConfig.copyFile({
18548
- old_paths: selectedItems.map(itemUrl => ({
18549
- old_path: itemUrl
18550
- })),
18551
- new_path: newPath
18802
+ const newTreeData = await getTreeStructure('');
18803
+ if (!newTreeData) return;
18804
+ const foundNodes = findTreeNodeByPath(newTreeData, treeState.currentPath, {
18805
+ exactMatch: true,
18806
+ returnFirst: true
18552
18807
  });
18808
+ const currentNode = foundNodes[0];
18809
+ if (currentNode) {
18810
+ updateTreeState({
18811
+ contents: currentNode.contents
18812
+ });
18813
+ }
18814
+ } catch (error) {
18815
+ handleError(error, 'UPDATE FILE CONTENTS');
18816
+ }
18817
+ }, [getTreeStructure, treeState.currentPath]);
18553
18818
 
18554
- // 更新状态
18555
- setSelectedItems([]);
18556
- setIndeterminate(false);
18557
- setCheckAll(false);
18558
- message.success('Success');
18559
- setModalVisible(false);
18560
-
18561
- // 延迟2s刷新数据
18562
- await new Promise(resolve => setTimeout(resolve, 2000));
18819
+ // 删除文件
18820
+ const removeFile = useCallback(async url => {
18821
+ try {
18822
+ await removeFolderFile({
18823
+ path: url
18824
+ });
18563
18825
  await updateFileContents();
18564
18826
  } catch (error) {
18565
- console.error("Batch copy error:", error);
18566
- } finally {
18567
- setBatchLoading(false);
18827
+ handleError(error, 'REMOVE FILE');
18568
18828
  }
18569
- }, [selectedItems, mergedApiConfig, updateFileContents]);
18570
-
18571
- // 处理单个项目的选择变化
18572
- const handleSelectChange = (item, checked) => {
18573
- const newSelectedItems = checked ? [...selectedItems, item.url] : selectedItems.filter(url => url !== item.url);
18574
- setSelectedItems(newSelectedItems);
18575
- setIndeterminate(!!newSelectedItems.length && newSelectedItems.length !== filteredContents.length);
18576
- setCheckAll(newSelectedItems.length === filteredContents.length);
18577
- };
18578
-
18579
- // 处理全选/取消全选
18580
- const handleSelectAll = e => {
18581
- const checked = e.target.checked;
18582
- setSelectedItems(checked ? filteredContents.map(item => item.url) : []);
18583
- setIndeterminate(false);
18584
- setCheckAll(checked);
18585
- };
18586
-
18587
- // 搜索框变化处理
18588
- const handleSearchChange = e => {
18589
- setKeyword(e.target.value);
18829
+ }, [updateFileContents]);
18830
+ const MemoizedTree = useMemo(() => {
18831
+ var _treeState$data;
18832
+ if (!((_treeState$data = treeState.data) !== null && _treeState$data !== void 0 && _treeState$data.length)) {
18833
+ return /*#__PURE__*/jsx(Empty, {
18834
+ image: Empty.PRESENTED_IMAGE_SIMPLE
18835
+ });
18836
+ }
18837
+ return /*#__PURE__*/jsx("div", {
18838
+ style: wrapperStyle,
18839
+ children: /*#__PURE__*/jsx(ConfigProvider, {
18840
+ theme: theme,
18841
+ children: /*#__PURE__*/jsx(Tree, {
18842
+ blockNode: true,
18843
+ showIcon: true,
18844
+ selectedKeys: treeState.selectedKeys,
18845
+ expandedKeys: treeState.expandedKeys,
18846
+ onSelect: onSelect,
18847
+ onExpand: expandable ? onExpand : undefined // 不允许展开时不绑定事件
18848
+ ,
18849
+ treeData: treeState.data,
18850
+ titleRender: nodeData => /*#__PURE__*/jsx(TreeTitle$1, {
18851
+ title: nodeData.title,
18852
+ nodeData: nodeData,
18853
+ isEditable: isEditable,
18854
+ handleSave: handleRename,
18855
+ handleDel: handleRemove,
18856
+ handleAdd: handleCreate
18857
+ }),
18858
+ height: height,
18859
+ style: treeStyle
18860
+ })
18861
+ })
18862
+ }, "folder-directory");
18863
+ }, [treeState.data, treeState.selectedKeys, treeState.expandedKeys]);
18864
+ return {
18865
+ directoryTree: MemoizedTree,
18866
+ contents: treeState.contents,
18867
+ currentPath: treeState.currentPath,
18868
+ loading: treeState.loading,
18869
+ originTreeData,
18870
+ updateFileContents,
18871
+ removeFile,
18872
+ refreshTreeData,
18873
+ // 暴露刷新方法
18874
+ fetchFolderData: refreshTreeData // 也可以保留别名
18590
18875
  };
18591
-
18592
- // 创建 URL 构建器
18593
- const getFullUrl = useMemo(() => {
18594
- return createUrlBuilder({
18595
- baseUrl: apiConfig.baseUrl,
18596
- devBaseUrl: apiConfig.devBaseUrl,
18597
- isDev: process.env.NODE_ENV === 'development',
18598
- isBrowser: typeof window !== 'undefined'
18599
- });
18600
- }, [apiConfig.baseUrl, apiConfig.devBaseUrl]);
18601
- return /*#__PURE__*/jsxs("div", {
18602
- className: "resources-view ".concat(className),
18603
- style: style,
18604
- children: [/*#__PURE__*/jsxs("div", {
18605
- className: "content-container",
18606
- children: [/*#__PURE__*/jsxs("div", {
18607
- className: "directory-tree",
18608
- children: [renderTreeHeader ? renderTreeHeader() : /*#__PURE__*/jsx("div", {
18609
- className: "search-bar"
18610
- }), directoryTree]
18611
- }), /*#__PURE__*/jsxs("div", {
18612
- className: "folder-contents",
18613
- children: [/*#__PURE__*/jsxs(Flex, {
18614
- justify: "center",
18615
- align: "center",
18616
- className: "search-bar",
18617
- children: [mergedFeatures.search && /*#__PURE__*/jsx(Input, {
18618
- value: keyword,
18619
- onChange: handleSearchChange,
18620
- placeholder: searchPlaceholder,
18621
- prefix: /*#__PURE__*/jsx(SearchOutlined, {}),
18622
- allowClear: true,
18623
- className: "search-input"
18624
- }), mergedFeatures.upload && /*#__PURE__*/jsxs(Fragment, {
18625
- children: [/*#__PURE__*/jsx(CloudUploadOutlined, {
18626
- className: "upload-icon",
18627
- title: "upload",
18628
- onClick: onTriggerUpload
18629
- }), /*#__PURE__*/jsx("input", {
18630
- ref: inputRef,
18631
- type: "file",
18632
- onChange: onUploadFile,
18633
- className: "hidden",
18634
- accept: acceptFileTypes,
18635
- multiple: true
18636
- })]
18637
- })]
18638
- }), /*#__PURE__*/jsxs("div", {
18639
- className: "media-grid-container",
18640
- onContextMenu: e => e.preventDefault(),
18641
- children: [renderGridHeader ? renderGridHeader() : filteredContents.length > 0 && mergedFeatures.batchOperations && /*#__PURE__*/jsx("div", {
18642
- className: "batch-operations",
18643
- children: /*#__PURE__*/jsxs(Space, {
18644
- size: "middle",
18645
- children: [/*#__PURE__*/jsx(Checkbox, {
18646
- indeterminate: indeterminate,
18647
- checked: checkAll,
18648
- onChange: handleSelectAll,
18649
- children: "Select All"
18650
- }), /*#__PURE__*/jsx(Button, {
18651
- type: "primary",
18652
- disabled: !selectedItems.length,
18653
- onClick: () => setModalVisible(true),
18654
- children: "Copy"
18655
- }), /*#__PURE__*/jsx(Button, {
18656
- className: "btn-gray",
18657
- disabled: !selectedItems.length,
18658
- onClick: handleBatchRemove,
18659
- children: "Delete"
18660
- })]
18661
- })
18662
- }), /*#__PURE__*/jsx(MediaGrid$1, {
18663
- items: filteredContents,
18664
- loading: loading,
18665
- onContextMenu: mergedFeatures.delete || mergedFeatures.download || mergedFeatures.copy ? onContextMenu : null,
18666
- selectedKeys: selectedItems,
18667
- showCheckbox: mergedFeatures.batchOperations,
18668
- onSelectChange: mergedFeatures.batchOperations ? handleSelectChange : null,
18669
- gridConfig: mergedFeatures.batchOperations ? {
18670
- gutter: 0,
18671
- column: 6
18672
- } : {
18673
- gutter: 24,
18674
- column: 6
18675
- },
18676
- features: mergedFeatures,
18677
- getFullUrl: getFullUrl
18678
- })]
18679
- })]
18680
- })]
18681
- }), showProgress && /*#__PURE__*/jsx(UploadProgress$1, {
18682
- percent: percent
18683
- }), /*#__PURE__*/jsx(SelectFolderPathModal$1, {
18684
- open: modalVisible,
18685
- directoryTree: originTreeData,
18686
- batchLoading: batchLoading,
18687
- onClose: () => setModalVisible(false),
18688
- onOk: handleCopyConfirm
18689
- })]
18690
- });
18691
18876
  };
18692
- var ResourcesView$1 = ResourcesView;
18693
18877
 
18694
18878
  export { ResourcesView$1 as ResourcesView, useDirectoryTree };
18695
18879
  //# sourceMappingURL=index.esm.js.map