perf(platform): 短剧创作工作台 - 拆分长篇项目接口并优化章节与素材分页
This commit is contained in:
@@ -6,6 +6,7 @@ import { useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
||||
import { AssetMediaPreview } from "@/components/asset-media-preview";
|
||||
import { AssetLibraryCard } from "@/components/assets/asset-library-card";
|
||||
import { CachedResourceImage } from "@/components/cached-resource-image";
|
||||
import { PaginationBar } from "@/components/layout/workspace-page";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { ExternalAssetPickerReference } from "@/lib/plugins/plugin-types";
|
||||
import type { Asset } from "@/stores/use-asset-store";
|
||||
@@ -47,6 +48,8 @@ type Props = {
|
||||
emptyTitle?: string;
|
||||
emptyDescription?: string;
|
||||
footerNote?: string;
|
||||
loading?: boolean;
|
||||
pagination?: { current: number; pageSize: number; total: number; onChange: (page: number, pageSize: number) => void };
|
||||
folderActionLabel?: string;
|
||||
folderActionSource?: "local" | "all";
|
||||
upload?: {
|
||||
@@ -79,6 +82,8 @@ export function AssetLibraryPickerModal({
|
||||
emptyTitle = "这个分类还没有素材",
|
||||
emptyDescription = "换个分类后再试。",
|
||||
footerNote,
|
||||
loading = false,
|
||||
pagination,
|
||||
folderActionLabel = "将文件夹放到画布",
|
||||
folderActionSource = "all",
|
||||
upload,
|
||||
@@ -123,10 +128,10 @@ export function AssetLibraryPickerModal({
|
||||
return !query || [item.title, item.searchText || "", item.description || ""].join(" ").toLowerCase().includes(query);
|
||||
});
|
||||
}, [category, folderId, keyword, sourceItems]);
|
||||
const selectedIds = useMemo(
|
||||
() => allItems.filter((item) => !item.disabledReason && selected.has(item.id)).map((item) => item.id),
|
||||
[allItems, selected],
|
||||
);
|
||||
const selectedIds = useMemo(() => Array.from(selected).filter((id) => {
|
||||
const item = allItems.find((entry) => entry.id === id);
|
||||
return !item?.disabledReason;
|
||||
}), [allItems, selected]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
@@ -272,7 +277,7 @@ export function AssetLibraryPickerModal({
|
||||
</div>
|
||||
</div>
|
||||
<label className="asset-picker-search"><Search aria-hidden /><input value={keyword} onChange={(event) => setKeyword(event.target.value)} placeholder="搜索素材名称或标签" aria-label="搜索素材" /></label>
|
||||
<span className="asset-picker-count">已选 {selectedIds.length} · {visibleItems.length} 个素材</span>
|
||||
<span className="asset-picker-count">已选 {selectedIds.length} · {pagination ? pagination.total : visibleItems.length} 个素材</span>
|
||||
</header>
|
||||
<div className="asset-picker-body">
|
||||
<nav className="asset-picker-categories" aria-label="素材分类">
|
||||
@@ -285,12 +290,15 @@ export function AssetLibraryPickerModal({
|
||||
</nav>
|
||||
<div className="asset-picker-grid-wrap">
|
||||
<div className="asset-picker-grid">
|
||||
{visibleItems.length ? visibleItems.map((item) => (
|
||||
{loading ? (
|
||||
<div className="asset-picker-empty"><LoaderCircle className="animate-spin" /><strong>正在读取素材</strong><span>素材会按页加载,不会一次下载整个项目库。</span></div>
|
||||
) : visibleItems.length ? visibleItems.map((item) => (
|
||||
<PickerCard key={item.id} item={item} selected={selected.has(item.id)} onToggle={() => toggle(item)} />
|
||||
)) : (
|
||||
<div className="asset-picker-empty"><FolderOpen /><strong>{emptyTitle}</strong><span>{activeUpload ? "换个分类,或从底部上传一份新素材。" : emptyDescription}</span></div>
|
||||
)}
|
||||
</div>
|
||||
{pagination ? <PaginationBar alwaysShow current={pagination.current} pageSize={pagination.pageSize} total={pagination.total} itemLabel="项" pageSizeOptions={[20, 40, 80]} onChange={pagination.onChange} /> : null}
|
||||
</div>
|
||||
</div>
|
||||
<footer className={cn("asset-picker-footer", !activeUpload && "is-compact")}>
|
||||
|
||||
@@ -106,6 +106,7 @@ export function PaginationBar({
|
||||
onChange,
|
||||
pageSizeOptions = [20, 50, 100],
|
||||
alwaysShow = false,
|
||||
itemLabel = "条",
|
||||
}: {
|
||||
current: number;
|
||||
pageSize: number;
|
||||
@@ -113,6 +114,7 @@ export function PaginationBar({
|
||||
onChange: (page: number, pageSize: number) => void;
|
||||
pageSizeOptions?: number[];
|
||||
alwaysShow?: boolean;
|
||||
itemLabel?: string;
|
||||
}) {
|
||||
if (!alwaysShow && total <= pageSize && current === 1) return null;
|
||||
const pages = Math.max(1, Math.ceil(total / pageSize));
|
||||
@@ -121,8 +123,8 @@ export function PaginationBar({
|
||||
const items = pageItems(current, pages);
|
||||
return (
|
||||
<div className="app-pagination-bar admin-pagination-bar mt-4 flex min-h-10 min-w-0 items-center justify-end gap-2 px-2 py-1.5">
|
||||
<span className="admin-pagination-total">{total === 0 ? "共 0 条" : `${start}-${end} / 共 ${total} 条`}</span>
|
||||
<Select size="small" value={pageSize} className="app-pagination-size" options={pageSizeOptions.map((size) => ({ value: size, label: `${size} 条/页` }))} onChange={(value) => onChange(1, Number(value))} />
|
||||
<span className="admin-pagination-total">{total === 0 ? `共 0 ${itemLabel}` : `${start}-${end} / 共 ${total} ${itemLabel}`}</span>
|
||||
<Select size="small" value={pageSize} className="app-pagination-size" options={pageSizeOptions.map((size) => ({ value: size, label: `${size} ${itemLabel}/页` }))} onChange={(value) => onChange(1, Number(value))} />
|
||||
<div className="app-pagination-pages" role="navigation" aria-label="分页">
|
||||
<button type="button" className="app-pagination-btn app-pagination-prev" disabled={current <= 1} aria-label="上一页" onClick={() => onChange(current - 1, pageSize)}>
|
||||
<ChevronLeft className="size-4" />
|
||||
|
||||
Reference in New Issue
Block a user