根组件index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import {RouterProvider} from "react-router-dom"
import router from "./router"
import { Provider } from "react-redux"
import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<RouterProvider router={router}></RouterProvider>
</Provider>
);
App.js 路由切换动画
app.js
import './App.scss';
import React from 'react';
import {Outlet,useOutlet,useLocation} from "react-router-dom"
import {CSSTransition,SwitchTransition} from "react-transition-group"
function App() {
const currentOutlet = useOutlet();
const location = useLocation()
return (
<div className="App">
{/* <Outlet></Outlet> */}
<SwitchTransition>
<CSSTransition key={location.pathname} classNames="fade" timeout={ 1000 }>
{
()=>(
<div className="fade">{currentOutlet}</div>
)
}
</CSSTransition>
</SwitchTransition>
</div>
);
}
export default App;
App.scss
#root,
body,
html {
width: 100%;
height: 100%;
}
.App {
width: 100%;
height: 100%;
}
a {
margin: 0 10px;
text-decoration: none;
color: #333;
}
.active {
color: #c00;
}
.fade-enter {
/* opacity: 0; */
/* transform: scale(1.1); */
transform: translateX(100%);
}
.fade-enter-active {
/* opacity: 1; */
/* transform: scale(1);
transition: opacity 300ms, transform 300ms; */
transform: translateX(0);
transition: .3s;
}
.fade-exit {
/* opacity: 1; */
/* transform: scale(1); */
transform: translateX(0);
}
.fade-exit-active {
/* opacity: 0; */
/* transform: scale(0.9);
transition: opacity 300ms, transform 300ms; */
transform: translateX(100%);
transition: .3s;
}
redux
store index.js
import { configureStore } from "@reduxjs/toolkit"
import book from "./module/book"
const store=configureStore({
reducer:book.reducer
})
export default store
book.js
import { createSlice } from "@reduxjs/toolkit"
const book=createSlice({
name:"book",
initialState:{
book:[]
},
reducers:{
addMyBook(state,{payload}){
let idx=state.book.findIndex(item=>item.name===payload)
if(idx===-1){
state.book=[...state.book,{id:Date.now(),name:payload,isComplete:false}]
}
},
comChange(state,{payload}){
let idx=state.book.findIndex(item=>item.id===payload)
state.book[idx].isComplete=!state.book[idx].isComplete
}
}
})
export const {addMyBook,comChange}=book.actions
export default book
router V6
index.js 拦截
import { createBrowserRouter,Navigate } from "react-router-dom"
import Home from "../component/Home"
import MyBook from "../component/MyBook"
import App from "../App"
import Login from "../component/Login"
// 封装一个高阶组件(其实就是函数,这个函数要接收一个组件作为参数,返回一个组件)
const AuthComponent = props => {
// 获取到当前组件
const Com = props.children.type;
// 判断token是否存在
if(sessionStorage.token) {
return <Com />
} else {
return <Navigate to="/login" />
}
}
const router=createBrowserRouter([
{
path:"/",
element:<App/>,
children:[
{
path:"/login",
element:<Login/>
},
{
path:"/home",
element:<AuthComponent><Home/></AuthComponent>
},
{
path:"/mybook",
element:<AuthComponent><MyBook/></AuthComponent>
},
{
path:"/",
element:<Navigate to="/login"></Navigate>
}
]
}
])
export default router
组件 component
login.js
import React, { useState } from 'react';
import {useNavigate} from "react-router-dom"
const YourComponent = () => {
const [phoneNumber, setPhoneNumber] = useState('');
const [password, setPassword] = useState('');
const [phoneNumberValid, setPhoneNumberValid] = useState(true);
const [passwordValid, setPasswordValid] = useState(true);
// 验证手机号码格式和位数
const validatePhoneNumber = (value) => {
const regex = /^1[3456789]\d{9}$/;
return regex.test(value);
};
// 验证密码格式
const validatePassword = (value) => {
const regex = /^[A-Za-z][A-Za-z0-9]{5}$/;
return regex.test(value);
};
// 处理手机号码输入变化
const handlePhoneNumberChange = (e) => {
const value = e.target.value;
setPhoneNumber(value);
setPhoneNumberValid(validatePhoneNumber(value));
};
// 处理密码输入变化
const handlePasswordChange = (e) => {
const value = e.target.value;
setPassword(value);
setPasswordValid(validatePassword(value));
};
// 渲染错误或正确图标
const renderIcon = (valid) => {
if (valid) {
return <i className="icon-correct" />;
} else {
return <i className="icon-error" />;
}
};
const navigate=useNavigate()
const login=()=>{
sessionStorage.setItem("token","123")
navigate("/home")
}
return (
<div>
<label>手机号码:</label>
<input
type="text"
value={phoneNumber}
onChange={handlePhoneNumberChange}
/>
{renderIcon(phoneNumberValid)}
{!phoneNumberValid && <p>手机号码格式不正确</p>}
<br />
<label>密码:</label>
<input
type="password"
value={password}
onChange={handlePasswordChange}
/>
{renderIcon(passwordValid)}
{!passwordValid && <p>密码格式不正确</p>}
<div>
<button onClick={login}>登录</button>
</div>
</div>
);
};
export default YourComponent;
home.js
import React ,{ useState } from 'react'
import { useEffect } from 'react'
import "./index.scss"
import {useDispatch,useSelector} from "react-redux"
import {useNavigate} from "react-router-dom"
import { addMyBook } from '../../store/module/book'
export default function Index() {
const navigate=useNavigate()
const dispatch=useDispatch()
const [val,setVal]=useState("")
const [list,setList]=useState([])
const [book,setBook]=useState([
{id:1,name:"红楼梦",img:"/img/1.jpg"},
{id:2,name:"西游记",img:"/img/2.jpg"},
{id:3,name:"水浒传",img:"/img/3.jpg"},
{id:4,name:"三国演义",img:"/img/4.jpg"},
{id:5,name:"笑面人",img:"/img/5.jpg"},
{id:6,name:"拿破仑",img:"/img/6.jpg"},
{id:7,name:"郭论",img:"/img/7.jpg"},
{id:8,name:"呼啸山庄",img:"/img/8.jpg"},
])
const getList=()=>{
let arr=[...book]
setList(arr)
}
useEffect(()=>{
getList()
},[])
const keydown=(ev)=>{
if(ev.keyCode===13){
let arr= book.filter(item=>item.name.indexOf(val)>-1)
console.log(arr);
setList(arr)
setVal("")
}
}
const quxiao=()=>{
let arr=[...book]
setList(arr)
}
return (
<div className='home'>
<div className="header">
<h3>书城</h3>
<span onClick={()=>navigate("/mybook")}>我的书架</span>
</div>
<div className="search">
<input type="text" value={val} onChange={(ev)=>setVal(ev.target.value)} onKeyDown={(ev)=>keydown(ev)}/>
<button onClick={quxiao}>取消</button>
</div>
<div className="content">
{
list.map((item,index)=>(
<div className="book" key={item.id}>
<div className="img">
<img src={item.img} alt="" />
</div>
<div className="n">
<span>{item.name}</span>
<button onClick={()=>dispatch(addMyBook(item.name))}>+</button>
</div>
</div>
))
}
</div>
</div>
)
}
mybook.js
import React,{ useState } from 'react'
import {useDispatch,useSelector} from "react-redux"
import {useNavigate} from "react-router-dom"
import "./index.scss"
import {DownOutlined,UpOutlined} from "@ant-design/icons"
import { comChange } from '../../store/module/book'
import { useEffect } from 'react'
export default function Index() {
const [isShow,setIsShow]=useState(false)
const [isShow2,setIsShow2]=useState(false)
const dispatch=useDispatch()
const navigate=useNavigate()
const book=useSelector(store=>store.book)
const com=()=>{
setIsShow2(!isShow2)
}
const uncom=()=>{
setIsShow(!isShow)
}
// useEffect(()=>{
// const book=useSelector(store=>store.book)
// },[])
return (
<div className='mybook'>
<div className="header">
<h3>我的书架</h3>
<span onClick={()=>navigate("/home")}>返回书城</span>
</div>
<div className="uncom">
<div className="uncom_head">
<span>未完成</span>
{!isShow?<DownOutlined onClick={uncom}/>:<UpOutlined onClick={uncom} />}
</div>
{
isShow?
book.filter(item=>!item.isComplete).map(item=>(
<div key={item.id}>
<span>{item.name}</span>
<button onClick={()=>dispatch(comChange(item.id))}>已看完</button>
</div>
)):
<></>
}
</div>
<div className="com">
<div className="com_head">
<span>已完成</span>
{!isShow2?<DownOutlined onClick={com}/>:<UpOutlined onClick={com} />}
</div>
{
isShow2?
book.filter(item=>item.isComplete).map(item=>(
<div>
<span>{item.name}</span>
<button onClick={()=>dispatch(comChange(item.id))}>再看一遍</button>
</div>
)):
<></>
}
</div>
</div>
)
}
344






