react + zarm + rc-form + crypto-js 实现个人中心页面,头像上传,密码重置,登录退出功能

简介: react + zarm + rc-form + crypto-js 实现个人中心页面,头像上传,密码重置,登录退出功能

需要实现的大致效果


这里大致需要实现4个页面,一个个人中心的主页面,另外还有3个子页面,用户信息修改页,重置密码页,关于我们页。这里关于我们页,就不写了。重点实现一下用户信息修改以及重置密码页面功能。下面样式部分的代码就不粘贴了,有需要的参考我这篇,里面有我学习的项目地址:


https://kaimo313.blog.csdn.net/article/details/122586455



d7d226ce87b04e6f85e62dbb9f671223.png



实现过程


1.联调问题以及路由添加


一个问题是上传的时候有问题,该问题由Lweilve提出https://blog.csdn.net/kaimo313/article/details/122689960#comments_20557507


2148eb6dc6b143f897ec556d68d0391f.png


所以首先需要改动一下后端服务的问题:

51026feab1a1426297ac6741403f541c.png


第二个问题就是获取头像的时候,获取参数有点问题:应该改成query去获取,这个是get请求。

8f2e78fbbf8444a09b9bac9426c94f98.png


路由添加:在 router.js 文件里添加路由

import Login from '@/container/Login'
import Home from '@/container/Home'
import Data from '@/container/Data'
import User from '@/container/User'
import Detail from '@/container/Detail'
import UserInfo from '@/container/UserInfo'
import Account from '@/container/Account'
const routes = [
  {
    path: "/login",
    component: Login
  },{
    path: "/",
    component: Home
  },{
    path: "/data",
    component: Data
  },{
    path: "/user",
    component: User
  },{
    path: "/userInfo",
    component: UserInfo
  },{
    path: "/detail",
    component: Detail
  },{
    path: "/account",
    component: Account
  }
];
export default routes


2.实现个人中心页面:退出登录

实现的效果如下:


d60e3a79c33c4edaa798e1a29dfd80ae.png


我们在按照下面的结构新建文件,分别添加代码:


48212f67db9c486798730e0a4167fdf4.png

import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Cell, Button, Toast } from 'zarm';
import { userInfo, getAvatar } from './api/index.js';
// 引入图片
import geqian from "@/assets/images/geqian.png";
import gxqm from "@/assets/images/gxqm.png";
import zhaq from "@/assets/images/zhaq.png";
import lianxi from "@/assets/images/lianxi.png";
import defaultAvatar from "@/assets/images/default.jpg";
import s from './style.module.less';
const User = () => {
  const navigate = useNavigate();
  const [avatarUrl, setAvatarUrl] = useState(defaultAvatar); // 头像显示路径
  const [user, setUser] = useState({});
  useEffect(() => {
    getUserInfo();
  }, []);
  // 获取用户信息
  const getUserInfo = async () => {
    const { status, desc, data } = await userInfo({});
    if(status === 200) {
      setUser(data);
      // 获取头像
      if(data.avatar) {
        const res = await getAvatar({picname: data.avatar});
        console.log(res);
        if(res.status === 200) {
          setAvatarUrl(res.data);
        }else{
          Toast.show(res.desc);
        }
      }
    }else{
      Toast.show(desc);
    }
  };
  // 退出登录
  const logout = async () => {
    localStorage.removeItem('token');
    navigate('/login');
  };
  return <div className={s.user}>
    <div className={s.head}>
      <div className={s.info}>
        <span>昵称:{user.username || '--'}</span>
        <span>
          <img style={{ width: 30, height: 30, verticalAlign: '-10px' }} src={geqian} alt="" />
          <b>{user.signature || '这个人很懒,暂无个签'}</b>
        </span>
      </div>
      <img className={s.avatar} style={{ width: 60, height: 60, borderRadius: 8 }} src={avatarUrl} alt="" />
   </div>
   <div className={s.content}>
    <Cell
      hasArrow
      title="用户信息修改"
      onClick={() => navigate('/userinfo')}
      icon={<img style={{ width: 20, verticalAlign: '-7px' }} src={gxqm} alt="" />}
    />
    <Cell
      hasArrow
      title="重制密码"
      onClick={() => navigate('/account')}
      icon={<img style={{ width: 20, verticalAlign: '-7px' }} src={zhaq} alt="" />}
    />
    <Cell
      hasArrow
      title="关于我们"
      onClick={() => navigate('/about')}
      icon={<img style={{ width: 20, verticalAlign: '-7px' }} src={lianxi} alt="" />}
    />
   </div>
   <Button className={s.logout} block theme="danger" onClick={logout}>退出登录</Button>
  </div>
}
export default User


import { fetchData } from "@/utils/axios.js";
// 获取用户信息
export function userInfo(data) {
  return fetchData('/api/user/getUserInfo', 'get', data);
}
// 获取头像
export function getAvatar(data) {
  return fetchData('/api/upload/getAvatar', 'get', data);
}


3.实现用户信息修改页:上传头像

这里需要注意的是需要用表单方式上传, 通过 axios 传 forms 设置 'Content-Type': 'multipart/form-data', 进行文件上传

if (ajaxType === 'get') {
  config.url = url + '?r=' + Math.random() * 1000;
  config.params = data;
} else if (ajaxType === 'post') {
  if(data.forms) {
    config.data = data.forms;
    config.headers['Content-Type'] = 'multipart/form-data;charset=UTF-8';
  }else{
    config.data = data;
    config.headers['Content-Type'] = 'application/json;charset=UTF-8';
  }
}



我们在按照下面的结构新建文件,分别添加代码:

0fca39d2e3fe40dd80d4f64b3e2c684d.png

import React, { useEffect, useState } from 'react';
import { Button, FilePicker, Input, Toast } from 'zarm';
import { useNavigate } from 'react-router-dom';
import Header from '@/components/Header';
import { getAvatar, userInfo, uploadAvatar, updateUserInfo } from './api/index.js';
import defaultAvatar from "@/assets/images/default.jpg";
import s from './style.module.less';
const UserInfo = () => {
  const navigate = useNavigate();; // 路由实例
  const [avatarUrl, setAvatarUrl] = useState(defaultAvatar); // 头像显示路径
  const [avatar, setAvatar] = useState(''); // 头像
  const [signature, setSignature] = useState(''); // 个签
  useEffect(() => {
    getUserInfo(); // 初始化请求
  }, []);
  useEffect(async () => {
    if(avatar) {
      // 获取头像
      const { status, data, desc } = await getAvatar({picname: avatar});
      if(status === 200) {
        setAvatarUrl(data);
      }else{
        Toast.show(desc);
      }
    }
  }, [avatar]);
  // 获取用户信息
  const getUserInfo = async () => {
    const { status, desc, data } = await userInfo({});
    if(status === 200) {
      setAvatar(data.avatar)
      setSignature(data.signature)
    }else{
      Toast.show(desc);
    }
  };
  // 获取图片回调 
  const handleSelect = async (file) => {
    console.log('file.file', file.file)
    if (file && file.file.size > 200 * 1024) {
      Toast.show('上传头像不得超过 200 KB!')
      return
    }
    // 生成 form-data 数据类型
    let formData = new FormData()
    formData.append('file', file.file)
    // 通过 axios 传 forms 设置  'Content-Type': 'multipart/form-data', 进行文件上传
    const { status, desc, data } = await uploadAvatar({
      forms: formData
    });
    // 返回图片地址
    if(status === 200) {
      setAvatar(data)
    }else{
      Toast.show(desc);
    }
  }
  // 编辑用户信息方法
  const save = async () => {
    if (signature && signature.length > 36) {
      Toast.show('个性签名不能超过36个字!')
      return
    }
    const { status, desc } = await updateUserInfo({
      signature,
      avatar
    });
    if(status === 200) {
      Toast.show('修改成功')
      // 成功后回到个人中心页面
      navigate(-1)
    }else{
      Toast.show(desc);
    }
  }
  return <>
    <Header title='用户信息修改' />
    <div className={s.userinfo}>
      <div className={s.item}>
        <div className={s.title}>头像</div>
        <div className={s.avatar}>
          <img className={s.avatarUrl} src={avatarUrl} alt=""/>
          <div className={s.desc}>
            <span>支持 jpg、png、jpeg 格式大小 200KB 以内的图片</span>
            <FilePicker className={s.filePicker} onChange={handleSelect} accept="image/*">
              <Button className={s.upload} theme='primary' size='xs'>点击上传</Button>
            </FilePicker>
          </div>
        </div>
      </div>
      <div className={s.item}>
        <div className={s.title}>个性签名</div>
        <div className={s.signature}>
          <Input
            clearable
            type="text"
            value={signature}
            placeholder="个性签名不能超过36个字"
            onChange={(value) => setSignature(value)}
          />
        </div>
      </div>
      <Button onClick={save} style={{ marginTop: 50 }} block theme='primary'>保存</Button>
    </div>
  </>
};
export default UserInfo;


import { fetchData } from "@/utils/axios.js";
// 获取头像
export function getAvatar(data) {
  return fetchData('/api/upload/getAvatar', 'get', data);
}
// 获取用户信息
export function userInfo(data) {
  return fetchData('/api/user/getUserInfo', 'get', data);
}
// 更新用户信息
export function updateUserInfo(data) {
  return fetchData('/api/user/updateUserInfo', 'post', data);
}
// 上传头像
export function uploadAvatar(data) {
  return fetchData('/api/upload/avatar', 'post', data);
}

写完测试一下:点击用户信息修改进入

c0007cd0c1574cde9ecaabf148945c6f.png



我们可以通过下面的地方修改信息:



70865abb22c54c4b9731d8cdb1089d7b.png


比如修改头像:

b6a4f6aa17884b08ab5f57330868b634.png


我们发现就上传成功了

374e448cc79c4d75ae016efa429c01d8.png


我们可以查看本地的盘里是有该图片的:


57fff36d07404da8a6997c5bf15c20e8.png


修改个签:


ec9e465757704ef8b46af3b91879aefb.png


成功之后就会跳转到个人中心。


ae02a13da52845919a9b61c71135d1d1.png



4.安装依赖 rc-form、crypto-js

npm i rc-form -S

9c70a8daca95442e81fd680f942bedb0.png


用法:参考https://github.com/react-component/form

import { createForm, formShape } from 'rc-form';
class Form extends React.Component {
  static propTypes = {
    form: formShape,
  };
  submit = () => {
    this.props.form.validateFields((error, value) => {
      console.log(error, value);
    });
  }
  render() {
    let errors;
    const { getFieldProps, getFieldError } = this.props.form;
    return (
      <div>
        <input {...getFieldProps('normal')}/>
        <input {...getFieldProps('required', {
          onChange(){}, // have to write original onChange here if you need
          rules: [{required: true}],
        })}/>
        {(errors = getFieldError('required')) ? errors.join(',') : null}
        <button onClick={this.submit}>submit</button>
      </div>
    );
  }
}
export createForm()(Form);


0d6cbfb32f56410581b88e6f08b6069f.png


安装 crypto-js,用法请参考:crypto-js:加密标准的JavaScript库

npm install crypto-js -S


c9451d7e0e704082a88b1f20218b02a5.png


5.实现重置密码页

在下面文件里添加相应的代码:

22c5d6b896d94addbba2f9a31e5e1df6.png


import React from 'react';
import { Cell, Input, Button, Toast } from 'zarm';
import { useNavigate } from 'react-router-dom';
import { createForm  } from 'rc-form';
import Header from '@/components/Header'
import { resetPassword } from './api/index'
import CryptoJS from "crypto-js";
import s from './style.module.less'
const Account = (props) => {
  const navigate = useNavigate();
  // Account 通过 createForm 高阶组件包裹之后,可以在 props 中获取到 form 属性
  console.log('Account 通过 createForm 高阶组件包裹之后', props)
  const { getFieldProps, validateFields } = props.form;
  // DES 加密
  const DES_encrypt = (hashStr) => {
    return CryptoJS.DES.encrypt(
      hashStr,
      CryptoJS.enc.Utf8.parse("ABF"),// keyHex
      { 
        mode: CryptoJS.mode.ECB, 
        padding: CryptoJS.pad.Pkcs7 
      } // option
    ).ciphertext.toString();
  }
  // 提交修改方法
  const submit = () => {
    // validateFields 获取表单属性元素
    validateFields(async (error, value) => {
      // error 表单验证全部通过,为 false,否则为 true
      if (!error) {
        console.log(value)
        if (value.newPassword != value.newPassword2) {
          Toast.show('新密码输入不一致');
          return
        }
        const {status, desc} = await resetPassword({
          oldPassword: DES_encrypt(value.oldPassword),
          newPassword: DES_encrypt(value.newPassword)
        })
        if(status === 200) {
          Toast.show('修改成功');
          // 退出登录跳转登录页
          localStorage.removeItem("token");
          navigate('/login')
        }else{
          Toast.show(desc)
        }
      }else{
        Toast.show('参数校验不通过,请填写完整!')
      }
    });
  }
  return <>
    <Header title="重制密码" />
    <div className={s.account}>
      <div className={s.form}>
        <Cell title="原密码">
          <Input
            clearable
            type="text"
            placeholder="请输入原密码"
            {...getFieldProps('oldPassword', { rules: [{ required: true }] })}
          />
        </Cell>
        <Cell title="新密码">
          <Input
            clearable
            type="text"
            placeholder="请输入新密码"
            {...getFieldProps('newPassword', { rules: [{ required: true }] })}
          />
        </Cell>
        <Cell title="确认密码">
          <Input
            clearable
            type="text"
            placeholder="请确认新密码"
            {...getFieldProps('newPassword2', { rules: [{ required: true }] })}
          />
        </Cell>
      </div>
      <Button className={s.btn} block theme="primary" onClick={submit}>提交</Button>
    </div>
  </>
};
export default createForm()(Account);


import { fetchData } from "@/utils/axios.js";
// 重置密码
export function resetPassword(data) {
  return fetchData('/api/user/resetPassword', 'post', data);
}

测试一下效果:点击重置密码进去

ff3d67493c98453887d9f233269fb27c.png


填写好修改的信息


56ac72963aea4dea915b8823f046cf11.png


提交成功之后就会回到首页

f21d6502f9804da9983bb0926068d726.png


输入之前的密码

f6d5891e18c9467c8965136f2164d055.png


就会提示密码错误

92c6230becab41c1aaa4ab1d65f0ca13.png



改成新的密码de97abe2b80a4adfbd0358315dd4ab19.png



发现可以登录成功了

f08d9d38e94946da9439f3bf0274af47.png





目录
相关文章
|
10天前
|
JavaScript 容器
带方向感知功能的js图片遮罩层插件
带方向感知功能的js图片遮罩层插件
|
1月前
|
前端开发 JavaScript 测试技术
React 中集成 Chart.js 图表库
本文介绍了如何在 React 项目中集成 Chart.js 创建动态图表,涵盖基础概念、安装步骤、代码示例及常见问题解决方法,帮助开发者轻松实现数据可视化。
45 11
|
1月前
|
JavaScript 前端开发 容器
jQuery多功能滑块插件r-slider.js
r-slider.js是一款jQuery多功能滑块插件。使用该插件,可以制作出滑块、开关按钮、进度条、向导步骤等多种效果。
38 5
|
1月前
|
前端开发 JavaScript 安全
HTML+CSS+JS密码灯登录表单
通过结合使用HTML、CSS和JavaScript,我们创建了一个带有密码强度指示器的登录表单。这不仅提高了用户体验,还帮助用户创建更安全的密码。希望本文的详细介绍和代码示例能帮助您在实际项目中实现类似功能,提升网站的安全性和用户友好性。
48 3
|
1月前
|
JavaScript
js实现简洁实用的网页计算器功能源码
这是一款使用js实现简洁实用的网页计算器功能源码。可实现比较基本的加减乘除四则运算功能,界面简洁实用,是一款比较基本的js运算功能源码。该源码可兼容目前最新的各类主流浏览器。
27 2
|
1月前
|
资源调度 前端开发 数据可视化
构建高效的数据可视化仪表板:D3.js与React的融合之道
【10月更文挑战第25天】在数据驱动的时代,将复杂的数据集转换为直观、互动式的可视化表示已成为一项至关重要的技能。本文深入探讨了如何结合D3.js的强大可视化功能和React框架的响应式特性来构建高效、动态的数据可视化仪表板。文章首先介绍了D3.js和React的基础知识,然后通过一个实际的项目案例,详细阐述了如何将两者结合使用,并提供了实用的代码示例。无论你是数据科学家、前端开发者还是可视化爱好者,这篇文章都将为你提供宝贵的洞见和实用技能。
70 5
|
1月前
|
JavaScript 前端开发 开发者
JavaScript框架React vs. Vue:一场性能与易用性的较量
JavaScript框架React vs. Vue:一场性能与易用性的较量
39 0
|
7月前
|
设计模式 前端开发 数据可视化
【第4期】一文了解React UI 组件库
【第4期】一文了解React UI 组件库
389 0
|
7月前
|
存储 前端开发 JavaScript
【第34期】一文学会React组件传值
【第34期】一文学会React组件传值
80 0
|
7月前
|
资源调度 前端开发 JavaScript
React 的antd-mobile 组件库,嵌套路由
React 的antd-mobile 组件库,嵌套路由
132 0