day-088-eighty-eight-20230608-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><divclassName="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"><ImageUploadermaxCount={1}value={fileList}onDelete={handleDelete}beforeUpload={beforeUpload}upload={upload}showFailed={false}/></div></div><div className="item"><div className="label">姓名</div><div className="input"><Inputplaceholder="请输入账号名称"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);