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





目录
相关文章
|
13天前
|
前端开发 JavaScript 安全
HTML+CSS+JS密码灯登录表单
通过结合使用HTML、CSS和JavaScript,我们创建了一个带有密码强度指示器的登录表单。这不仅提高了用户体验,还帮助用户创建更安全的密码。希望本文的详细介绍和代码示例能帮助您在实际项目中实现类似功能,提升网站的安全性和用户友好性。
26 3
|
24天前
|
监控 前端开发 JavaScript
React 静态网站生成工具 Next.js 入门指南
【10月更文挑战第20天】Next.js 是一个基于 React 的服务器端渲染框架,由 Vercel 开发。本文从基础概念出发,逐步探讨 Next.js 的常见问题、易错点及解决方法,并通过具体代码示例进行说明,帮助开发者快速构建高性能的 Web 应用。
60 10
|
22天前
|
资源调度 前端开发 数据可视化
构建高效的数据可视化仪表板:D3.js与React的融合之道
【10月更文挑战第25天】在数据驱动的时代,将复杂的数据集转换为直观、互动式的可视化表示已成为一项至关重要的技能。本文深入探讨了如何结合D3.js的强大可视化功能和React框架的响应式特性来构建高效、动态的数据可视化仪表板。文章首先介绍了D3.js和React的基础知识,然后通过一个实际的项目案例,详细阐述了如何将两者结合使用,并提供了实用的代码示例。无论你是数据科学家、前端开发者还是可视化爱好者,这篇文章都将为你提供宝贵的洞见和实用技能。
46 5
|
1月前
|
人工智能 JavaScript 网络安全
ToB项目身份认证AD集成(三完):利用ldap.js实现与windows AD对接实现用户搜索、认证、密码修改等功能 - 以及针对中文转义问题的补丁方法
本文详细介绍了如何使用 `ldapjs` 库在 Node.js 中实现与 Windows AD 的交互,包括用户搜索、身份验证、密码修改和重置等功能。通过创建 `LdapService` 类,提供了与 AD 服务器通信的完整解决方案,同时解决了中文字段在 LDAP 操作中被转义的问题。
|
1月前
|
开发框架 前端开发 JavaScript
React、Vue.js 和 Angular主流前端框架和选择指南
在当今的前端开发领域,选择合适的框架对于项目的成功至关重要。本文将介绍几个主流的前端框架——React、Vue.js 和 Angular,探讨它们各自的特点、开发场景、优缺点,并提供选择框架的建议。
43 6
|
1月前
|
JavaScript
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单(二)
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单(一)
34 0
|
1月前
|
存储 JSON JavaScript
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单(一)
Node.js单点登录SSO详解:Session、JWT、CORS让登录更简单(一)
89 0
|
2月前
|
前端开发 JavaScript API
React、Vue.js 和 Angular前端三大框架对比与选择
前端框架是用于构建用户界面的工具和库,它提供组件化结构、数据绑定、路由管理和状态管理等功能,帮助开发者高效地创建和维护 web 应用的前端部分。常见的前端框架如 React、Vue.js 和 Angular,能够提高开发效率并促进团队协作。
118 4
|
1月前
|
前端开发 JavaScript 安全
前端JS实现密码校验键盘横竖、26字母、相同字母、相同数字、密码包含用户名、数字 字母不能连续 不能相同三个、不能横向 竖向 连续三个 包含字符、不能有中文符号
该 JavaScript 代码实现了一个严格的密码校验功能,确保密码满足多种安全要求,包括长度、字符类型、不包含中文及特殊字符、不与用户名相似等。通过多个辅助函数,如 `validateFormat` 检查密码格式,`isHasChinaCharFun` 检测中文符号,`getCharAll` 生成键盘组合,以及 `checkPasswordFun` 综合验证密码的有效性和安全性。此工具对于提高用户账户的安全性非常有用。
33 0
|
2月前
|
前端开发
React页面跳转取消上一个页面的所有请求
React页面跳转时取消上一个页面的所有axios请求,通过axios拦截器设置cancelToken,并在页面跳转时调用cancel函数取消未完成的请求。
36 2
下一篇
无影云桌面