x-movie/src/Main.jsx

553 lines
22 KiB
React
Raw Normal View History

2025-06-02 03:19:36 +08:00
import React, { useState, useEffect, useContext, useCallback, useRef } from 'react';
2023-07-04 23:47:01 +08:00
import axios from 'axios';
import Container from '@mui/material/Container';
import Grid from '@mui/material/Grid';
import Pagination from '@mui/material/Pagination';
2025-06-03 01:29:26 +08:00
import { Link } from 'react-router-dom'; // useLocation removed as not directly used for this fix
2023-07-05 01:33:55 +08:00
import CircularProgress from '@mui/material/CircularProgress';
2025-06-02 06:20:16 +08:00
import TextField from '@mui/material/TextField';
import InputAdornment from '@mui/material/InputAdornment';
import IconButton from '@mui/material/IconButton';
import SearchIcon from '@mui/icons-material/Search';
import ClearIcon from '@mui/icons-material/Clear';
2023-07-04 23:47:01 +08:00
2025-06-03 01:29:26 +08:00
import ConfigContext from './Config'; // Ensure this path is correct
import MovieCard from './components/MovieCard'; // Ensure this path is correct
import CategoryNav from './components/CategoryNav'; // Ensure this path is correct
2023-07-04 23:47:01 +08:00
2025-06-03 01:29:26 +08:00
// 分类配置
2025-06-02 03:19:36 +08:00
const categories = [
{ label: '15min', value: '15min' },
{ label: '30min', value: '30min' },
{ label: '60min', value: '60min' },
{ label: '大于60min', value: '大于60min' },
2025-06-02 06:20:16 +08:00
{ label: '最新添加', value: '最新添加' },
2025-06-02 03:19:36 +08:00
];
2025-06-02 06:20:16 +08:00
const SEARCH_CATEGORY = 'search';
2025-06-02 03:19:36 +08:00
const LIMIT = 20;
2025-06-02 06:20:16 +08:00
const DEFAULT_CATEGORY = categories[0].value;
2025-06-02 03:19:36 +08:00
const usePersistedState = (key, defaultValue) => {
const [state, setState] = useState(() => {
const stored = localStorage.getItem(key);
2025-06-03 01:29:26 +08:00
try {
return stored ? JSON.parse(stored) : defaultValue;
} catch (e) {
console.warn(`Error parsing persisted state for key "${key}":`, e);
return defaultValue;
}
2025-06-02 03:19:36 +08:00
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(state));
}, [key, state]);
return [state, setState];
};
2024-03-28 03:40:13 +08:00
const Main = () => {
const config = useContext(ConfigContext);
2023-07-04 23:47:01 +08:00
2025-06-03 01:29:26 +08:00
const [loading, setLoading] = useState(true);
2025-06-02 03:19:36 +08:00
const [movies, setMovies] = useState([]);
const [pagination, setPagination] = useState({ page: 1, total: 0, pages: 1 });
2025-06-03 01:29:26 +08:00
const [searchInput, setSearchInput] = useState('');
const [activeCategory, setActiveCategory] = useState(DEFAULT_CATEGORY);
const [currentPage, setCurrentPage] = useState(1);
const [activeSearchQuery, setActiveSearchQuery] = useState('');
const [persistedParams, setPersistedParams] = usePersistedState('mainViewParams', {
lastKnownState: {
category: DEFAULT_CATEGORY,
page: 1,
searchQuery: '',
scrollPos: 0,
},
categoryHistory: Object.fromEntries([
...categories.map(cat => [cat.value, { lastPage: 1, scrollPos: 0 }]),
[SEARCH_CATEGORY, { lastPage: 1, scrollPos: 0, searchQuery: '' }]
]),
});
2025-06-02 06:20:16 +08:00
2025-06-03 01:29:26 +08:00
const isMounted = useRef(false);
const isPopStateNav = useRef(false);
const scrollTimeoutRef = useRef(null);
2025-06-02 06:20:16 +08:00
2025-06-03 00:44:35 +08:00
const fetchMovies = useCallback(async (category, page, search = '', options = {}) => {
setLoading(true);
2023-07-04 23:47:01 +08:00
try {
2025-06-02 06:20:16 +08:00
const isSearch = category === SEARCH_CATEGORY;
2025-06-02 21:29:31 +08:00
let apiUrl = `/movie/list?page=${page}&limit=${LIMIT}`;
2025-06-03 01:29:26 +08:00
2025-06-03 00:44:35 +08:00
if (isSearch) apiUrl += `&search=${encodeURIComponent(search)}`;
2025-06-03 01:29:26 +08:00
else if (category === '最新添加') apiUrl += `&sort=created_time&order=desc`;
2025-06-03 00:44:35 +08:00
else apiUrl += `&category=${encodeURIComponent(category)}`;
2025-06-02 06:20:16 +08:00
const response = await axios.get(apiUrl);
const { items, total } = response.data.data;
const totalPages = Math.ceil(total / LIMIT);
2025-06-03 01:29:26 +08:00
if (items.length === 0 && page > 1 && !isSearch && !options.isPopState) {
console.log("Empty page, current page has no items:", category, page);
2025-06-03 00:44:35 +08:00
setMovies([]);
2025-06-03 01:29:26 +08:00
setPagination({ page, total, pages: totalPages });
} else {
setMovies(items);
setPagination({ page, total, pages: totalPages });
2025-06-02 06:20:16 +08:00
}
2023-07-04 23:47:01 +08:00
} catch (error) {
2025-06-03 01:29:26 +08:00
console.error('获取电影数据失败:', error);
2025-06-03 00:44:35 +08:00
setMovies([]);
setPagination({ page, total: 0, pages: 1 });
2023-07-05 01:33:55 +08:00
} finally {
2025-06-03 01:29:26 +08:00
setLoading(false);
if (options.restoreScrollPos !== undefined && !options.skipScrollRestore) {
requestAnimationFrame(() => {
setTimeout(() => window.scrollTo(0, options.restoreScrollPos), 50);
});
} else if (!options.skipScrollRestore && !options.isPopStatePaging) {
window.scrollTo(0, 0);
}
isPopStateNav.current = false;
2023-07-04 23:47:01 +08:00
}
2025-06-03 01:29:26 +08:00
// IMPORTANT: Removed duplicated scroll logic block that was here
}, []); // No dependencies that change frequently, setPersistedParams is stable
2025-06-02 03:19:36 +08:00
2025-06-03 01:29:26 +08:00
const navigateAndFetch = useCallback((newCategory, newPage, newSearchQuery = '', options = {}) => {
const { replace = false, preserveScroll = false, isPopStatePaging = false } = options;
2025-06-02 06:20:16 +08:00
2025-06-03 01:29:26 +08:00
setActiveCategory(newCategory);
setCurrentPage(newPage);
setActiveSearchQuery(newSearchQuery);
if (newCategory === SEARCH_CATEGORY) {
setSearchInput(newSearchQuery);
} else {
setSearchInput('');
}
2025-06-03 00:44:35 +08:00
2025-06-03 01:29:26 +08:00
// Determine scroll position for history state
// If preserving scroll, use current window.scrollY or a remembered scroll for the target category if available
// Otherwise, new navigations (not popstate) typically scroll to 0 unless specified.
let scrollForHistory = 0;
if (preserveScroll) {
// For general preserveScroll (like back/fwd), use current scrollY.
// For specific category change restorations, this might be overridden by options.restoreScrollPos in fetchMovies.
scrollForHistory = window.history.state?.appState?.scrollPos || window.scrollY;
}
// If navigating to a category with a known scroll position, that should be prioritized for restoration.
// This is handled by passing restoreScrollPos to fetchMovies. For history state, use `scrollForHistory`.
2023-07-04 23:47:01 +08:00
2025-06-03 01:29:26 +08:00
const historyState = {
category: newCategory,
page: newPage,
searchQuery: newSearchQuery,
scrollPos: scrollForHistory, // This is the scroll position *at the moment of navigation*
};
const url = window.location.pathname; // Keep URL simple, no query params in URL itself for now
const browserHistoryState = window.history.state?.appState;
const needsPush = !browserHistoryState ||
browserHistoryState.category !== newCategory ||
browserHistoryState.page !== newPage ||
browserHistoryState.searchQuery !== newSearchQuery;
if (replace) {
window.history.replaceState({ appState: historyState }, '', url);
} else if (needsPush) {
window.history.pushState({ appState: historyState }, '', url);
2025-06-02 06:20:16 +08:00
}
2025-06-03 00:44:35 +08:00
2025-06-03 01:29:26 +08:00
setPersistedParams(prev => {
const newCategoryHistory = { ...prev.categoryHistory };
const oldCategoryState = prev.lastKnownState.category;
2025-06-02 06:20:16 +08:00
2025-06-03 01:29:26 +08:00
if (oldCategoryState && oldCategoryState !== newCategory) {
newCategoryHistory[oldCategoryState] = {
...newCategoryHistory[oldCategoryState],
scrollPos: window.scrollY, // Save scroll of category being left
};
}
2025-06-02 06:20:16 +08:00
2025-06-03 01:29:26 +08:00
newCategoryHistory[newCategory] = {
...newCategoryHistory[newCategory],
lastPage: newPage,
scrollPos: scrollForHistory, // Store the scroll we intend to be at for the new state
...(newCategory === SEARCH_CATEGORY && { searchQuery: newSearchQuery }),
};
return {
lastKnownState: historyState,
categoryHistory: newCategoryHistory,
};
});
2025-06-02 03:19:36 +08:00
2025-06-03 01:29:26 +08:00
// Determine scroll position for fetching movies
// If preserving scroll, it means we want to restore to where we were or a specific point
let scrollPosForFetch = scrollForHistory; // Default to the scrollForHistory calculated
if (!preserveScroll) { // If not preserving, new main navigations usually go to top
scrollPosForFetch = 0;
2025-06-03 00:44:35 +08:00
}
2025-06-03 01:29:26 +08:00
// If navigating to a category and it has a specific remembered scroll, that should be preferred
// This part becomes complex if we want category changes to always restore *their* scroll.
// For now, `preserveScroll` will try to keep `window.scrollY`, otherwise `0`.
// PopState correctly restores its specific scroll.
fetchMovies(newCategory, newPage, newSearchQuery, {
restoreScrollPos: scrollPosForFetch,
skipScrollRestore: false, // Allow fetchMovies to handle scroll unless explicitly told otherwise later
isPopStatePaging: isPopStatePaging,
});
}, [fetchMovies, setPersistedParams]);
2025-06-03 00:44:35 +08:00
2025-06-03 01:29:26 +08:00
useEffect(() => {
const handlePopState = (event) => {
if (event.state && event.state.appState) {
const { category, page, searchQuery, scrollPos } = event.state.appState;
isPopStateNav.current = true;
setActiveCategory(category);
setCurrentPage(page);
setActiveSearchQuery(searchQuery);
setSearchInput(searchQuery || '');
setPersistedParams(prev => ({
lastKnownState: event.state.appState,
categoryHistory: {
...prev.categoryHistory,
[category]: {
...prev.categoryHistory[category],
lastPage: page,
scrollPos: scrollPos,
...(category === SEARCH_CATEGORY && { searchQuery: searchQuery }),
}
}
}));
fetchMovies(category, page, searchQuery, { restoreScrollPos: scrollPos, isPopStatePaging: true });
} else {
const lastState = persistedParams.lastKnownState;
navigateAndFetch(lastState.category, lastState.page, lastState.searchQuery, { replace: true, preserveScroll: true });
}
};
2025-06-03 00:44:35 +08:00
2025-06-03 01:29:26 +08:00
window.addEventListener('popstate', handlePopState);
2025-06-02 06:20:16 +08:00
2025-06-03 01:29:26 +08:00
if (!isMounted.current) {
isMounted.current = true;
if ('scrollRestoration' in window.history) {
window.history.scrollRestoration = 'manual';
}
2025-06-03 00:44:35 +08:00
2025-06-03 01:29:26 +08:00
if (window.history.state && window.history.state.appState) {
const { category, page, searchQuery, scrollPos } = window.history.state.appState;
isPopStateNav.current = true;
setActiveCategory(category);
setCurrentPage(page);
setActiveSearchQuery(searchQuery);
setSearchInput(searchQuery || '');
setPersistedParams(prev => ({
lastKnownState: window.history.state.appState,
categoryHistory: {
...prev.categoryHistory,
[category]: { ...prev.categoryHistory[category], lastPage: page, scrollPos: scrollPos, ...(category === SEARCH_CATEGORY && { searchQuery: searchQuery }) }
}
}));
fetchMovies(category, page, searchQuery, { restoreScrollPos: scrollPos, isPopStatePaging: true });
2025-06-02 06:20:16 +08:00
} else {
2025-06-03 01:29:26 +08:00
const { category, page, searchQuery } = persistedParams.lastKnownState;
// For initial load from localStorage, use its scrollPos. navigateAndFetch with preserveScroll=true will handle it.
navigateAndFetch(category, page, searchQuery, { replace: true, preserveScroll: true });
2025-06-02 06:20:16 +08:00
}
2025-06-03 01:29:26 +08:00
}
2025-06-03 01:29:26 +08:00
return () => {
window.removeEventListener('popstate', handlePopState);
clearTimeout(scrollTimeoutRef.current);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [navigateAndFetch, setPersistedParams]); // Removed fetchMovies as navigateAndFetch calls it.
useEffect(() => {
const handleScroll = () => {
clearTimeout(scrollTimeoutRef.current);
scrollTimeoutRef.current = setTimeout(() => {
if (window.history.state && window.history.state.appState && !loading && movies.length > 0 && !isPopStateNav.current) {
const newScrollPos = window.scrollY;
const currentState = window.history.state.appState;
if (currentState.scrollPos !== newScrollPos) {
const updatedState = { ...currentState, scrollPos: newScrollPos };
window.history.replaceState({ appState: updatedState }, '', window.location.href);
setPersistedParams(prev => ({
lastKnownState: updatedState,
categoryHistory: {
...prev.categoryHistory,
[currentState.category]: {
...prev.categoryHistory[currentState.category],
lastPage: currentState.page,
scrollPos: newScrollPos,
...(currentState.category === SEARCH_CATEGORY && { searchQuery: currentState.searchQuery }),
}
}
}));
}
}
}, 150);
};
if (!loading && movies.length > 0) {
window.addEventListener('scroll', handleScroll, { passive: true });
}
return () => {
window.removeEventListener('scroll', handleScroll);
clearTimeout(scrollTimeoutRef.current);
};
}, [loading, movies.length, setPersistedParams]);
const handleCategoryChange = useCallback((newCategory) => {
// Save current scroll for the category we are leaving
setPersistedParams(prev => {
const oldCat = prev.lastKnownState.category;
return {
...prev,
categoryHistory: {
...prev.categoryHistory,
[oldCat]: { ...prev.categoryHistory[oldCat], scrollPos: window.scrollY }
}
};
2025-06-02 03:19:36 +08:00
});
2025-06-03 00:44:35 +08:00
2025-06-03 01:29:26 +08:00
let targetPage = 1;
let targetSearchQuery = '';
// For category change, restore to its own last known scroll position or top
const categorySpecificScroll = persistedParams.categoryHistory[newCategory]?.scrollPos || 0;
2023-07-10 00:50:11 +08:00
2025-06-03 01:29:26 +08:00
if (newCategory === SEARCH_CATEGORY) {
targetSearchQuery = persistedParams.categoryHistory[SEARCH_CATEGORY]?.searchQuery || searchInput.trim() || '';
targetPage = persistedParams.categoryHistory[SEARCH_CATEGORY]?.lastPage || 1;
} else {
targetPage = persistedParams.categoryHistory[newCategory]?.lastPage || 1;
}
// Update React state first
setActiveCategory(newCategory);
setCurrentPage(targetPage);
setActiveSearchQuery(targetSearchQuery);
if (newCategory === SEARCH_CATEGORY) {
setSearchInput(targetSearchQuery);
} else {
setSearchInput('');
}
// Then fetch, telling fetchMovies to restore to the category's specific scroll
const historyState = { category: newCategory, page: targetPage, searchQuery: targetSearchQuery, scrollPos: categorySpecificScroll };
window.history.pushState({ appState: historyState }, '', window.location.pathname);
setPersistedParams(prev => ({
lastKnownState: historyState,
categoryHistory: {
...prev.categoryHistory,
[newCategory]: { ...prev.categoryHistory[newCategory], lastPage: targetPage, scrollPos: categorySpecificScroll, ...(newCategory === SEARCH_CATEGORY && { searchQuery: targetSearchQuery }) }
2025-06-02 03:19:36 +08:00
}
}));
2025-06-03 01:29:26 +08:00
fetchMovies(newCategory, targetPage, targetSearchQuery, { restoreScrollPos: categorySpecificScroll });
2025-06-02 06:20:16 +08:00
2025-06-03 01:29:26 +08:00
}, [searchInput, persistedParams.categoryHistory, setPersistedParams, fetchMovies]);
const handlePageChange = useCallback((_, page) => {
setPersistedParams(prev => ({
...prev,
categoryHistory: {
...prev.categoryHistory,
[activeCategory]: { ...prev.categoryHistory[activeCategory], scrollPos: window.scrollY }
}
}));
// For page change, typically scroll to top
navigateAndFetch(activeCategory, page, activeSearchQuery, { preserveScroll: false });
}, [activeCategory, activeSearchQuery, navigateAndFetch, setPersistedParams]);
2025-06-02 03:19:36 +08:00
2025-06-02 06:20:16 +08:00
const handleSearchSubmit = useCallback(() => {
2025-06-03 00:44:35 +08:00
const trimmedSearch = searchInput.trim();
if (!trimmedSearch) return;
2025-06-03 01:29:26 +08:00
setPersistedParams(prev => {
const oldCat = prev.lastKnownState.category;
if (oldCat && oldCat !== SEARCH_CATEGORY) {
return { ...prev, categoryHistory: { ...prev.categoryHistory, [oldCat]: { ...prev.categoryHistory[oldCat], scrollPos: window.scrollY } } };
}
return prev;
2025-06-02 06:20:16 +08:00
});
2025-06-03 01:29:26 +08:00
navigateAndFetch(SEARCH_CATEGORY, 1, trimmedSearch, { preserveScroll: false });
}, [searchInput, navigateAndFetch, setPersistedParams]);
2025-06-03 00:44:35 +08:00
2025-06-02 06:20:16 +08:00
const handleClearSearch = useCallback(() => {
setSearchInput('');
2025-06-03 01:29:26 +08:00
if (activeCategory === SEARCH_CATEGORY) {
setPersistedParams(prev => ({
...prev,
categoryHistory: {
...prev.categoryHistory,
[SEARCH_CATEGORY]: { ...prev.categoryHistory[SEARCH_CATEGORY], scrollPos: window.scrollY, searchQuery: '', lastPage: 1 }
}
}));
const targetCategory = DEFAULT_CATEGORY;
const targetPage = persistedParams.categoryHistory[targetCategory]?.lastPage || 1;
const targetScroll = persistedParams.categoryHistory[targetCategory]?.scrollPos || 0;
// Manually set active states before calling fetch to avoid race with navigateAndFetch
setActiveCategory(targetCategory);
setCurrentPage(targetPage);
setActiveSearchQuery('');
const historyState = { category: targetCategory, page: targetPage, searchQuery: '', scrollPos: targetScroll };
window.history.pushState({ appState: historyState }, '', window.location.pathname);
setPersistedParams(prev => ({
lastKnownState: historyState,
categoryHistory: {
...prev.categoryHistory,
[targetCategory]: { ...prev.categoryHistory[targetCategory], lastPage: targetPage, scrollPos: targetScroll }
}
}));
fetchMovies(targetCategory, targetPage, '', { restoreScrollPos: targetScroll });
2025-06-02 06:20:16 +08:00
} else {
2025-06-03 01:29:26 +08:00
setActiveSearchQuery('');
setPersistedParams(prev => ({ ...prev, lastKnownState: { ...prev.lastKnownState, searchQuery: '' } }));
2025-06-02 06:20:16 +08:00
}
2025-06-03 01:29:26 +08:00
}, [activeCategory, navigateAndFetch, persistedParams.categoryHistory, setPersistedParams, fetchMovies]);
2025-06-02 06:20:16 +08:00
2025-06-03 01:29:26 +08:00
// MODIFIED handleRename
2025-06-03 00:44:35 +08:00
const handleRename = useCallback(async (oldName, newName) => {
2025-06-03 01:29:26 +08:00
const scrollPosBeforeRename = window.scrollY; // 1. Capture scroll position
2025-06-03 00:44:35 +08:00
try {
const response = await axios.post('/movie/rename', { old_name: oldName, new_name: newName });
if (response.data.code === 200) {
2025-06-03 01:29:26 +08:00
// 2. await fetchMovies to ensure data is re-fetched and state is updated.
// Pass skipScrollRestore: true so fetchMovies doesn't scroll by itself.
await fetchMovies(activeCategory, currentPage, activeSearchQuery, { skipScrollRestore: true });
// 3. After movies are fetched and React has re-rendered, restore the scroll position.
requestAnimationFrame(() => {
window.scrollTo(0, scrollPosBeforeRename);
});
2025-06-03 00:44:35 +08:00
} else {
throw new Error(response.data.message || '重命名失败');
}
} catch (error) {
console.error('重命名错误:', error);
2025-06-03 01:29:26 +08:00
throw error; // Re-throw for MovieCard to handle Snackbar
2025-06-02 06:20:16 +08:00
}
2025-06-03 01:29:26 +08:00
}, [fetchMovies, activeCategory, currentPage, activeSearchQuery]); // Added fetchMovies dependency
2025-06-03 00:44:35 +08:00
const handleMovieCardClick = useCallback(() => {
2025-06-03 01:29:26 +08:00
if (window.history.state && window.history.state.appState) {
const currentScrollPos = window.scrollY;
const currentState = window.history.state.appState;
if (currentState.scrollPos !== currentScrollPos) { // Only update if changed
const updatedState = { ...currentState, scrollPos: currentScrollPos };
window.history.replaceState({ appState: updatedState }, '', window.location.href);
setPersistedParams(prev => ({
...prev,
lastKnownState: updatedState,
categoryHistory: {
...prev.categoryHistory,
[updatedState.category]: {
...prev.categoryHistory[updatedState.category],
lastPage: updatedState.page,
scrollPos: currentScrollPos,
...(updatedState.category === SEARCH_CATEGORY && { searchQuery: updatedState.searchQuery }),
}
}
}));
}
}
}, [setPersistedParams]);
2025-06-03 00:44:35 +08:00
2025-06-03 01:29:26 +08:00
const handleSearchChange = (e) => setSearchInput(e.target.value);
const handleSearchKeyDown = (e) => {
if (e.key === 'Enter') handleSearchSubmit();
};
2025-06-02 06:20:16 +08:00
2025-06-03 01:29:26 +08:00
const paginationComponent = !loading && movies.length > 0 && pagination.pages > 1 && (
2025-06-02 06:20:16 +08:00
<Pagination
count={pagination.pages}
2025-06-03 01:29:26 +08:00
page={currentPage}
2025-06-02 06:20:16 +08:00
onChange={handlePageChange}
sx={{ my: 2, display: 'flex', justifyContent: 'center' }}
/>
);
2023-07-04 23:47:01 +08:00
return (
2023-07-08 03:15:48 +08:00
<Container style={{ marginTop: 20 }}>
2025-06-02 06:20:16 +08:00
<TextField
fullWidth
variant="outlined"
placeholder="搜索文件名..."
value={searchInput}
onChange={handleSearchChange}
onKeyDown={handleSearchKeyDown}
InputProps={{
2025-06-03 00:44:35 +08:00
startAdornment: <InputAdornment position="start"><SearchIcon /></InputAdornment>,
2025-06-02 06:20:16 +08:00
endAdornment: searchInput && (
<InputAdornment position="end">
2025-06-03 00:44:35 +08:00
<IconButton onClick={handleClearSearch} edge="end"><ClearIcon /></IconButton>
2025-06-02 06:20:16 +08:00
</InputAdornment>
)
}}
sx={{ mb: 2 }}
/>
2023-07-09 11:30:56 +08:00
<CategoryNav
2024-03-28 03:40:13 +08:00
categories={categories}
2025-06-03 01:29:26 +08:00
currentCategory={activeCategory}
2023-07-09 11:30:56 +08:00
onCategoryChange={handleCategoryChange}
/>
2025-06-03 01:29:26 +08:00
{activeCategory === SEARCH_CATEGORY && activeSearchQuery && (
2025-06-02 06:20:16 +08:00
<div style={{ textAlign: 'center', margin: '10px 0' }}>
2025-06-03 01:29:26 +08:00
搜索: "{activeSearchQuery}" ({pagination.total} 个结果)
2025-06-02 06:20:16 +08:00
</div>
)}
{paginationComponent}
2025-06-03 00:44:35 +08:00
{loading && <CircularProgress sx={{ display: 'block', margin: '20px auto' }} />}
{!loading && movies.length === 0 && (
<div style={{ textAlign: 'center', margin: '20px 0', color: 'gray' }}>
2025-06-03 01:29:26 +08:00
{activeCategory === SEARCH_CATEGORY && (searchInput || activeSearchQuery) ? `没有找到关于 "${searchInput || activeSearchQuery}" 的结果。` : '没有找到结果。'}
2025-06-03 00:44:35 +08:00
</div>
)}
{!loading && movies.length > 0 && (
2025-06-02 03:19:36 +08:00
<Grid container spacing={2} sx={{ mt: 0.5 }}>
2025-06-02 06:20:16 +08:00
{movies.map(item => (
2025-06-02 03:19:36 +08:00
<Grid item xs={6} sm={4} md={3} lg={2} key={item.filename}>
<Link
2025-06-03 01:29:26 +08:00
to={`/res/${item.filename}`} // Ensure your routing handles this path
style={{ textDecoration: 'none' }}
2025-06-02 03:19:36 +08:00
onClick={handleMovieCardClick}
>
2025-06-02 21:29:31 +08:00
<MovieCard movie={item} config={config} onRename={handleRename} />
2025-06-02 03:19:36 +08:00
</Link>
</Grid>
))}
</Grid>
)}
2025-06-02 06:20:16 +08:00
{paginationComponent}
2023-07-04 23:47:01 +08:00
</Container>
);
};
export default Main;