简化代码和组件位置

This commit is contained in:
eson 2023-07-09 05:58:33 +08:00
parent 505c187eb7
commit 29331006a6
12 changed files with 147 additions and 136 deletions

View File

@ -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 (
<ConfigContext.Provider value={config}>
<div>
<AppBar position="static">
<Toolbar>
<Box display="flex" alignItems="center" flexGrow={1}>
<IconButton edge="start" color="inherit" aria-label="menu">
<img src={VuetifyLogo} alt="Vuetify Logo" width="40" />
</IconButton>
</Box>
{username && (
<Box display="flex" alignItems="center" marginRight="1rem">
<Avatar>{username.charAt(0).toUpperCase()}</Avatar>
<Typography variant="subtitle1" style={{ marginLeft: '1rem' }}>
{username}
</Typography>
</Box>
)}
<IconButton edge="end" color="inherit" aria-label="logout" onClick={handleLogout}>
<ExitToAppIcon />
</IconButton>
</Toolbar>
</AppBar>
</div>
<Routes>
<Route path="/login" element={<LoginForm />} />
<Route path="/" element={<Main />} />
<Route path="/res/:filename" element={<VideoPlayer />} />
</Routes>
</ConfigContext.Provider>
);
};
export default App;

36
src/App.jsx Normal file
View File

@ -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 (
<ConfigContext.Provider value={config}>
<AuthProvider>
<AppContent />
</AuthProvider>
</ConfigContext.Provider>
);
};
const AppContent = () => {
return (
<>
<Bar></Bar>
<Routes>
<Route path="/login" element={<LoginForm />} />
<Route path="/" element={<Main />} />
<Route path="/res/:filename" element={<VideoPlayer />} />
</Routes>
</>
);
};
export default App;

56
src/AuthContext.jsx Normal file
View File

@ -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 (
<AuthContext.Provider value={{ username, setUsername, handleLogout }}>
{children}
</AuthContext.Provider>
);
};

View File

@ -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 (
<Container style={{ marginTop: 20 }}>
<CategoryNav
@ -114,12 +103,7 @@ const Main = () => {
<div>
{loading ? (
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '50vh',
}}
style={{display: 'flex',justifyContent: 'center',alignItems: 'center',height: '50vh'}}
>
<CircularProgress />
</div>
@ -127,12 +111,7 @@ const Main = () => {
<Grid container spacing={2} style={{ marginTop: 3 }}>
{pagination.movies.map((item) => (
<Grid
item
xs={6}
sm={4}
md={3}
lg={2}
<Grid item xs={6} sm={4} md={3} lg={2}
style={{ display: 'flex', justifyContent: 'space-between' }}
key={item.filename}
>

View File

@ -1 +0,0 @@

39
src/components/Bar.jsx Normal file
View File

@ -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 (
<AppBar position="static">
<Toolbar>
<Box display="flex" alignItems="center" flexGrow={1}>
<IconButton edge="start" color="inherit" aria-label="menu">
<img src={VuetifyLogo} alt="Vuetify Logo" width="40" />
</IconButton>
</Box>
{username && (
<Box display="flex" alignItems="center" marginRight="1rem">
<Avatar>{username.charAt(0).toUpperCase()}</Avatar>
<Typography variant="subtitle1" style={{ marginLeft: '1rem' }}>
{username}
</Typography>
</Box>
)}
<IconButton edge="end" color="inherit" aria-label="logout" onClick={handleLogout}>
<ExitToAppIcon />
</IconButton>
</Toolbar>
</AppBar>
);
};
export default Bar;

View File

@ -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,

View File

@ -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';

View File

@ -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'));