20230609----重返学习-react项目的细节

day-088-eighty-eight-20230609-react项目的细节

react项目的细节

进入登录页的情况分析

  • 什么情况下,会进入登录页?
    1. 手动输入 /login 地址http://127.0.0.1:3000/#/login

      • 登录成功:跳转到首页push()
    2. 我原本想进入的是 个人中心/我的收藏/修改个人信息页,但是经过登录态校验,发现我没有登录,则跳转到登录页

      • 虽然没有渲染真正的个人中心等组件,但是路由匹配后,渲染了Element这个统一处理的组件 —> 历史记录已经有了
      • 个人中心 /personal replace到登录页
      • 跳转到登录页 /login?to=/personal replace到来源页
        • 登录成功{之前想去哪,我们应该让其直接跳转到哪} /personal
    3. 在新闻详情页,我们点击收藏按钮,但因为没有登录,需要跳转到登录页

      • /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);

进阶参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值