diff --git a/src/App.js b/src/App.js deleted file mode 100644 index d424c4d..0000000 --- a/src/App.js +++ /dev/null @@ -1,96 +0,0 @@ -import React, { useState, useEffect } from 'react'; - -import AppBar from '@mui/material/AppBar'; -import Avatar from '@mui/material/Avatar'; -import Box from '@mui/material/Box'; -import IconButton from '@mui/material/IconButton'; -import Toolbar from '@mui/material/Toolbar'; -import Typography from '@mui/material/Typography'; -import ExitToAppIcon from '@mui/icons-material/ExitToApp'; -import { Route, Router, Routes } from 'react-router-dom'; -import ConfigContext, { config } from './Config'; -import LoginForm from './LoginForm'; -import Main from './Main'; -import VideoPlayer from './VideoPlayer'; -import { default as VuetifyLogo, default as VuetifyName } from './logo.svg'; - -import axios from 'axios'; -import { useLocation, useNavigate } from 'react-router-dom'; -import jwtDecode from 'jwt-decode'; - -axios.interceptors.request.use((config) => { - const token = localStorage.getItem('token'); - if (token) { - config.headers.Authorization = `Bearer ${token}`; - } - return config; -}); - -const App = () => { - const navigate = useNavigate(); - const location = useLocation(); - - const [username, setUsername] = useState(''); - - useEffect(() => { - const token = localStorage.getItem('token'); - if (token) { - const decodedToken = jwtDecode(token); - setUsername(decodedToken.username); - } - }, []); - - axios.interceptors.response.use( - (response) => { - return response; - }, - (error) => { - if (error.response.status === 401) { - sessionStorage.setItem('previousLocation', location.pathname); - navigate('/login'); - } - return Promise.reject(error); - } - ); - - const handleLogout = () => { - localStorage.removeItem('token'); - setUsername(''); - navigate('/login'); - }; - - return ( - -
- - - - - Vuetify Logo - - - - {username && ( - - {username.charAt(0).toUpperCase()} - - {username} - - - )} - - - - - -
- - } /> - } /> - } /> - -
- ); -}; - -export default App; \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx new file mode 100644 index 0000000..b833387 --- /dev/null +++ b/src/App.jsx @@ -0,0 +1,36 @@ +import React, { useContext } from 'react'; +import { Route, Routes } from 'react-router-dom'; +import LoginForm from './components/LoginForm'; +import Main from './Main'; +import VideoPlayer from './components/VideoPlayer'; +import { AuthProvider } from './AuthContext'; +import ConfigContext, { config } from './Config'; +import Bar from './components/Bar'; + +const App = () => { + return ( + + + + + + ); +}; + +const AppContent = () => { + + + return ( + <> + + + + } /> + } /> + } /> + + + ); +}; + +export default App; \ No newline at end of file diff --git a/src/AuthContext.jsx b/src/AuthContext.jsx new file mode 100644 index 0000000..bb8df59 --- /dev/null +++ b/src/AuthContext.jsx @@ -0,0 +1,56 @@ +import { createContext, useState, useEffect } from 'react'; +import axios from 'axios'; +import jwtDecode from 'jwt-decode'; +import { useLocation, useNavigate } from 'react-router-dom'; + + +export const AuthContext = createContext(); + +export const AuthProvider = ({ children }) => { + + const navigate = useNavigate(); + const location = useLocation(); + + const [username, setUsername] = useState(''); + + useEffect(() => { + const token = localStorage.getItem('token'); + if (token) { + const decodedToken = jwtDecode(token); + setUsername(decodedToken.username); + } + }, []); + + axios.interceptors.request.use((config) => { + const token = localStorage.getItem('token'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }); + + axios.interceptors.response.use( + (response) => { + return response; + }, + (error) => { + if (error.response.status === 401) { + sessionStorage.setItem('previousLocation', location.pathname); + navigate('/login'); + } + return Promise.reject(error); + } + ); + + const handleLogout = () => { + localStorage.removeItem('token'); + setUsername(''); + navigate('/login'); + }; + + return ( + + {children} + + ); +}; \ No newline at end of file diff --git a/src/Config.js b/src/Config.jsx similarity index 100% rename from src/Config.js rename to src/Config.jsx diff --git a/src/Main.js b/src/Main.jsx similarity index 79% rename from src/Main.js rename to src/Main.jsx index 8589a46..1ec5f79 100644 --- a/src/Main.js +++ b/src/Main.jsx @@ -1,19 +1,15 @@ // Main.js -import React, { useState, useEffect, useContext } from 'react'; +import React, { useState, useEffect, useContext, useCallback } from 'react'; import axios from 'axios'; import Container from '@mui/material/Container'; import Grid from '@mui/material/Grid'; -import Card from '@mui/material/Card'; -import CardMedia from '@mui/material/CardMedia'; -import CardContent from '@mui/material/CardContent'; -import Typography from '@mui/material/Typography'; import Pagination from '@mui/material/Pagination'; import { Link } from 'react-router-dom'; import CircularProgress from '@mui/material/CircularProgress'; import ConfigContext from './Config'; -import MovieCard from './MovieCard'; -import CategoryNav from './CategoryNav'; +import MovieCard from './components/MovieCard'; +import CategoryNav from './components/CategoryNav'; const Main = () => { @@ -45,20 +41,19 @@ const Main = () => { }; - const fetchMovies = async (category, page) => { + const fetchMovies = useCallback(async (category, page) => { setLoading(true); try { const response = await axios.get( `${config.Host}/movie/?page=${page}&limit=${limit}&category=${category}` ); - if (response.status === 200) { const data = response.data.data; - + if (data.items.length === 0 && page > 1) { // 如果返回的数据为空且请求的页码大于1,则尝试获取上一页的数据 - fetchMovies(page - 1); + fetchMovies(category, page - 1); } else { setPagination({ movies: data.items, @@ -74,24 +69,18 @@ const Main = () => { } finally { setLoading(false); } - }; + }, [config.Host, limit]); useEffect(() => { const lastPage = localStorage.getItem('lastPage') || 1; fetchMovies(currentCategory, lastPage); - }, []); + }, [ currentCategory, fetchMovies]); const handlePageChange = (event, value) => { fetchMovies(currentCategory, value); }; - - const truncateFilename = (filename, maxLength) => { - return filename.length > maxLength - ? filename.substring(0, maxLength - 3) + '...' - : filename; - }; - - + + return ( {
{loading ? (
@@ -127,12 +111,7 @@ const Main = () => { {pagination.movies.map((item) => ( - diff --git a/src/axiosConfig.js b/src/axiosConfig.js deleted file mode 100644 index 0519ecb..0000000 --- a/src/axiosConfig.js +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/components/Bar.jsx b/src/components/Bar.jsx new file mode 100644 index 0000000..69151fc --- /dev/null +++ b/src/components/Bar.jsx @@ -0,0 +1,39 @@ +import React, { useContext } from 'react'; +import AppBar from '@mui/material/AppBar'; +import Avatar from '@mui/material/Avatar'; +import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import Toolbar from '@mui/material/Toolbar'; +import Typography from '@mui/material/Typography'; +import ExitToAppIcon from '@mui/icons-material/ExitToApp'; +import { default as VuetifyLogo } from '../logo.svg'; +import { AuthContext } from '../AuthContext'; + +const Bar = () => { + const { username, handleLogout } = useContext(AuthContext); + + return ( + + + + + Vuetify Logo + + + {username && ( + + {username.charAt(0).toUpperCase()} + + {username} + + + )} + + + + + + ); +}; + +export default Bar; \ No newline at end of file diff --git a/src/CategoryNav.js b/src/components/CategoryNav.jsx similarity index 100% rename from src/CategoryNav.js rename to src/components/CategoryNav.jsx diff --git a/src/LoginForm.js b/src/components/LoginForm.jsx similarity index 98% rename from src/LoginForm.js rename to src/components/LoginForm.jsx index 574eac3..180f559 100644 --- a/src/LoginForm.js +++ b/src/components/LoginForm.jsx @@ -1,7 +1,7 @@ // LoginForm.js import React, { useContext, useState } from 'react'; import axios from 'axios'; -import ConfigContext from './Config'; +import ConfigContext from '../Config'; import { useNavigate } from 'react-router-dom'; import { Box, diff --git a/src/MovieCard.js b/src/components/MovieCard.jsx similarity index 100% rename from src/MovieCard.js rename to src/components/MovieCard.jsx diff --git a/src/VideoPlayer.js b/src/components/VideoPlayer.jsx similarity index 94% rename from src/VideoPlayer.js rename to src/components/VideoPlayer.jsx index 2297427..9d49bfb 100644 --- a/src/VideoPlayer.js +++ b/src/components/VideoPlayer.jsx @@ -2,7 +2,7 @@ import {React, useContext} from 'react'; import { useParams } from 'react-router-dom'; import Container from '@mui/material/Container'; import Typography from '@mui/material/Typography'; -import ConfigContext from './Config'; +import ConfigContext from '../Config'; diff --git a/src/index.js b/src/index.jsx similarity index 72% rename from src/index.js rename to src/index.jsx index c34ba83..aab5116 100644 --- a/src/index.js +++ b/src/index.jsx @@ -3,10 +3,8 @@ import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; -import { BrowserRouter,Router, Routes, Route} from 'react-router-dom'; -import LoginForm from './LoginForm'; -import Main from './Main'; -import VideoPlayer from './VideoPlayer'; // 导入我们将创建的VideoPlayer组件 +import { BrowserRouter } from 'react-router-dom'; + const root = ReactDOM.createRoot(document.getElementById('root'));