seeder-resources-view 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1278 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var antd = require('antd');
5
+ var icons = require('@ant-design/icons');
6
+ var axios = require('axios');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+
9
+ function _defineProperty(e, r, t) {
10
+ return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
11
+ value: t,
12
+ enumerable: !0,
13
+ configurable: !0,
14
+ writable: !0
15
+ }) : e[r] = t, e;
16
+ }
17
+ function _extends() {
18
+ return _extends = Object.assign ? Object.assign.bind() : function (n) {
19
+ for (var e = 1; e < arguments.length; e++) {
20
+ var t = arguments[e];
21
+ for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
22
+ }
23
+ return n;
24
+ }, _extends.apply(null, arguments);
25
+ }
26
+ function _objectDestructuringEmpty(t) {
27
+ if (null == t) throw new TypeError("Cannot destructure " + t);
28
+ }
29
+ function ownKeys(e, r) {
30
+ var t = Object.keys(e);
31
+ if (Object.getOwnPropertySymbols) {
32
+ var o = Object.getOwnPropertySymbols(e);
33
+ r && (o = o.filter(function (r) {
34
+ return Object.getOwnPropertyDescriptor(e, r).enumerable;
35
+ })), t.push.apply(t, o);
36
+ }
37
+ return t;
38
+ }
39
+ function _objectSpread2(e) {
40
+ for (var r = 1; r < arguments.length; r++) {
41
+ var t = null != arguments[r] ? arguments[r] : {};
42
+ r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
43
+ _defineProperty(e, r, t[r]);
44
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
45
+ Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
46
+ });
47
+ }
48
+ return e;
49
+ }
50
+ function _toPrimitive(t, r) {
51
+ if ("object" != typeof t || !t) return t;
52
+ var e = t[Symbol.toPrimitive];
53
+ if (void 0 !== e) {
54
+ var i = e.call(t, r || "default");
55
+ if ("object" != typeof i) return i;
56
+ throw new TypeError("@@toPrimitive must return a primitive value.");
57
+ }
58
+ return ("string" === r ? String : Number)(t);
59
+ }
60
+ function _toPropertyKey(t) {
61
+ var i = _toPrimitive(t, "string");
62
+ return "symbol" == typeof i ? i : i + "";
63
+ }
64
+
65
+ const BASE_MENU_ITEMS = [{
66
+ label: 'Download',
67
+ key: 'download',
68
+ featureKey: 'download'
69
+ }, {
70
+ label: 'Copy',
71
+ key: 'copy',
72
+ featureKey: 'copy'
73
+ }, {
74
+ label: 'Delete',
75
+ key: 'del',
76
+ featureKey: 'delete'
77
+ }];
78
+ const MediaGridItem = /*#__PURE__*/react.memo(_ref => {
79
+ let {
80
+ item,
81
+ onContextMenu,
82
+ menuItems = DEFAULT_MENU_ITEMS,
83
+ checked,
84
+ onSelectChange,
85
+ showCheckbox = true,
86
+ onClick,
87
+ getFullUrl
88
+ } = _ref;
89
+ const handleMenuClick = react.useCallback(_ref2 => {
90
+ let {
91
+ key
92
+ } = _ref2;
93
+ return onContextMenu(key, item);
94
+ }, [item, onContextMenu]);
95
+ const handleItemClick = react.useCallback(() => {
96
+ onClick === null || onClick === void 0 || onClick(item);
97
+ }, [item, onClick]);
98
+ const handleCheckboxChange = react.useCallback(e => {
99
+ e.stopPropagation();
100
+ onSelectChange === null || onSelectChange === void 0 || onSelectChange(item, e.target.checked);
101
+ }, [item, onSelectChange]);
102
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Dropdown, {
103
+ menu: {
104
+ items: menuItems,
105
+ onClick: handleMenuClick
106
+ },
107
+ trigger: ['contextMenu'],
108
+ disabled: !onContextMenu,
109
+ children: /*#__PURE__*/jsxRuntime.jsxs(antd.List.Item, {
110
+ className: "media-grid-item ".concat(checked ? 'media-grid-item-selected' : ''),
111
+ title: item.name,
112
+ onClick: handleItemClick,
113
+ children: [showCheckbox && /*#__PURE__*/jsxRuntime.jsx("div", {
114
+ className: "media-grid-checkbox-wrapper",
115
+ onClick: e => e.stopPropagation(),
116
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Checkbox, {
117
+ checked: checked,
118
+ onChange: handleCheckboxChange
119
+ })
120
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
121
+ className: "media-grid-thumbnail",
122
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Image, {
123
+ width: "100%",
124
+ height: "100%",
125
+ src: getFullUrl(item.head_thumbnail),
126
+ preview: false
127
+ })
128
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
129
+ className: "media-grid-info",
130
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Typography.Text, {
131
+ ellipsis: true,
132
+ children: item.name
133
+ })
134
+ })]
135
+ })
136
+ });
137
+ });
138
+ const MediaGrid = /*#__PURE__*/react.memo(_ref3 => {
139
+ let {
140
+ items = [],
141
+ loading = false,
142
+ onContextMenu,
143
+ selectedKeys = [],
144
+ onSelectChange,
145
+ menuItems: customMenuItems,
146
+ gridConfig = {
147
+ gutter: 0,
148
+ column: 6
149
+ },
150
+ showCheckbox = true,
151
+ onClick,
152
+ features = {},
153
+ getFullUrl
154
+ } = _ref3;
155
+ // 合并默认 features 和传入的 features
156
+ const mergedFeatures = useMemo(() => _objectSpread2({
157
+ download: true,
158
+ copy: true,
159
+ delete: true
160
+ }, features), [features]);
161
+
162
+ // 根据 features 动态生成 menuItems
163
+ const menuItems = useMemo(() => {
164
+ // 如果传入了自定义 menuItems,则优先使用
165
+ if (customMenuItems) return customMenuItems;
166
+
167
+ // 否则根据 features 过滤出可用的菜单项
168
+ return BASE_MENU_ITEMS.filter(item => mergedFeatures[item.featureKey] !== false);
169
+ }, [customMenuItems, mergedFeatures]);
170
+ const renderItem = react.useCallback(item => /*#__PURE__*/jsxRuntime.jsx(MediaGridItem, {
171
+ item: item,
172
+ onContextMenu: menuItems.length > 0 ? onContextMenu : null,
173
+ menuItems: menuItems,
174
+ checked: selectedKeys.includes(item.url) // 使用url作为唯一标识
175
+ ,
176
+ onSelectChange: onSelectChange,
177
+ showCheckbox: showCheckbox,
178
+ onClick: onClick,
179
+ getFullUrl: getFullUrl // 传递 getFullUrl 给子组件
180
+ }), [onContextMenu, menuItems, selectedKeys, onSelectChange, showCheckbox, onClick]);
181
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Spin, {
182
+ spinning: loading,
183
+ size: "large",
184
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.List, {
185
+ grid: gridConfig,
186
+ dataSource: items,
187
+ renderItem: renderItem,
188
+ pagination: false
189
+ // locale={{ emptyText: 'No media files found' }}
190
+ })
191
+ });
192
+ });
193
+ var MediaGrid$1 = MediaGrid;
194
+
195
+ const SelectFolderPathModal = _ref => {
196
+ let {
197
+ open,
198
+ onClose,
199
+ onOk,
200
+ directoryTree,
201
+ loading: externalLoading,
202
+ batchLoading,
203
+ title = "Select Folder Path",
204
+ okText = "Copy",
205
+ cancelText = "Cancel",
206
+ placeholder = "Please select (click arrow to expand)",
207
+ width = 520,
208
+ formItemLabel = "Folder Path",
209
+ showFullPath = false,
210
+ disabled = false,
211
+ allowClear = true,
212
+ expandTrigger = "hover"
213
+ } = _ref;
214
+ const [form] = antd.Form.useForm();
215
+ const [options, setOptions] = react.useState([]);
216
+ const [internalLoading, setInternalLoading] = react.useState(false);
217
+ const [selectedPath, setSelectedPath] = react.useState([]);
218
+ const loading = externalLoading || internalLoading;
219
+
220
+ // 将 directoryTree 转换为 Cascader 需要的 options 格式
221
+ const convertToCascaderOptions = react.useCallback(treeData => {
222
+ if (!treeData || !Array.isArray(treeData)) return [];
223
+ return treeData.map(item => {
224
+ if (!item) return null;
225
+ const option = {
226
+ value: item.id,
227
+ label: item.sd_index || item.name,
228
+ isLeaf: !item.contents || item.contents.length === 0,
229
+ path: item.path,
230
+ rawData: item // 保留原始数据
231
+ };
232
+ if (item.contents && Array.isArray(item.contents) && item.contents.length > 0) {
233
+ // 递归处理子目录
234
+ option.children = convertToCascaderOptions(item.contents.filter(child => child && child.type === 'directory'));
235
+ }
236
+ return option;
237
+ }).filter(Boolean);
238
+ }, []);
239
+
240
+ // 初始化 options
241
+ react.useEffect(() => {
242
+ if (open && directoryTree) {
243
+ setInternalLoading(true);
244
+ try {
245
+ const formattedOptions = convertToCascaderOptions(directoryTree);
246
+ setOptions(formattedOptions);
247
+ } catch (error) {
248
+ console.error('Error converting directory tree:', error);
249
+ } finally {
250
+ setInternalLoading(false);
251
+ }
252
+ }
253
+ }, [open, directoryTree, convertToCascaderOptions]);
254
+
255
+ // 根据value路径查找对应的节点信息
256
+ const getSelectedNodes = react.useCallback(function (valuePath, currentOptions) {
257
+ let collectedNodes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
258
+ if (!valuePath || valuePath.length === 0 || !currentOptions) return collectedNodes;
259
+ const currentValue = valuePath[0];
260
+ const currentNode = currentOptions.find(opt => opt && opt.value === currentValue);
261
+ if (!currentNode) return collectedNodes;
262
+ collectedNodes.push(currentNode);
263
+ if (valuePath.length > 1 && currentNode.children) {
264
+ return getSelectedNodes(valuePath.slice(1), currentNode.children, collectedNodes);
265
+ }
266
+ return collectedNodes;
267
+ }, []);
268
+ const handleOk = react.useCallback(() => {
269
+ form.validateFields().then(values => {
270
+ if (!values.path) {
271
+ message.warning('Please select a folder path');
272
+ return;
273
+ }
274
+ const selectedNodes = getSelectedNodes(values.path, options);
275
+ if (selectedNodes.length === 0) {
276
+ message.warning('Invalid path selected');
277
+ return;
278
+ }
279
+ const lastNode = selectedNodes[selectedNodes.length - 1];
280
+
281
+ // 返回完整路径或最后节点路径
282
+ const resultPath = showFullPath ? selectedNodes.map(node => node.path).join('/') : lastNode.path;
283
+ onOk === null || onOk === void 0 || onOk(resultPath);
284
+ }).catch(error => {
285
+ console.error('Validation failed:', error);
286
+ });
287
+ }, [form, options, getSelectedNodes, onOk, showFullPath]);
288
+ const handleAfterClose = react.useCallback(() => {
289
+ form.resetFields();
290
+ setSelectedPath([]);
291
+ }, [form]);
292
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Modal, {
293
+ title: title,
294
+ open: open,
295
+ onCancel: onClose,
296
+ afterClose: handleAfterClose,
297
+ width: width,
298
+ destroyOnHidden: true,
299
+ footer: [/*#__PURE__*/jsxRuntime.jsx(antd.Button, {
300
+ onClick: onClose,
301
+ disabled: batchLoading,
302
+ children: cancelText
303
+ }, "cancel"), /*#__PURE__*/jsxRuntime.jsx(antd.Button, {
304
+ type: "primary",
305
+ onClick: handleOk,
306
+ loading: batchLoading,
307
+ disabled: disabled || selectedPath.length === 0,
308
+ children: okText
309
+ }, "submit")],
310
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Spin, {
311
+ spinning: loading,
312
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Form, {
313
+ form: form,
314
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Form.Item, {
315
+ name: "path",
316
+ label: formItemLabel,
317
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Cascader, {
318
+ options: options,
319
+ changeOnSelect: true,
320
+ placeholder: placeholder,
321
+ style: {
322
+ width: '100%'
323
+ },
324
+ disabled: disabled || loading,
325
+ expandTrigger: expandTrigger,
326
+ allowClear: allowClear
327
+ })
328
+ })
329
+ })
330
+ })
331
+ });
332
+ };
333
+ var SelectFolderPathModal$1 = /*#__PURE__*/react.memo(SelectFolderPathModal);
334
+
335
+ const UploadProgress = _ref => {
336
+ let {
337
+ percent
338
+ } = _ref;
339
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
340
+ className: "upload-progress",
341
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Progress, {
342
+ type: "circle",
343
+ percent: percent
344
+ })
345
+ });
346
+ };
347
+ var UploadProgress$1 = /*#__PURE__*/react.memo(UploadProgress);
348
+
349
+ const PopoverContent = /*#__PURE__*/react.forwardRef((_ref, ref) => {
350
+ let {
351
+ onClose,
352
+ onConfirm
353
+ } = _ref;
354
+ const [value, setValue] = react.useState('');
355
+ const handleConfirm = react.useCallback(() => {
356
+ onConfirm(value, () => setValue(''));
357
+ }, [confirm, value]);
358
+ const handleKeyDown = react.useCallback(e => {
359
+ if (e.key === 'Enter') {
360
+ handleConfirm();
361
+ }
362
+ }, [handleConfirm]);
363
+ return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
364
+ children: [/*#__PURE__*/jsxRuntime.jsx(antd.Form.Item, {
365
+ label: "Folder Name",
366
+ style: {
367
+ marginBottom: 8
368
+ },
369
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Input, {
370
+ ref: ref,
371
+ value: value,
372
+ onChange: e => setValue(e.target.value),
373
+ onKeyDown: handleKeyDown,
374
+ size: "small",
375
+ style: {
376
+ width: 120
377
+ },
378
+ autoFocus: true
379
+ })
380
+ }), /*#__PURE__*/jsxRuntime.jsxs(antd.Flex, {
381
+ justify: "flex-end",
382
+ gap: "small",
383
+ children: [/*#__PURE__*/jsxRuntime.jsx(antd.Button, {
384
+ type: "default",
385
+ size: "small",
386
+ onClick: onClose,
387
+ children: "Close"
388
+ }), /*#__PURE__*/jsxRuntime.jsx(antd.Button, {
389
+ type: "primary",
390
+ size: "small",
391
+ onClick: handleConfirm,
392
+ children: "OK"
393
+ })]
394
+ })]
395
+ });
396
+ });
397
+ const EditablePopover = _ref2 => {
398
+ let {
399
+ children,
400
+ add
401
+ } = _ref2;
402
+ const inputRef = react.useRef(null);
403
+ const [open, setOpen] = react.useState(false);
404
+ react.useEffect(() => {
405
+ if (open && inputRef.current) {
406
+ const timer = setTimeout(() => {
407
+ var _inputRef$current;
408
+ (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.focus();
409
+ }, 100);
410
+ return () => clearTimeout(timer);
411
+ }
412
+ }, [open]);
413
+
414
+ // 使用 open 属性控制浮层显示
415
+ const hide = react.useCallback(() => {
416
+ setOpen(false);
417
+ }, []);
418
+ const handleConfirm = react.useCallback((newTitle, callback) => {
419
+ add(newTitle);
420
+ callback === null || callback === void 0 || callback();
421
+ hide();
422
+ }, [add, hide]);
423
+ const handleOpenChange = react.useCallback(newOpen => {
424
+ setOpen(newOpen);
425
+ }, []);
426
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
427
+ content: /*#__PURE__*/jsxRuntime.jsx(PopoverContent, {
428
+ ref: inputRef,
429
+ onClose: hide,
430
+ onConfirm: handleConfirm
431
+ }),
432
+ trigger: "click",
433
+ open: open,
434
+ onOpenChange: handleOpenChange,
435
+ destroyOnHidden: true,
436
+ children: children
437
+ });
438
+ };
439
+ var EditablePopover$1 = /*#__PURE__*/react.memo(EditablePopover);
440
+
441
+ const EditableContext = /*#__PURE__*/react.createContext(null);
442
+ const TreeTitle = _ref => {
443
+ let props = _extends({}, (_objectDestructuringEmpty(_ref), _ref));
444
+ const [form] = antd.Form.useForm();
445
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Form, {
446
+ form: form,
447
+ component: false,
448
+ children: /*#__PURE__*/jsxRuntime.jsx(EditableContext.Provider, {
449
+ value: form,
450
+ children: /*#__PURE__*/jsxRuntime.jsx(TreeTitleNode, _objectSpread2({}, props))
451
+ })
452
+ });
453
+ };
454
+ const TreeTitleNode = _ref2 => {
455
+ let {
456
+ title,
457
+ nodeData,
458
+ handleSave,
459
+ handleDel,
460
+ handleAdd
461
+ } = _ref2;
462
+ const [editing, setEditing] = react.useState(false);
463
+ const inputRef = react.useRef(null);
464
+ const form = react.useContext(EditableContext);
465
+ react.useEffect(() => {
466
+ if (editing) {
467
+ var _inputRef$current;
468
+ (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.focus();
469
+ }
470
+ }, [editing]);
471
+ const toggleEdit = react.useCallback(() => {
472
+ setEditing(prev => !prev);
473
+ form.setFieldsValue({
474
+ [nodeData.title]: nodeData.title
475
+ });
476
+ }, [form, nodeData.title]);
477
+ const save = react.useCallback(async () => {
478
+ try {
479
+ const values = await form.validateFields();
480
+ toggleEdit();
481
+ handleSave(_objectSpread2(_objectSpread2({}, nodeData), {}, {
482
+ title: values[nodeData.title]
483
+ }));
484
+ } catch (errInfo) {
485
+ console.error('Save failed:', errInfo);
486
+ }
487
+ }, [form, toggleEdit, handleSave, nodeData]);
488
+
489
+ // 新建文件夹
490
+ // 修改文件/文件夹
491
+ // 删除文件/文件夹
492
+ const renderIconNode = react.useCallback(() => {
493
+ return /*#__PURE__*/jsxRuntime.jsxs("span", {
494
+ className: "tree-node-icons",
495
+ onClick: e => e.stopPropagation(),
496
+ children: [/*#__PURE__*/jsxRuntime.jsx(EditablePopover$1, {
497
+ add: newTitle => handleAdd(nodeData, newTitle),
498
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
499
+ className: "px-1",
500
+ title: "create",
501
+ children: /*#__PURE__*/jsxRuntime.jsx("i", {
502
+ className: "iconfont icon-jia"
503
+ })
504
+ })
505
+ }), !nodeData.isRoot && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
506
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
507
+ className: "px-1",
508
+ onClick: toggleEdit,
509
+ title: "edit",
510
+ children: /*#__PURE__*/jsxRuntime.jsx("i", {
511
+ className: "iconfont icon-bianji"
512
+ })
513
+ }), /*#__PURE__*/jsxRuntime.jsx(antd.Popconfirm, {
514
+ title: "Confirm deletion?",
515
+ onConfirm: () => handleDel(nodeData),
516
+ okText: "Yes",
517
+ cancelText: "No",
518
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
519
+ className: "px-1",
520
+ title: "delete",
521
+ children: /*#__PURE__*/jsxRuntime.jsx("i", {
522
+ className: "iconfont icon-jian"
523
+ })
524
+ })
525
+ })]
526
+ })]
527
+ });
528
+ }, [handleAdd, nodeData, title, toggleEdit, handleDel]);
529
+ const renderChildNode = react.useCallback(() => {
530
+ return editing ? /*#__PURE__*/jsxRuntime.jsx(antd.Form.Item, {
531
+ style: {
532
+ margin: 0,
533
+ width: "100%"
534
+ },
535
+ name: nodeData.title,
536
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Input, {
537
+ ref: inputRef,
538
+ onPressEnter: save,
539
+ onBlur: save,
540
+ autoComplete: "off",
541
+ size: "small"
542
+ })
543
+ }) : /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
544
+ children: [/*#__PURE__*/jsxRuntime.jsx(antd.Typography.Text, {
545
+ ellipsis: true,
546
+ style: {
547
+ width: "75%"
548
+ },
549
+ children: title
550
+ }), renderIconNode()]
551
+ });
552
+ }, [editing, nodeData.title, save, title, renderIconNode]);
553
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
554
+ className: "tree-title",
555
+ children: renderChildNode()
556
+ });
557
+ };
558
+ var TreeTitle$1 = /*#__PURE__*/react.memo(TreeTitle);
559
+
560
+ const buildDirectoryTree = function (data) {
561
+ let basePath = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '0';
562
+ let parentRoute = arguments.length > 2 ? arguments[2] : undefined;
563
+ if (!Array.isArray(data)) return [];
564
+ return data.reduce((acc, item, index) => {
565
+ var _item$sd_index;
566
+ // 跳过文件类型,只处理目录
567
+ if (item.type === 'file') return acc;
568
+ const title = parentRoute ? item.name : (_item$sd_index = item.sd_index) !== null && _item$sd_index !== void 0 ? _item$sd_index : item.name;
569
+ const key = "".concat(basePath, "-").concat(index);
570
+ const currentPath = parentRoute ? "".concat(parentRoute, "/").concat(title) : item.path;
571
+ const treeNode = {
572
+ title,
573
+ // 文件/文件夹名称
574
+ key,
575
+ icon: _ref => {
576
+ let {
577
+ expanded
578
+ } = _ref;
579
+ return expanded ? /*#__PURE__*/jsxRuntime.jsx(icons.FolderOpenOutlined, {}) : /*#__PURE__*/jsxRuntime.jsx(icons.FolderOutlined, {});
580
+ },
581
+ children: [],
582
+ isLeaf: false,
583
+ isRoot: Boolean(item.sd_index),
584
+ path: currentPath,
585
+ contents: (item.contents || []).filter(content => content.type === 'file'),
586
+ rawData: item // 保留原始数据
587
+ };
588
+
589
+ // 递归处理子目录
590
+ if (Array.isArray(item.contents) && item.contents.length > 0) {
591
+ treeNode.children = buildDirectoryTree(item.contents.filter(content => content.type !== 'file'), key, currentPath);
592
+ }
593
+ acc.push(treeNode);
594
+ return acc;
595
+ }, []);
596
+ };
597
+
598
+ /**
599
+ * 查找树中指定key的节点
600
+ * @param {Array} treeData - 树数据
601
+ * @param {string} targetKey - 要查找的key
602
+ * @returns {Array} 包含匹配节点的数组
603
+ */
604
+ const findTreeNode = (treeData, targetKey) => {
605
+ if (!Array.isArray(treeData) || typeof targetKey !== 'string') return [];
606
+ const result = [];
607
+ const stack = [...treeData];
608
+ while (stack.length) {
609
+ const node = stack.pop();
610
+ if (node.key === targetKey) {
611
+ result.push(node);
612
+ // 如果只需要第一个匹配项,可以在这里break
613
+ }
614
+ if (Array.isArray(node.children)) {
615
+ stack.push(...node.children);
616
+ }
617
+ }
618
+ return result;
619
+ };
620
+
621
+ /**
622
+ * 获取树中所有节点的keys
623
+ * @param {Array} treeData - 树数据
624
+ * @returns {Array} 包含所有节点keys的数组
625
+ */
626
+ const getAllNodeKeys = treeData => {
627
+ if (!Array.isArray(treeData)) return [];
628
+ const keys = [];
629
+ const stack = [...treeData];
630
+ while (stack.length) {
631
+ const node = stack.pop();
632
+ if (node.key != null) {
633
+ keys.push(node.key);
634
+ }
635
+ if (Array.isArray(node.children)) {
636
+ stack.push(...node.children);
637
+ }
638
+ }
639
+ return keys;
640
+ };
641
+
642
+ const useDirectoryTree = _ref => {
643
+ let {
644
+ getFolderData,
645
+ createFolder,
646
+ removeFolderFile,
647
+ renameFolderFile,
648
+ height = 760,
649
+ theme = {
650
+ components: {
651
+ Tree: {
652
+ titleHeight: 30
653
+ }
654
+ }
655
+ }
656
+ } = _ref;
657
+ const [treeState, setTreeState] = react.useState({
658
+ data: [],
659
+ selectedKeys: [],
660
+ expandedKeys: [],
661
+ currentPath: "",
662
+ contents: [],
663
+ loading: false
664
+ });
665
+ const [originTreeData, setOriginTreeData] = react.useState([]);
666
+ const updateTreeState = partialState => {
667
+ setTreeState(prev => _objectSpread2(_objectSpread2({}, prev), partialState));
668
+ };
669
+
670
+ // 错误处理
671
+ const handleError = (error, operation) => {
672
+ console.error("".concat(operation, " ERROR"), error);
673
+ // 可以添加通知等统一错误处理
674
+ };
675
+
676
+ // 路径处理工具
677
+ const pathUtils = {
678
+ getNewPath: (node, newTitle) => {
679
+ const arr = node.path.split('/');
680
+ arr[arr.length - 1] = newTitle;
681
+ return arr.join('/');
682
+ },
683
+ isSamePath: (path1, path2) => {
684
+ return path1 === path2;
685
+ }
686
+ };
687
+
688
+ // 节点查找工具
689
+ const nodeUtils = {
690
+ findNode: (treeData, key) => {
691
+ return findTreeNode(treeData, key);
692
+ }
693
+ };
694
+
695
+ // 获取文件夹数据 文件夹名称为空时返回根目录数据
696
+ const fetchFolderData = react.useCallback(async function () {
697
+ let initialization = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
698
+ try {
699
+ const data = await getFolderData({
700
+ folder: ""
701
+ });
702
+ if (!(data !== null && data !== void 0 && data.directoryTree)) {
703
+ return null;
704
+ }
705
+ setOriginTreeData(data.directoryTree);
706
+
707
+ // 递归生成treenodes
708
+ const treeNodes = buildDirectoryTree(data.directoryTree);
709
+ if (!treeNodes.length) {
710
+ return null;
711
+ }
712
+ const firstChild = treeNodes[0];
713
+ const newState = _objectSpread2({
714
+ data: treeNodes
715
+ }, initialization && {
716
+ selectedKeys: [firstChild === null || firstChild === void 0 ? void 0 : firstChild.key],
717
+ expandedKeys: getAllNodeKeys(treeNodes),
718
+ currentPath: firstChild === null || firstChild === void 0 ? void 0 : firstChild.path,
719
+ contents: (firstChild === null || firstChild === void 0 ? void 0 : firstChild.contents) || []
720
+ });
721
+ updateTreeState(newState);
722
+ return treeNodes;
723
+ } catch (error) {
724
+ handleError(error, 'GET FOLDER DATA');
725
+ }
726
+ }, []);
727
+
728
+ // 初始化数据
729
+ react.useEffect(() => {
730
+ fetchFolderData(true);
731
+ }, [fetchFolderData]);
732
+
733
+ // 创建文件夹
734
+ const handleCreate = react.useCallback(async (node, newTitle) => {
735
+ if (!(newTitle !== null && newTitle !== void 0 && newTitle.trim())) return false;
736
+ try {
737
+ var _parentNode$;
738
+ const path = "".concat(node.path, "/").concat(newTitle);
739
+ await createFolder({
740
+ path
741
+ });
742
+ const newTreeData = await fetchFolderData();
743
+
744
+ // 找到新增节点的父节点
745
+ const parentNode = nodeUtils.findNode(newTreeData, node.key);
746
+ if (parentNode !== null && parentNode !== void 0 && (_parentNode$ = parentNode[0]) !== null && _parentNode$ !== void 0 && _parentNode$.children) {
747
+ const expectedPath = "".concat(node.path, "/").concat(newTitle);
748
+ // 通过 path 找到新增节点,得到新增节点的key
749
+ const addedNode = parentNode[0].children.find(ch => pathUtils.isSamePath(ch.path, expectedPath));
750
+ if (addedNode) {
751
+ updateTreeState({
752
+ expandedKeys: [...treeState.expandedKeys, addedNode.key, parentNode[0].key]
753
+ });
754
+ }
755
+ }
756
+ } catch (error) {
757
+ handleError(error, 'CREATE FOLDER');
758
+ }
759
+ }, [fetchFolderData, treeState.expandedKeys]);
760
+
761
+ // 删除文件/文件夹
762
+ const handleRemove = react.useCallback(async node => {
763
+ if (!node.path) return;
764
+ try {
765
+ await removeFolderFile({
766
+ paths: [{
767
+ path: node.path
768
+ }]
769
+ });
770
+ // 如果删除的是当前选中节点,则重新初始化
771
+ const shouldReinitialize = treeState.selectedKeys[0] === node.key;
772
+ await fetchFolderData(shouldReinitialize);
773
+ } catch (error) {
774
+ handleError(error, 'REMOVE FOLDER/FILE');
775
+ }
776
+ }, [fetchFolderData, treeState.selectedKeys]);
777
+
778
+ // 修改文件/文件夹
779
+ const handleRename = react.useCallback(async node => {
780
+ const newPath = pathUtils.getNewPath(node, node.title);
781
+ if (pathUtils.isSamePath(node.path, newPath)) return false;
782
+ try {
783
+ await renameFolderFile({
784
+ old_path: node.path,
785
+ new_path: newPath
786
+ });
787
+ await fetchFolderData();
788
+ } catch (error) {
789
+ handleError(error, 'RENAME FOLDER/FILE');
790
+ }
791
+ }, [fetchFolderData]);
792
+
793
+ // 选择节点
794
+ const onSelect = react.useCallback(async (keys, info) => {
795
+ if (!keys.length || keys[0] === treeState.selectedKeys[0] && pathUtils.isSamePath(info.node.path, treeState.currentPath)) return;
796
+ updateTreeState({
797
+ selectedKeys: keys,
798
+ currentPath: info.node.path,
799
+ loading: true
800
+ });
801
+ try {
802
+ // 模拟延迟加载
803
+ // eslint-disable-next-line no-promise-executor-return
804
+ await new Promise(resolve => setTimeout(resolve, 300));
805
+ updateTreeState({
806
+ contents: info.node.contents,
807
+ loading: false
808
+ });
809
+ } catch (error) {
810
+ handleError(error, 'SELECT NODE');
811
+ updateTreeState({
812
+ loading: false
813
+ });
814
+ }
815
+ }, [treeState.selectedKeys, treeState.currentPath]);
816
+
817
+ // 展开节点
818
+ const onExpand = react.useCallback(keys => {
819
+ updateTreeState({
820
+ expandedKeys: keys
821
+ });
822
+ }, []);
823
+
824
+ // 上传右侧文件或删除右侧文件 成功后的回调函数
825
+ const updateFileContents = react.useCallback(async () => {
826
+ // 找到当前选中的节点,更新当前节点的 contents
827
+ const newTreeData = await fetchFolderData();
828
+ const selectedNode = nodeUtils.findNode(newTreeData, treeState.selectedKeys[0]);
829
+ if (selectedNode !== null && selectedNode !== void 0 && selectedNode[0]) {
830
+ updateTreeState({
831
+ contents: selectedNode[0].contents
832
+ });
833
+ }
834
+ }, [fetchFolderData, treeState.selectedKeys]);
835
+
836
+ // 删除文件
837
+ const removeFile = react.useCallback(async url => {
838
+ try {
839
+ await removeFolderFile({
840
+ path: url
841
+ });
842
+ await updateFileContents();
843
+ } catch (error) {
844
+ handleError(error, 'REMOVE FILE');
845
+ }
846
+ }, [updateFileContents]);
847
+ const MemoizedTree = react.useMemo(() => {
848
+ var _treeState$data;
849
+ if (!((_treeState$data = treeState.data) !== null && _treeState$data !== void 0 && _treeState$data.length)) {
850
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Empty, {
851
+ image: antd.Empty.PRESENTED_IMAGE_SIMPLE
852
+ });
853
+ }
854
+ return /*#__PURE__*/jsxRuntime.jsx("div", {
855
+ style: {
856
+ paddingTop: 16
857
+ },
858
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.ConfigProvider, {
859
+ theme: theme,
860
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Tree, {
861
+ blockNode: true,
862
+ showIcon: true,
863
+ selectedKeys: treeState.selectedKeys,
864
+ expandedKeys: treeState.expandedKeys,
865
+ onSelect: onSelect,
866
+ onExpand: onExpand,
867
+ treeData: treeState.data,
868
+ titleRender: nodeData => /*#__PURE__*/jsxRuntime.jsx(TreeTitle$1, {
869
+ title: nodeData.title,
870
+ nodeData: nodeData,
871
+ handleSave: handleRename,
872
+ handleDel: handleRemove,
873
+ handleAdd: handleCreate
874
+ }),
875
+ height: height
876
+ })
877
+ })
878
+ }, "folder-directory");
879
+ }, [treeState.data, treeState.selectedKeys, treeState.expandedKeys]);
880
+ return {
881
+ directoryTree: MemoizedTree,
882
+ contents: treeState.contents,
883
+ currentPath: treeState.currentPath,
884
+ loading: treeState.loading,
885
+ originTreeData,
886
+ updateFileContents,
887
+ removeFile
888
+ };
889
+ };
890
+
891
+ const createUrlBuilder = function () {
892
+ let config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
893
+ const {
894
+ baseUrl = '',
895
+ isDev = false,
896
+ devBaseUrl = '',
897
+ isBrowser = false
898
+ } = config;
899
+ return path => {
900
+ if (!path) return '';
901
+
902
+ // 如果传入的是完整 URL,直接返回
903
+ if (/^https?:\/\//.test(path)) {
904
+ return path;
905
+ }
906
+
907
+ // 去除 path 开头可能的多余斜杠
908
+ const cleanPath = path.replace(/^\/+/, '');
909
+
910
+ // 开发环境使用 devBaseUrl,否则使用 baseUrl
911
+ const host = isDev ? devBaseUrl : baseUrl;
912
+
913
+ // 如果没有配置 baseUrl,则根据环境返回相对路径或完整路径
914
+ if (!host) {
915
+ return isBrowser ? "/".concat(cleanPath) : cleanPath;
916
+ }
917
+ return host ? "".concat(host, "/").concat(cleanPath) : "/".concat(cleanPath);
918
+ };
919
+ };
920
+
921
+ const ResourcesView = _ref => {
922
+ let {
923
+ apiConfig = {},
924
+ features = {},
925
+ className = '',
926
+ style = {},
927
+ renderTreeHeader,
928
+ renderGridHeader,
929
+ acceptFileTypes = "video/*,.mxf,application/mxf,video/mxf",
930
+ uploadTimeout = 60 * 60 * 1000,
931
+ searchPlaceholder = "Search resources..."
932
+ } = _ref;
933
+ const {
934
+ message,
935
+ modal
936
+ } = antd.App.useApp();
937
+
938
+ // 合并默认配置和传入配置
939
+ const mergedApiConfig = react.useMemo(() => _objectSpread2({
940
+ upload: '/api/dvr/media/source/upload',
941
+ download: '/download'
942
+ }, apiConfig), [apiConfig]);
943
+ const mergedFeatures = react.useMemo(() => _objectSpread2({
944
+ upload: true,
945
+ download: true,
946
+ copy: true,
947
+ delete: true,
948
+ search: true,
949
+ batchOperations: true
950
+ }, features), [features]);
951
+ const {
952
+ directoryTree,
953
+ contents,
954
+ currentPath,
955
+ loading,
956
+ originTreeData,
957
+ updateFileContents,
958
+ removeFile
959
+ } = useDirectoryTree({
960
+ getFolderData: mergedApiConfig.getFolderData,
961
+ createFolder: mergedApiConfig.createFolder,
962
+ removeFolderFile: mergedApiConfig.removeFolderFile,
963
+ renameFolderFile: mergedApiConfig.renameFolderFile
964
+ });
965
+ const [showProgress, setShowProgress] = react.useState(false);
966
+ const [percent, setPercent] = react.useState(0);
967
+ const inputRef = react.useRef(null);
968
+ const uploadController = react.useRef(new AbortController());
969
+ const [modalVisible, setModalVisible] = react.useState(false);
970
+ // 处理批量操作
971
+ const [selectedItems, setSelectedItems] = react.useState([]);
972
+ const [indeterminate, setIndeterminate] = react.useState(false);
973
+ const [checkAll, setCheckAll] = react.useState(false);
974
+ const [batchLoading, setBatchLoading] = react.useState(false);
975
+ const [keyword, setKeyword] = react.useState('');
976
+
977
+ // 过滤内容
978
+ const filteredContents = react.useMemo(() => {
979
+ if (!keyword) return contents;
980
+ return contents.filter(item => item.name.toLowerCase().includes(keyword.toLowerCase()));
981
+ }, [contents, keyword]);
982
+
983
+ // 在组件卸载时取消上传
984
+ react.useEffect(() => {
985
+ return () => {
986
+ var _uploadController$cur;
987
+ return (_uploadController$cur = uploadController.current) === null || _uploadController$cur === void 0 ? void 0 : _uploadController$cur.abort();
988
+ };
989
+ }, []);
990
+
991
+ // 重置批量状态的效果
992
+ react.useEffect(() => {
993
+ setSelectedItems([]);
994
+ setIndeterminate(false);
995
+ setCheckAll(false);
996
+ }, [currentPath]);
997
+
998
+ // 上传相关函数
999
+ const onTriggerUpload = react.useCallback(() => {
1000
+ var _inputRef$current;
1001
+ (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.click();
1002
+ }, []);
1003
+ const onUploadFile = async event => {
1004
+ const files = event.target.files;
1005
+ if (!(files !== null && files !== void 0 && files.length)) return;
1006
+
1007
+ // 初始化上传状态
1008
+ setShowProgress(true);
1009
+ setPercent(0);
1010
+ try {
1011
+ await uploadFiles(files, currentPath);
1012
+ // 延迟2s刷新数据
1013
+ await new Promise(resolve => setTimeout(resolve, 2000));
1014
+ await updateFileContents();
1015
+ } catch (error) {
1016
+ if (axios.isCancel(error)) {
1017
+ console.log('Upload canceled');
1018
+ } else if (error.response) {
1019
+ var _error$response$data;
1020
+ 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));
1021
+ } else {
1022
+ console.error("Upload error: ".concat(error.message));
1023
+ }
1024
+ } finally {
1025
+ resetUploadState();
1026
+ }
1027
+ };
1028
+ const uploadFiles = async (files, folder) => {
1029
+ // 计算所有文件的总大小
1030
+ const totalSize = calculateTotalSize(files);
1031
+ // 存储每个文件的上一次 loaded 值(避免重复计算)
1032
+ const previousLoadedMap = new Map();
1033
+ let uploadedSize = 0; // 记录已上传的字节数
1034
+
1035
+ const uploadTasks = Array.from(files).map(file => uploadSingleFile(file, folder, previousLoadedMap, newLoaded => {
1036
+ uploadedSize += newLoaded;
1037
+ updateProgress(uploadedSize, totalSize);
1038
+ }));
1039
+ await Promise.all(uploadTasks);
1040
+ };
1041
+
1042
+ // 计算总文件大小
1043
+ const calculateTotalSize = files => Array.from(files).reduce((sum, file) => sum + file.size, 0);
1044
+
1045
+ // 上传单个文件
1046
+ const uploadSingleFile = (file, folder, previousLoadedMap, onProgress) => {
1047
+ const formData = new FormData();
1048
+ formData.append('file', file);
1049
+ formData.append('folder', folder);
1050
+ return axios.post(mergedApiConfig.upload, formData, {
1051
+ timeout: uploadTimeout,
1052
+ headers: {
1053
+ 'Content-Type': 'multipart/form-data'
1054
+ },
1055
+ // signal: uploadController.current.signal,
1056
+ onUploadProgress: progressEvent => {
1057
+ if (progressEvent.lengthComputable) {
1058
+ // 获取当前文件的上一次 loaded 值(默认 0)
1059
+ const previousLoaded = previousLoadedMap.get(file.name) || 0;
1060
+ // 计算新增的字节数(避免重复累加)
1061
+ const newLoaded = progressEvent.loaded - previousLoaded;
1062
+ // 更新当前文件的 loaded 值
1063
+ previousLoadedMap.set(file.name, progressEvent.loaded);
1064
+ onProgress(newLoaded);
1065
+ }
1066
+ }
1067
+ });
1068
+ };
1069
+ const updateProgress = (uploadedSize, totalSize) => {
1070
+ const totalPercent = Math.round(uploadedSize / totalSize * 100);
1071
+ setPercent(totalPercent);
1072
+ };
1073
+
1074
+ // 重置上传状态
1075
+ const resetUploadState = () => {
1076
+ setShowProgress(false);
1077
+ setPercent(0);
1078
+ if (inputRef.current) inputRef.current.value = "";
1079
+ };
1080
+
1081
+ // 文件操作函数
1082
+ const downloadFile = async url => {
1083
+ try {
1084
+ const isDev = process.env.NODE_ENV === 'development';
1085
+ const baseUrl = isDev ? process.env.API_HOST // 开发环境默认值
1086
+ : window.location.origin; // 生产环境用当前域名
1087
+
1088
+ const link = document.createElement('a');
1089
+ link.href = "".concat(baseUrl).concat(mergedApiConfig.download, "/").concat(url);
1090
+ // link.download = filename;
1091
+ link.style.display = 'none';
1092
+ document.body.appendChild(link);
1093
+ link.click();
1094
+ document.body.removeChild(link);
1095
+ } catch (error) {
1096
+ console.error("Download failed:", error);
1097
+ }
1098
+ };
1099
+ const copyFile = () => {
1100
+ setModalVisible(true);
1101
+ };
1102
+ const onContextMenu = (key, item) => {
1103
+ if (key === 'del') removeFile(item.url);
1104
+ if (key === 'download') downloadFile(item.url);
1105
+ if (key === 'copy') copyFile(item.url);
1106
+ };
1107
+ const handleBatchRemove = react.useCallback(async () => {
1108
+ if (!selectedItems.length) return;
1109
+ modal.confirm({
1110
+ icon: /*#__PURE__*/jsxRuntime.jsx(icons.ExclamationCircleFilled, {}),
1111
+ title: "Are you sure you want to delete ".concat(selectedItems.length, " files?"),
1112
+ cancelText: "No",
1113
+ okText: "Yes",
1114
+ onOk: async () => {
1115
+ try {
1116
+ // 批量处理所有删除请求
1117
+ await mergedApiConfig.removeFolderFile({
1118
+ paths: selectedItems.map(itemUrl => ({
1119
+ path: itemUrl
1120
+ }))
1121
+ });
1122
+
1123
+ // 更新状态
1124
+ setSelectedItems([]);
1125
+ setIndeterminate(false);
1126
+ setCheckAll(false);
1127
+ await updateFileContents(); // 刷新文件列表
1128
+ } catch (error) {
1129
+ console.error('Batch deletion error:', error);
1130
+ }
1131
+ }
1132
+ });
1133
+ }, [selectedItems, mergedApiConfig, updateFileContents]);
1134
+ const handleCopyConfirm = react.useCallback(async newPath => {
1135
+ setBatchLoading(true);
1136
+ try {
1137
+ // 批量处理所有拷贝请求
1138
+ await mergedApiConfig.copyFile({
1139
+ old_paths: selectedItems.map(itemUrl => ({
1140
+ old_path: itemUrl
1141
+ })),
1142
+ new_path: newPath
1143
+ });
1144
+
1145
+ // 更新状态
1146
+ setSelectedItems([]);
1147
+ setIndeterminate(false);
1148
+ setCheckAll(false);
1149
+ message.success('Success');
1150
+ setModalVisible(false);
1151
+
1152
+ // 延迟2s刷新数据
1153
+ await new Promise(resolve => setTimeout(resolve, 2000));
1154
+ await updateFileContents();
1155
+ } catch (error) {
1156
+ console.error("Batch copy error:", error);
1157
+ } finally {
1158
+ setBatchLoading(false);
1159
+ }
1160
+ }, [selectedItems, mergedApiConfig, updateFileContents]);
1161
+
1162
+ // 处理单个项目的选择变化
1163
+ const handleSelectChange = (item, checked) => {
1164
+ const newSelectedItems = checked ? [...selectedItems, item.url] : selectedItems.filter(url => url !== item.url);
1165
+ setSelectedItems(newSelectedItems);
1166
+ setIndeterminate(!!newSelectedItems.length && newSelectedItems.length !== filteredContents.length);
1167
+ setCheckAll(newSelectedItems.length === filteredContents.length);
1168
+ };
1169
+
1170
+ // 处理全选/取消全选
1171
+ const handleSelectAll = e => {
1172
+ const checked = e.target.checked;
1173
+ setSelectedItems(checked ? filteredContents.map(item => item.url) : []);
1174
+ setIndeterminate(false);
1175
+ setCheckAll(checked);
1176
+ };
1177
+
1178
+ // 搜索处理
1179
+ const handleSearchChange = e => {
1180
+ setKeyword(e.target.value);
1181
+ };
1182
+
1183
+ // 创建 URL 构建器
1184
+ const getFullUrl = react.useMemo(() => {
1185
+ return createUrlBuilder({
1186
+ baseUrl: apiConfig.baseUrl,
1187
+ devBaseUrl: apiConfig.devBaseUrl,
1188
+ isDev: process.env.NODE_ENV === 'development',
1189
+ isBrowser: typeof window !== 'undefined'
1190
+ });
1191
+ }, [apiConfig.baseUrl, apiConfig.devBaseUrl]);
1192
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
1193
+ className: "resources-view ".concat(className),
1194
+ style: style,
1195
+ children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
1196
+ className: "content-container",
1197
+ children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
1198
+ className: "directory-tree",
1199
+ children: [renderTreeHeader ? renderTreeHeader() : /*#__PURE__*/jsxRuntime.jsx("div", {
1200
+ className: "search-bar"
1201
+ }), directoryTree]
1202
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
1203
+ className: "folder-contents",
1204
+ children: [/*#__PURE__*/jsxRuntime.jsxs(antd.Flex, {
1205
+ justify: "center",
1206
+ align: "center",
1207
+ className: "search-bar",
1208
+ children: [mergedFeatures.search && /*#__PURE__*/jsxRuntime.jsx("input", {
1209
+ type: "text",
1210
+ placeholder: searchPlaceholder,
1211
+ value: keyword,
1212
+ onChange: handleSearchChange,
1213
+ className: "search-input"
1214
+ }), mergedFeatures.upload && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
1215
+ children: [/*#__PURE__*/jsxRuntime.jsx(icons.CloudUploadOutlined, {
1216
+ className: "upload-icon",
1217
+ title: "upload",
1218
+ onClick: onTriggerUpload
1219
+ }), /*#__PURE__*/jsxRuntime.jsx("input", {
1220
+ ref: inputRef,
1221
+ type: "file",
1222
+ onChange: onUploadFile,
1223
+ className: "hidden",
1224
+ accept: acceptFileTypes,
1225
+ multiple: true
1226
+ })]
1227
+ })]
1228
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
1229
+ className: "media-grid-container",
1230
+ onContextMenu: e => e.preventDefault(),
1231
+ children: [renderGridHeader ? renderGridHeader() : filteredContents.length > 0 && mergedFeatures.batchOperations && /*#__PURE__*/jsxRuntime.jsx("div", {
1232
+ className: "batch-operations",
1233
+ children: /*#__PURE__*/jsxRuntime.jsxs(antd.Space, {
1234
+ size: "middle",
1235
+ children: [/*#__PURE__*/jsxRuntime.jsx(antd.Checkbox, {
1236
+ indeterminate: indeterminate,
1237
+ checked: checkAll,
1238
+ onChange: handleSelectAll,
1239
+ children: "Select All"
1240
+ }), /*#__PURE__*/jsxRuntime.jsx(antd.Button, {
1241
+ type: "primary",
1242
+ disabled: !selectedItems.length,
1243
+ onClick: () => setModalVisible(true),
1244
+ children: "Batch Copy"
1245
+ }), /*#__PURE__*/jsxRuntime.jsx(antd.Button, {
1246
+ className: "btn-gray",
1247
+ disabled: !selectedItems.length,
1248
+ onClick: handleBatchRemove,
1249
+ children: "Batch Delete"
1250
+ })]
1251
+ })
1252
+ }), /*#__PURE__*/jsxRuntime.jsx(MediaGrid$1, {
1253
+ items: filteredContents,
1254
+ loading: loading,
1255
+ onContextMenu: mergedFeatures.delete || mergedFeatures.download || mergedFeatures.copy ? onContextMenu : null,
1256
+ selectedKeys: selectedItems,
1257
+ onSelectChange: mergedFeatures.batchOperations ? handleSelectChange : null,
1258
+ onClick: () => {},
1259
+ features: mergedFeatures,
1260
+ getFullUrl: getFullUrl
1261
+ })]
1262
+ })]
1263
+ })]
1264
+ }), showProgress && /*#__PURE__*/jsxRuntime.jsx(UploadProgress$1, {
1265
+ percent: percent
1266
+ }), /*#__PURE__*/jsxRuntime.jsx(SelectFolderPathModal$1, {
1267
+ open: modalVisible,
1268
+ directoryTree: originTreeData,
1269
+ batchLoading: batchLoading,
1270
+ onClose: () => setModalVisible(false),
1271
+ onOk: handleCopyConfirm
1272
+ })]
1273
+ });
1274
+ };
1275
+ var ResourcesView$1 = ResourcesView;
1276
+
1277
+ exports.ResourcesView = ResourcesView$1;
1278
+ //# sourceMappingURL=index.js.map