需要实现的大致效果
这里大致需要实现4个页面,一个个人中心的主页面,另外还有3个子页面,用户信息修改页,重置密码页,关于我们页。这里关于我们页,就不写了。重点实现一下用户信息修改以及重置密码页面功能。下面样式部分的代码就不粘贴了,有需要的参考我这篇,里面有我学习的项目地址:
https://kaimo313.blog.csdn.net/article/details/122586455
实现过程
1.联调问题以及路由添加
一个问题是上传的时候有问题,该问题由Lweilve提出https://blog.csdn.net/kaimo313/article/details/122689960#comments_20557507
所以首先需要改动一下后端服务的问题:
第二个问题就是获取头像的时候,获取参数有点问题:应该改成query去获取,这个是get请求。
路由添加:在 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.实现个人中心页面:退出登录
实现的效果如下:
我们在按照下面的结构新建文件,分别添加代码:
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'; } }
我们在按照下面的结构新建文件,分别添加代码:
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); }
写完测试一下:点击用户信息修改进入
我们可以通过下面的地方修改信息:
比如修改头像:
我们发现就上传成功了
我们可以查看本地的盘里是有该图片的:
修改个签:
成功之后就会跳转到个人中心。
4.安装依赖 rc-form、crypto-js
npm i rc-form -S
用法:参考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);
安装 crypto-js,用法请参考:crypto-js:加密标准的JavaScript库
npm install crypto-js -S
5.实现重置密码页
在下面文件里添加相应的代码:
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); }
测试一下效果:点击重置密码进去
填写好修改的信息
提交成功之后就会回到首页
输入之前的密码
就会提示密码错误
改成新的密码
发现可以登录成功了