day-088-eighty-eight-20230609-react项目的细节
react项目的细节
进入登录页的情况分析
- 什么情况下,会进入登录页?
-
手动输入
/login
地址http://127.0.0.1:3000/#/login
- 登录成功:跳转到首页
push()
- 登录成功:跳转到首页
-
我原本想进入的是 个人中心/我的收藏/修改个人信息页,但是经过登录态校验,发现我没有登录,则跳转到登录页
- 虽然没有渲染真正的个人中心等组件,但是路由匹配后,渲染了
Element
这个统一处理的组件 —> 历史记录已经有了 - 个人中心
/personal
replace
到登录页 - 跳转到登录页
/login?to=/personal
replace
到来源页- 登录成功{之前想去哪,我们应该让其直接跳转到哪}
/personal
- 登录成功{之前想去哪,我们应该让其直接跳转到哪}
- 虽然没有渲染真正的个人中心等组件,但是路由匹配后,渲染了
-
在新闻详情页,我们点击收藏按钮,但因为没有登录,需要跳转到登录页
/detail/xxx
replace
到登录页/login?to=/detail/xxx
replace
到来源页/detail/xxx
会记录下当前页。
-
点击返回组件的情况分析
- 返回组件:
- 点击返回时:
- 如果有to的查询字符串,就替换到to对应的页面。
- 如果没有to的查询字符串,就不替换。
- 所以才要把返回功能封装成一个组件。
- 样式是独立的,内部的组件可以使用。
- 点击返回时:
一个常规进入登录页的流程
- 直接输入登录页
- 手动输入登录页地址
- 进入个人中心但没登录
- 在非权限页点击需要登录才能进行的操作跳转到的登录页
- 点击退出登录页才进入的登录页(这个可能会有几个路由记录都是需要登录才能进的页面)
收藏功能
- 因为收藏功能是需要在应用整个周期中记录信息的,所以要放全局状态中管理。
- 因为刷新时要更新到最新的数据,同步来自其它终端的数据,放到本地存储时难以及时更新。
- 所以在redux仓库对应的store文件夹设置一个收藏相关的模块中。
- 实际上,仅仅收藏只是放在用户信息中。但这里为了演示另一个独立模块,所以单独把收藏的功能放在这。
- 功能有查询收藏列表、新增单个收藏、减少单个收藏
- 实际上,仅仅收藏只是放在用户信息中。但这里为了演示另一个独立模块,所以单独把收藏的功能放在这。
个人中心页
- fang/知乎日报/zhihu/src/views/Personal.jsx
// import { message } from "react";
import message from "@/components/message";
import styled from "styled-components";
import { Link } from "react-router-dom";
import { RightOutline } from "antd-mobile-icons";
import NavBarAgain from "@/components/NavBarAgain";
import { connect } from "react-redux";
import action from "@/store/actions";
import _ from "@/assets/utils";
/* 组件的样式 */
const PersonalStyle = styled.div`
.baseInfo {
box-sizing: border-box;
margin: 20px 0;
.pic {
display: block;
margin: 0 auto;
width: 86px;
height: 86px;
border-radius: 50%;
}
.name {
line-height: 50px;
font-size: 18px;
text-align: center;
color: #000;
}
}
.tab {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 15px;
height: 40px;
line-height: 40px;
font-size: 14px;
color: #000;
border-bottom: 1px solid #eee;
}
`;
const Personal = function Personal({
navigate,
profile,
removeLoginInfo,
clearStore,
}) {
return (
<PersonalStyle>
<NavBarAgain title="个人中心" />
<div className="baseInfo">
<Link to="/update">
<img className="pic" src={profile.pic} alt={profile.name} />
<p className="name">{profile.name}</p>
</Link>
</div>
<div>
<Link to="/mystore" className="tab">
我的收藏
<RightOutline />
</Link>
<div
className="tab"
onClick={() => {
removeLoginInfo();
clearStore();
_.storage.remove(`TK`);
message.success(`已安全退出`);
navigate(`/login?to=/personal&lx=singout`, { replace: true });
}}
>
退出登录
<RightOutline />
</div>
</div>
</PersonalStyle>
);
};
export default connect((state) => state.base, {
removeLoginInfo: action.base.removeLoginInfo,
clearStore: action.collect.clearStore,
})(Personal);
退出登录的返回上一级路由逻辑
-
fang/知乎日报/zhihu/src/components/NavBarAgain.jsx
import React from "react"; import { NavBar } from "antd-mobile"; import styled from "styled-components"; import { useNavigate, useSearchParams } from "react-router-dom"; /* 组件的样式 */ const NavBarAgainStyle = styled.div` .navbar-again-box { padding: 0 10px; height: 40px; .adm-nav-bar-title { font-size: 16px; } .adm-nav-bar-back-arrow { font-size: 18px; } } `; const NavBarAgain = function NavBarAgain({ title }) { const navigate = useNavigate(); const query = useSearchParams()[0]; const handle = () => { // console.log(`query`, query); let to = query.get("to"); let lx = query.get("lx"); if (/^\/detail\//.test(to)) { navigate(to, { replace: true }); return; } if (to === `/personal` && lx === `singout`) { //点击退出登录才进入的登录页。//因为退出登录之前,有可能进的还是需要权限校验的的地方,不会在退出登录时循环校验。 navigate(`/`, { replace: true }); return; } navigate(-1); }; return ( <NavBarAgainStyle> <NavBar className="navbar-again-box" onBack={handle}> {title} </NavBar> </NavBarAgainStyle> ); }; NavBarAgain.defaultProps = { title: "个人中心", }; export default NavBarAgain;
图片的上传及修改
- fang/知乎日报/zhihu/src/views/Update.jsx
// import { useState, message } from "react";
import { useState } from "react";
import message from "@/components/message";
import styled from "styled-components";
import { ImageUploader, Input, Button } from "antd-mobile";
import NavBarAgain from "@/components/NavBarAgain";
import { connect } from "react-redux";
import action from "@/store/actions";
import API from "@/api";
import ButtonAgain from "@/components/ButtonAgain";
/* 组件的样式 */
const UpdateStyle = styled.div`
.formBox {
padding: 15px;
.item {
display: flex;
align-items: center;
margin-bottom: 10px;
height: 55px;
line-height: 55px;
font-size: 14px;
&:nth-child(1) {
height: 80px;
line-height: 80px;
}
.label {
width: 20%;
text-align: center;
}
.input {
width: 80%;
}
}
.adm-space-item {
padding-bottom: 0;
}
}
.submit {
display: block;
margin: 0 auto;
width: 60%;
height: 35px;
font-size: 14px;
}
`;
const Update = function Update({ profile, queryLoginInfo, navigate }) {
// 图片上传的处理。
let [fileList, setFileList] = useState(() => {
return [
{
url: profile.pic,
},
];
});
// 清空已上传的图片。
const handleDelete = () => {
setFileList([]);
};
// 上传图片前:限制文件大小。
const beforeUpload = (file) => {
// console.log("beforeUpload", file);
let theMax = 1024 * 1024;
if (file.size > theMax) {
message.error(`上传图片过大,不能超过${theMax / 1024}kb`);
return null;
}
return file;
};
// 图片上传。
// const upload = async (file) => {
// // console.log("upload", file);
// let result = { url: `` };
// try {
// let { code, pic } = await API.upload(file);
// if(+code===0)
// if (+code !== 0) {
// message.error(`上传失败`);
// setFileList([]);
// } else {
// message.success(`上传成功`);
// setFileList([{ url: pic }]);
// result.url = pic;
// }
// } catch (_) {}
// return result;
// };
// 图片上传。
const upload = async (file) => {
try {
let { code, pic } = await API.upload(file);
if (+code === 0) {
message.success(`上传成功`);
setFileList([{ url: pic }]);
return { url: pic };
}
message.success(`上传失败`);
} catch (_) {}
setFileList([]);
return Promise.reject(`失败`);
};
// 表单处理。
let [name, setName] = useState(profile.name);
const submit = async () => {
let username = name.trim();
let pic = ``;
if (Array.isArray(fileList) && fileList.length > 0) {
pic = fileList[0].url;
}
//表单校验
// console.log(username ,pic);
if (!username || !pic) {
message.error(`姓名与头像不能为空`);
return;
}
// 发送请求
try {
let { code } = await API.updateUserInfo(username, pic);
if (+code !== 0) {
message.error(`修改失败`);
return;
}
await queryLoginInfo();
message.success(`修改成功`);
navigate(-1);
} catch (error) {}
};
return (
<UpdateStyle>
<NavBarAgain title="修改信息" />
<div className="formBox">
<div className="item">
<div className="label">头像</div>
<div className="input">
<ImageUploader
maxCount={1}
value={fileList}
onDelete={handleDelete}
beforeUpload={beforeUpload}
upload={upload}
showFailed={false}
/>
</div>
</div>
<div className="item">
<div className="label">姓名</div>
<div className="input">
<Input
placeholder="请输入账号名称"
value={name}
onChange={(value) => {
setName(value);
}}
/>
</div>
</div>
<ButtonAgain color="primary" className="submit" onClick={submit}>
提交
</ButtonAgain>
</div>
{/* 当前图片状态值:{JSON.stringify(fileList)} */}
</UpdateStyle>
);
};
export default connect((state) => state.base, action.base)(Update);