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