Browse Source

修改素材库

feature/composer
TianYun 3 weeks ago
parent
commit
8e3d997a7e
  1. 166
      src/components/AssetLibraryManagerModal.tsx

166
src/components/AssetLibraryManagerModal.tsx

@ -39,7 +39,6 @@ import {
import { AssetLibraryUploadModal, type AssetLibraryUploadFolderOption } from "./AssetLibraryUploadModal";
type AssetLibraryFileKind = "image" | "video" | "audio" | "other";
type AssetLibraryActionMenuView = "actions" | "move";
interface AssetLibraryManagerModalProps {
open: boolean;
@ -158,7 +157,7 @@ export function AssetLibraryManagerModal({ open, onClose, onChanged }: AssetLibr
const [expandedMoveFolderIds, setExpandedMoveFolderIds] = useState<Set<number>>(new Set());
const [moveFolderAncestorIds, setMoveFolderAncestorIds] = useState<Record<number, Set<number>>>({});
const [actionMenuOpenId, setActionMenuOpenId] = useState<number | null>(null);
const [actionMenuView, setActionMenuView] = useState<AssetLibraryActionMenuView>("actions");
const [movePanelOpenId, setMovePanelOpenId] = useState<number | null>(null);
const [batchMode, setBatchMode] = useState(false);
const [selectedItemIds, setSelectedItemIds] = useState<Set<number>>(new Set());
const [batchDeleting, setBatchDeleting] = useState(false);
@ -213,7 +212,7 @@ export function AssetLibraryManagerModal({ open, onClose, onChanged }: AssetLibr
setExpandedMoveFolderIds(new Set());
setMoveFolderAncestorIds({});
setActionMenuOpenId(null);
setActionMenuView("actions");
setMovePanelOpenId(null);
setBatchMode(false);
setSelectedItemIds(new Set());
setBatchDeleting(false);
@ -331,7 +330,7 @@ export function AssetLibraryManagerModal({ open, onClose, onChanged }: AssetLibr
await loadItems();
await onChanged?.(parentId);
setActionMenuOpenId(null);
setActionMenuView("actions");
setMovePanelOpenId(null);
void message.success(t("assetLibrary.moveSuccess"));
} catch (err) {
const messageText = err instanceof Error ? err.message : t("assetLibrary.moveFailed");
@ -578,7 +577,7 @@ export function AssetLibraryManagerModal({ open, onClose, onChanged }: AssetLibr
const isSelfOrDescendant = item.nodeType === "folder" && (
folder.id === item.id || Boolean(moveFolderAncestorIds[folder.id]?.has(item.id))
);
const disabled = isSelfOrDescendant;
const disabled = isSelfOrDescendant || isCurrentParent;
return (
<div key={folder.id}>
@ -604,10 +603,6 @@ export function AssetLibraryManagerModal({ open, onClose, onChanged }: AssetLibr
onClick={(event) => {
event.stopPropagation();
if (disabled) return;
if (isCurrentParent) {
setActionMenuOpenId(null);
return;
}
void handleMoveItem(item, folder.id);
}}
>
@ -650,20 +645,18 @@ export function AssetLibraryManagerModal({ open, onClose, onChanged }: AssetLibr
const rootFolders = moveFoldersByParentId[ROOT_PARENT_ID] ?? [];
const rootLoading = loadingMoveFolderIds.has(ROOT_PARENT_ID);
return (
<div className="max-h-[280px] min-w-[220px] overflow-y-auto p-1 [scrollbar-color:var(--text-muted)_transparent] [scrollbar-width:thin]">
{item.nodeType === "folder" && (
<button
type="button"
disabled={item.parentId === ROOT_PARENT_ID}
className="flex h-8 w-full items-center rounded-md px-3 text-left text-sm text-[var(--text-secondary)] transition-colors hover:bg-[var(--surface-hover)] hover:text-[var(--text-primary)] disabled:cursor-not-allowed disabled:opacity-45 disabled:hover:bg-transparent disabled:hover:text-[var(--text-secondary)]"
onClick={(event) => {
event.stopPropagation();
if (item.parentId !== ROOT_PARENT_ID) void handleMoveItem(item, ROOT_PARENT_ID);
}}
>
{t("assetLibrary.rootFolder")}
</button>
)}
<div className="max-h-[320px] w-[228px] overflow-y-auto p-1 [scrollbar-color:var(--text-muted)_transparent] [scrollbar-width:thin]">
<button
type="button"
disabled={item.parentId === ROOT_PARENT_ID}
className="flex h-8 w-full items-center rounded-md px-3 text-left text-sm text-[var(--text-secondary)] transition-colors hover:bg-[var(--surface-hover)] hover:text-[var(--text-primary)] disabled:cursor-not-allowed disabled:opacity-45 disabled:hover:bg-transparent disabled:hover:text-[var(--text-secondary)]"
onClick={(event) => {
event.stopPropagation();
if (item.parentId !== ROOT_PARENT_ID) void handleMoveItem(item, ROOT_PARENT_ID);
}}
>
{t("assetLibrary.rootFolder")}
</button>
{rootLoading ? (
<div className="px-3 py-2 text-sm text-[var(--text-muted)]">{t("common.processing")}</div>
) : rootFolders.length > 0 ? (
@ -675,90 +668,85 @@ export function AssetLibraryManagerModal({ open, onClose, onChanged }: AssetLibr
);
}, [handleMoveItem, loadingMoveFolderIds, moveFoldersByParentId, renderMoveTreeRows, t]);
const handleOpenMovePanel = useCallback((item: UserFileItem) => {
setMovePanelOpenId(item.id);
void loadMoveFolderChildren(ROOT_PARENT_ID);
}, [loadMoveFolderChildren]);
const renderItemActions = useCallback((item: UserFileItem) => {
const isFolder = item.nodeType === "folder";
const isMoveView = actionMenuView === "move";
const movePanelOpen = movePanelOpenId === item.id;
return (
<div
className="nodrag nopan nowheel min-w-[168px] rounded-lg bg-[var(--surface-2)] p-1 text-sm text-[var(--text-secondary)] shadow-[var(--shadow-panel)]"
className="nodrag nopan nowheel relative min-w-[168px] rounded-lg bg-[var(--surface-2)] p-1 text-sm text-[var(--text-secondary)] shadow-[var(--shadow-panel)]"
onClick={(event) => event.stopPropagation()}
>
{isMoveView ? (
<>
<div className="mb-1 flex h-8 items-center gap-1 px-1">
<button
type="button"
aria-label={t("common.back")}
className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-[var(--text-muted)] transition-colors hover:bg-[var(--surface-hover)] hover:text-[var(--text-primary)]"
onClick={() => setActionMenuView("actions")}
>
<LeftOutlined className="text-[12px]" />
</button>
<span className="min-w-0 flex-1 truncate text-sm font-medium text-[var(--text-primary)]">
{t("assetLibrary.moveTitle")}
</span>
</div>
{renderMovePanel(item)}
</>
) : (
<div className="flex flex-col">
<button
type="button"
className="flex h-8 w-full items-center gap-2 rounded-md px-3 text-left transition-colors hover:bg-[var(--surface-hover)] hover:text-[var(--text-primary)]"
onClick={() => {
setActionMenuView("move");
void loadMoveFolderChildren(ROOT_PARENT_ID);
}}
>
<SwapOutlined className="text-[14px]" />
<span className="flex-1">{t("assetLibrary.moveTitle")}</span>
<RightOutlined className="text-[10px]" />
</button>
{isFolder && (
<button
type="button"
className="flex h-8 items-center gap-2 rounded-md px-3 text-left transition-colors hover:bg-[var(--surface-hover)] hover:text-[var(--text-primary)]"
onClick={(event) => {
event.stopPropagation();
void message.info(t("assetLibrary.changeCoverUnsupported"));
}}
>
<PictureOutlined className="text-[14px]" />
<span>{t("assetLibrary.changeCover")}</span>
</button>
)}
<div className="flex flex-col">
<button
type="button"
className={[
"flex h-8 w-full items-center gap-2 rounded-md px-3 text-left transition-colors hover:bg-[var(--surface-hover)] hover:text-[var(--text-primary)]",
movePanelOpen ? "bg-[var(--surface-hover)] text-[var(--text-primary)]" : "",
].join(" ")}
onMouseEnter={() => handleOpenMovePanel(item)}
onClick={(event) => {
event.stopPropagation();
handleOpenMovePanel(item);
}}
>
<SwapOutlined className="text-[14px]" />
<span className="flex-1">{t("assetLibrary.moveTitle")}</span>
<RightOutlined className="text-[10px]" />
</button>
{isFolder && (
<button
type="button"
className="flex h-8 items-center gap-2 rounded-md px-3 text-left transition-colors hover:bg-[var(--surface-hover)] hover:text-[var(--text-primary)]"
onClick={(event) => {
event.stopPropagation();
setActionMenuOpenId(null);
setActionMenuView("actions");
openRenameModal(item);
void message.info(t("assetLibrary.changeCoverUnsupported"));
}}
>
<EditOutlined className="text-[14px]" />
<span>{t("assetLibrary.rename")}</span>
</button>
<button
type="button"
className="flex h-8 items-center gap-2 rounded-md px-3 text-left text-red-400 transition-colors hover:bg-red-500/10 hover:text-red-300"
onClick={(event) => {
event.stopPropagation();
setActionMenuOpenId(null);
setActionMenuView("actions");
handleDeleteItem(item);
}}
>
<DeleteOutlined className="text-[14px]" />
<span>{t("assetLibrary.delete")}</span>
<PictureOutlined className="text-[14px]" />
<span>{t("assetLibrary.changeCover")}</span>
</button>
)}
<button
type="button"
className="flex h-8 items-center gap-2 rounded-md px-3 text-left transition-colors hover:bg-[var(--surface-hover)] hover:text-[var(--text-primary)]"
onClick={(event) => {
event.stopPropagation();
setActionMenuOpenId(null);
setMovePanelOpenId(null);
openRenameModal(item);
}}
>
<EditOutlined className="text-[14px]" />
<span>{t("assetLibrary.rename")}</span>
</button>
<button
type="button"
className="flex h-8 items-center gap-2 rounded-md px-3 text-left text-red-400 transition-colors hover:bg-red-500/10 hover:text-red-300"
onClick={(event) => {
event.stopPropagation();
setActionMenuOpenId(null);
setMovePanelOpenId(null);
handleDeleteItem(item);
}}
>
<DeleteOutlined className="text-[14px]" />
<span>{t("assetLibrary.delete")}</span>
</button>
</div>
{movePanelOpen && (
<div className="absolute top-0 left-full z-20 ml-2 rounded-lg border border-[var(--border-subtle)] bg-[var(--surface-2)] text-sm text-[var(--text-secondary)] shadow-[var(--shadow-panel)]">
{renderMovePanel(item)}
</div>
)}
</div>
);
}, [actionMenuView, handleDeleteItem, loadMoveFolderChildren, openRenameModal, renderMovePanel, t]);
}, [handleDeleteItem, handleOpenMovePanel, movePanelOpenId, openRenameModal, renderMovePanel, t]);
const renderItem = (item: UserFileItem) => {
const isFolder = item.nodeType === "folder";
@ -848,7 +836,7 @@ export function AssetLibraryManagerModal({ open, onClose, onChanged }: AssetLibr
open={actionMenuOpenId === item.id}
onOpenChange={(nextOpen) => {
setActionMenuOpenId(nextOpen ? item.id : null);
setActionMenuView("actions");
if (!nextOpen) setMovePanelOpenId(null);
}}
trigger={["click"]}
placement="bottomRight"

Loading…
Cancel
Save