@@ -27,6 +27,7 @@ import { CSS } from "@dnd-kit/utilities";
2727import CheckIcon from "@mui/icons-material/Check" ;
2828import RemoveCircleOutlineIcon from "@mui/icons-material/RemoveCircleOutline" ;
2929import Box from "@mui/material/Box" ;
30+ import Button from "@mui/material/Button" ;
3031import Card from "@mui/material/Card" ;
3132import CardActionArea from "@mui/material/CardActionArea" ;
3233import CardMedia from "@mui/material/CardMedia" ;
@@ -241,16 +242,18 @@ function useDragSort(options: {
241242 const { gamesData, categoryId, enabled } = options ;
242243 const updateCategoryGamesMutation = useUpdateCategoryGames ( ) ;
243244
244- const [ games , setGames ] = useState ( gamesData ) ;
245+ const [ sortableGames , setSortableGames ] = useState ( gamesData ) ;
245246 const [ activeId , setActiveId ] = useState < number | null > ( null ) ;
246247 const isDraggingRef = useRef ( false ) ;
247248
248- // 同步外部数据到本地状态(仅在非拖拽状态下)
249+ const games = enabled ? sortableGames : gamesData ;
250+
251+ // 排序模式保留本地顺序,非排序模式直接使用外部数据,避免删除后慢一帧
249252 useEffect ( ( ) => {
250- if ( ! isDraggingRef . current ) {
251- setGames ( gamesData ) ;
253+ if ( enabled && ! isDraggingRef . current ) {
254+ setSortableGames ( gamesData ) ;
252255 }
253- } , [ gamesData ] ) ;
256+ } , [ enabled , gamesData ] ) ;
254257
255258 // 传感器配置
256259 const sensors = useSensors (
@@ -271,6 +274,11 @@ function useDragSort(options: {
271274 [ enabled ] ,
272275 ) ;
273276
277+ const handleDragCancel = useCallback ( ( ) => {
278+ isDraggingRef . current = false ;
279+ setActiveId ( null ) ;
280+ } , [ ] ) ;
281+
274282 const handleDragEnd = useCallback (
275283 async ( event : DragEndEvent ) => {
276284 const { active, over } = event ;
@@ -286,7 +294,7 @@ function useDragSort(options: {
286294
287295 if ( oldIndex !== - 1 && newIndex !== - 1 ) {
288296 const newGames = arrayMove ( games , oldIndex , newIndex ) ;
289- setGames ( newGames ) ;
297+ setSortableGames ( newGames ) ;
290298
291299 try {
292300 const gameIds = newGames . map ( ( g ) => g . id as number ) ;
@@ -296,14 +304,11 @@ function useDragSort(options: {
296304 } ) ;
297305 } catch ( error ) {
298306 console . error ( "排序更新失败:" , error ) ;
299- setGames ( games ) ; // 回滚
307+ setSortableGames ( games ) ; // 回滚
300308 }
301309 }
302310
303- // 延迟重置拖拽状态
304- setTimeout ( ( ) => {
305- isDraggingRef . current = false ;
306- } , 100 ) ;
311+ isDraggingRef . current = false ;
307312 } ,
308313 [ games , categoryId , updateCategoryGamesMutation ] ,
309314 ) ;
@@ -319,6 +324,7 @@ function useDragSort(options: {
319324 activeGame,
320325 sensors,
321326 handleDragStart,
327+ handleDragCancel,
322328 handleDragEnd,
323329 } ;
324330}
@@ -415,7 +421,6 @@ export const CardItem = memo(
415421 image = { coverImage }
416422 alt = "Card Image"
417423 draggable = "false"
418- loading = "lazy"
419424 />
420425 < div
421426 className = { `flex items-center justify-center h-8 px-1 w-full ${ isActive ? "!font-bold text-blue-500" : "" } ` }
@@ -488,6 +493,10 @@ const Cards: React.FC<CardsProps> = ({ gamesData, categoryId }) => {
488493 const isCollectionCategory = typeof categoryId === "number" && categoryId > 0 ;
489494 const canUseBatchMode = isLibraries || isCollectionCategory ;
490495
496+ // 卡片渲染限制
497+ const CARD_LIMIT = 168 ;
498+ const [ showAll , setShowAll ] = useState ( false ) ;
499+
491500 // Store 状态
492501 const {
493502 selectedGameId,
@@ -531,26 +540,31 @@ const Cards: React.FC<CardsProps> = ({ gamesData, categoryId }) => {
531540 ! ! gamesData ;
532541
533542 // 拖拽排序 Hook
534- const { games, activeGame, sensors, handleDragStart, handleDragEnd } =
535- useDragSort ( {
536- gamesData,
537- categoryId,
538- enabled : isSortable ,
539- } ) ;
543+ const {
544+ games,
545+ activeGame,
546+ sensors,
547+ handleDragStart,
548+ handleDragCancel,
549+ handleDragEnd,
550+ } = useDragSort ( {
551+ gamesData,
552+ categoryId,
553+ enabled : isSortable ,
554+ } ) ;
555+
556+ // 根据 showAll 状态决定显示的卡片数量
557+ const displayedGames = useMemo (
558+ ( ) => ( showAll ? games : games . slice ( 0 , CARD_LIMIT ) ) ,
559+ [ games , showAll ] ,
560+ ) ;
540561
541562 // 缓存 SortableContext 的 items 数组,避免每次渲染重新创建
542563 const sortableIds = useMemo ( ( ) => games . map ( ( g ) => g . id as number ) , [ games ] ) ;
543564 const gameIds = useMemo (
544565 ( ) => games . map ( ( game ) => game . id ) . filter ( ( id ) : id is number => id != null ) ,
545566 [ games ] ,
546567 ) ;
547- const selectionScope = `${ path } :${ categoryId ?? "all" } ` ;
548-
549- useEffect ( ( ) => {
550- void selectionScope ;
551- setBatchMode ( false ) ;
552- setSelectedBatchGameIds ( [ ] ) ;
553- } , [ selectionScope ] ) ;
554568
555569 const toggleBatchGame = useCallback ( ( gameId : number ) => {
556570 setSelectedBatchGameIds ( ( prev ) =>
@@ -773,7 +787,7 @@ const Cards: React.FC<CardsProps> = ({ gamesData, categoryId }) => {
773787 "text-center grid lg:grid-cols-6 xl:grid-cols-7 2xl:grid-cols-8 3xl:grid-cols-10 4xl:grid-cols-12 gap-4"
774788 }
775789 >
776- { games . map ( ( card ) => {
790+ { displayedGames . map ( ( card ) => {
777791 const props = getCardProps ( card ) ;
778792 return isSortable ? (
779793 < SortableCardItem key = { card . id } { ...props } />
@@ -782,6 +796,16 @@ const Cards: React.FC<CardsProps> = ({ gamesData, categoryId }) => {
782796 ) ;
783797 } ) }
784798 </ div >
799+ { ! showAll && games . length > CARD_LIMIT && (
800+ < Box className = "flex justify-center py-6" >
801+ < Button variant = "outlined" onClick = { ( ) => setShowAll ( true ) } >
802+ { t ( "components.Cards.loadAll" , {
803+ defaultValue : `加载全部(${ games . length } )` ,
804+ count : games . length ,
805+ } ) }
806+ </ Button >
807+ </ Box >
808+ ) }
785809 </ div >
786810 </ >
787811 ) ;
@@ -793,6 +817,7 @@ const Cards: React.FC<CardsProps> = ({ gamesData, categoryId }) => {
793817 sensors = { sensors }
794818 collisionDetection = { closestCenter }
795819 onDragStart = { handleDragStart }
820+ onDragCancel = { handleDragCancel }
796821 onDragEnd = { handleDragEnd }
797822 >
798823 < SortableContext items = { sortableIds } strategy = { rectSortingStrategy } >
0 commit comments