498b905bd8
<video src="attachments/2377fc8b-50e4-4919-ba41-5027b957709f" title="2026-07-27 16-15-53.mp4" controls></video> - 实现shift ctrl的范围选扩选 - 实现多选下载(为一个zip) - 原本有搜索和文件夹折叠展开功能, 我的处理是: 只作为方便查找的用途. 最终操作会有"目前一些(比如待删除)元素被隐藏了,请再确认"的提示. result: ```shell root@k88936-t14p: ~/Downloads# unzip 未命名画布-选中素材-20260806-195631.zip Archive: 未命名画布-选中素材-20260806-195631.zip creating: 未命名画布-选中素材/ creating: 未命名画布-选中素材/images/ creating: 未命名画布-选中素材/media/ extracting: 未命名画布-选中素材/media/001-角色动作(原始视频).mp4 creating: 未命名画布-选中素材/sequences/ creating: 未命名画布-选中素材/sequences/002-角色动作/ creating: 未命名画布-选中素材/sequences/002-角色动作/frames/ extracting: 未命名画布-选中素材/sequences/002-角色动作/frames/frame-01.png extracting: 未命名画布-选中素材/sequences/002-角色动作/frames/frame-02.png extracting: 未命名画布-选中素材/sequences/002-角色动作/frames/frame-03.png ... extracting: 未命名画布-选中素材/sequences/002-角色动作/frames/frame-31.png extracting: 未命名画布-选中素材/sequences/002-角色动作/frames/frame-32.png extracting: 未命名画布-选中素材/sequences/002-角色动作/metadata.json extracting: 未命名画布-选中素材/sequences/002-角色动作/skeleton.json extracting: 未命名画布-选中素材/sequences/002-角色动作/README.md extracting: 未命名画布-选中素材/sequences/002-角色动作/manifest.txt extracting: 未命名画布-选中素材/metadata.json extracting: 未命名画布-选中素材/manifest.txt ``` ~~动作的导出存在很大的问题, 在另一个pr #117 解决~~ --------- Co-authored-by: 段舒康 <kdletters@qq.com> Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/114 Co-authored-by: 王德宇 <kvtodev@outlook.com> Co-committed-by: 王德宇 <kvtodev@outlook.com>
295 lines
9.5 KiB
TypeScript
295 lines
9.5 KiB
TypeScript
import {
|
|
Check,
|
|
ChevronDown,
|
|
ChevronRight,
|
|
Folder,
|
|
ImagePlus,
|
|
PencilLine,
|
|
Trash2,
|
|
X,
|
|
} from 'lucide-react';
|
|
import type { Dispatch, SetStateAction } from 'react';
|
|
|
|
import { PlatformTextField } from '../common/PlatformTextField';
|
|
import type {
|
|
GroupedEditorAssetFolder,
|
|
UploadFilesOptions,
|
|
UploadRequestOptions,
|
|
} from './ImageCanvasAssetLibraryPanelView';
|
|
import { ImageCanvasAssetRowView } from './ImageCanvasAssetRowView';
|
|
import {
|
|
ASSET_DRAG_MIME_TYPE,
|
|
getDraggedAssetId,
|
|
hasDataTransferType,
|
|
} from './ImageCanvasEditorModel';
|
|
import { EditorIconButton } from './ImageCanvasEditorPrimitives';
|
|
import type {
|
|
AssetPointerDragState,
|
|
EditorAsset,
|
|
EditorAssetFolder,
|
|
UploadTarget,
|
|
} from './ImageCanvasEditorTypes';
|
|
import type { AssetSelectionInteraction } from './useImageCanvasAssetSelection';
|
|
|
|
type ImageCanvasAssetFolderSectionViewProps = {
|
|
folder: GroupedEditorAssetFolder;
|
|
assetPointerDragRef: { current: AssetPointerDragState | null };
|
|
suppressAssetClickRef: { current: boolean };
|
|
isAssetSelectionMode: boolean;
|
|
selectedAssetIds: Set<string>;
|
|
visibleAssetIds: string[];
|
|
assetMoveDropFolderId: string | null;
|
|
renamingFolder: { folderId: string; value: string } | null;
|
|
renamingAsset: { assetId: string; value: string } | null;
|
|
setRenamingFolder: Dispatch<
|
|
SetStateAction<{ folderId: string; value: string } | null>
|
|
>;
|
|
setRenamingAsset: Dispatch<
|
|
SetStateAction<{ assetId: string; value: string } | null>
|
|
>;
|
|
setActiveUploadFolderId: Dispatch<SetStateAction<string>>;
|
|
setUploadDropTarget: Dispatch<SetStateAction<'canvas' | 'assets' | null>>;
|
|
setAssetPointerDrag: Dispatch<SetStateAction<AssetPointerDragState | null>>;
|
|
updateAssetMoveDropFolder: (folderId: string | null) => void;
|
|
addUploadedFiles: (
|
|
files: FileList | File[],
|
|
options?: UploadFilesOptions,
|
|
) => void;
|
|
requestUpload: (target: UploadTarget, options?: UploadRequestOptions) => void;
|
|
moveAssetToFolder: (assetId: string, folderId: string) => void;
|
|
toggleAssetFolder: (folderId: string) => void;
|
|
startRenamingFolder: (folder: EditorAssetFolder) => void;
|
|
commitFolderRename: (folder: EditorAssetFolder) => void;
|
|
deleteAssetFolder: (folder: EditorAssetFolder) => void;
|
|
startRenamingAsset: (asset: EditorAsset) => void;
|
|
commitAssetRename: (asset: EditorAsset) => void;
|
|
deleteUploadedAsset: (asset: EditorAsset) => void;
|
|
submitAssetToShowcase: (asset: EditorAsset) => void;
|
|
toggleAssetSelected: (
|
|
assetId: string,
|
|
interaction: AssetSelectionInteraction,
|
|
) => void;
|
|
addAssetLayer: (asset: EditorAsset) => void;
|
|
onDownloadAsset: (asset: EditorAsset) => void;
|
|
};
|
|
|
|
export function ImageCanvasAssetFolderSectionView({
|
|
folder,
|
|
assetPointerDragRef,
|
|
suppressAssetClickRef,
|
|
isAssetSelectionMode,
|
|
selectedAssetIds,
|
|
visibleAssetIds,
|
|
assetMoveDropFolderId,
|
|
renamingFolder,
|
|
renamingAsset,
|
|
setRenamingFolder,
|
|
setRenamingAsset,
|
|
setActiveUploadFolderId,
|
|
setUploadDropTarget,
|
|
setAssetPointerDrag,
|
|
updateAssetMoveDropFolder,
|
|
addUploadedFiles,
|
|
requestUpload,
|
|
moveAssetToFolder,
|
|
toggleAssetFolder,
|
|
startRenamingFolder,
|
|
commitFolderRename,
|
|
deleteAssetFolder,
|
|
startRenamingAsset,
|
|
commitAssetRename,
|
|
deleteUploadedAsset,
|
|
submitAssetToShowcase,
|
|
toggleAssetSelected,
|
|
addAssetLayer,
|
|
onDownloadAsset,
|
|
}: ImageCanvasAssetFolderSectionViewProps) {
|
|
return (
|
|
<section
|
|
className={[
|
|
'image-canvas-editor__asset-folder',
|
|
assetMoveDropFolderId === folder.id
|
|
? 'image-canvas-editor__asset-folder--move-target'
|
|
: '',
|
|
]
|
|
.filter(Boolean)
|
|
.join(' ')}
|
|
aria-label={folder.label}
|
|
data-asset-folder-id={folder.id}
|
|
onDragOver={(event) => {
|
|
if (isAssetSelectionMode) {
|
|
if (
|
|
hasDataTransferType(event.dataTransfer, ASSET_DRAG_MIME_TYPE) ||
|
|
hasDataTransferType(event.dataTransfer, 'Files')
|
|
) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
event.dataTransfer.dropEffect = 'none';
|
|
}
|
|
return;
|
|
}
|
|
if (hasDataTransferType(event.dataTransfer, ASSET_DRAG_MIME_TYPE)) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
setUploadDropTarget(null);
|
|
updateAssetMoveDropFolder(folder.id);
|
|
event.dataTransfer.dropEffect = 'move';
|
|
return;
|
|
}
|
|
if (hasDataTransferType(event.dataTransfer, 'Files')) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
setUploadDropTarget('assets');
|
|
event.dataTransfer.dropEffect = 'copy';
|
|
}
|
|
}}
|
|
onDrop={(event) => {
|
|
if (isAssetSelectionMode) {
|
|
if (
|
|
getDraggedAssetId(event.dataTransfer) ||
|
|
event.dataTransfer.files.length > 0
|
|
) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
setUploadDropTarget(null);
|
|
updateAssetMoveDropFolder(null);
|
|
}
|
|
return;
|
|
}
|
|
const movingAssetId = getDraggedAssetId(event.dataTransfer);
|
|
if (movingAssetId) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
setUploadDropTarget(null);
|
|
updateAssetMoveDropFolder(null);
|
|
moveAssetToFolder(movingAssetId, folder.id);
|
|
return;
|
|
}
|
|
if (!event.dataTransfer.files.length) {
|
|
return;
|
|
}
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
setUploadDropTarget(null);
|
|
updateAssetMoveDropFolder(null);
|
|
addUploadedFiles(event.dataTransfer.files, {
|
|
folderId: folder.id,
|
|
});
|
|
}}
|
|
>
|
|
<div
|
|
className="image-canvas-editor__asset-folder-header"
|
|
data-asset-folder-header-id={folder.id}
|
|
>
|
|
<EditorIconButton
|
|
label={`${folder.collapsed ? '展开' : '折叠'}${folder.label}`}
|
|
title={folder.collapsed ? '展开' : '折叠'}
|
|
icon={folder.collapsed ? ChevronRight : ChevronDown}
|
|
expanded={!folder.collapsed}
|
|
onClick={() => toggleAssetFolder(folder.id)}
|
|
/>
|
|
<Folder className="h-4 w-4" />
|
|
{renamingFolder?.folderId === folder.id ? (
|
|
<PlatformTextField
|
|
aria-label={`重命名文件夹${folder.label}`}
|
|
value={renamingFolder.value}
|
|
autoFocus
|
|
size="xs"
|
|
density="compact"
|
|
className="image-canvas-editor__folder-rename-input"
|
|
onChange={(event) =>
|
|
setRenamingFolder({
|
|
folderId: folder.id,
|
|
value: event.target.value,
|
|
})
|
|
}
|
|
onKeyDown={(event) => {
|
|
if (event.key === 'Enter') {
|
|
event.preventDefault();
|
|
commitFolderRename(folder);
|
|
}
|
|
if (event.key === 'Escape') {
|
|
event.preventDefault();
|
|
setRenamingFolder(null);
|
|
}
|
|
}}
|
|
/>
|
|
) : (
|
|
<span>{folder.label}</span>
|
|
)}
|
|
<span>{folder.assets.length}</span>
|
|
{renamingFolder?.folderId === folder.id ? (
|
|
<>
|
|
<EditorIconButton
|
|
label={`保存文件夹${folder.label}名称`}
|
|
title="保存"
|
|
icon={Check}
|
|
onClick={() => commitFolderRename(folder)}
|
|
/>
|
|
<EditorIconButton
|
|
label={`取消重命名文件夹${folder.label}`}
|
|
title="取消"
|
|
icon={X}
|
|
onClick={() => setRenamingFolder(null)}
|
|
/>
|
|
</>
|
|
) : (
|
|
<EditorIconButton
|
|
label={`重命名文件夹${folder.label}`}
|
|
title="重命名"
|
|
icon={PencilLine}
|
|
onClick={() => startRenamingFolder(folder)}
|
|
/>
|
|
)}
|
|
{!folder.systemDefault ? (
|
|
<EditorIconButton
|
|
label={`删除文件夹${folder.label}`}
|
|
title="删除"
|
|
icon={Trash2}
|
|
onClick={() => deleteAssetFolder(folder)}
|
|
/>
|
|
) : null}
|
|
<EditorIconButton
|
|
label={`上传到${folder.label}`}
|
|
title="上传"
|
|
icon={ImagePlus}
|
|
onClick={() => {
|
|
setActiveUploadFolderId(folder.id);
|
|
requestUpload('asset', { folderId: folder.id });
|
|
}}
|
|
/>
|
|
</div>
|
|
<div
|
|
className="image-canvas-editor__asset-folder-list"
|
|
hidden={folder.collapsed}
|
|
>
|
|
{folder.assets.map((asset) => (
|
|
<ImageCanvasAssetRowView
|
|
key={asset.id}
|
|
asset={asset}
|
|
assetPointerDragRef={assetPointerDragRef}
|
|
suppressAssetClickRef={suppressAssetClickRef}
|
|
isAssetSelectionMode={isAssetSelectionMode}
|
|
selectedAssetIds={selectedAssetIds}
|
|
visibleAssetIds={visibleAssetIds}
|
|
renamingAsset={renamingAsset}
|
|
setRenamingAsset={setRenamingAsset}
|
|
setUploadDropTarget={setUploadDropTarget}
|
|
setAssetPointerDrag={setAssetPointerDrag}
|
|
updateAssetMoveDropFolder={updateAssetMoveDropFolder}
|
|
addUploadedFiles={addUploadedFiles}
|
|
moveAssetToFolder={moveAssetToFolder}
|
|
startRenamingAsset={startRenamingAsset}
|
|
commitAssetRename={commitAssetRename}
|
|
deleteUploadedAsset={deleteUploadedAsset}
|
|
submitAssetToShowcase={submitAssetToShowcase}
|
|
toggleAssetSelected={toggleAssetSelected}
|
|
addAssetLayer={addAssetLayer}
|
|
onDownloadAsset={onDownloadAsset}
|
|
/>
|
|
))}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|