实习小记(个人中心的编辑模块)

项目需要加一个个人中心的编辑模块,也是差不多搞了一天下来,其中遇到了很多问题,也是来记录、分享一下。

技术栈:React、antd、TypeScript

在这里插入图片描述

需求
点击编辑,弹出编辑个人信息浮窗,调用后端接口渲染初始表单。其中头像是点击上传新头像,手机号是不可修改。

代码

import {Form,Modal,FormProps,Input,message,Avatar,Upload,Typography
} from 'antd'
import React, { useImperativeHandle, useState } from 'react'
import { UserOutlined } from '@ant-design/icons'
import { Users } from '@ysx-use/certificate-service'
import { useStateAsync } from '@ysx-use/hooks'
import { noop } from '@ysx-use/shared'
import { eventBus } from '@/components'
import { personalStore, logout } from '@/store'const { Text } = Typographyexport namespace PersonalModule {export interface FormModalInstance {show: (id?: number) => void}export interface FormModalProps {onSuccess?: () => void}export const FormModal = React.memo(React.forwardRef<FormModalInstance, FormModalProps>(({ onSuccess = noop }, ref) => {const [isModalOpen, setIsModalOpen] = useState(false)const [id, setId] = useState<number>()const [avatarUrl, setAvatarUrl] = useState<string>('') // 显示头像const [form] = Form.useForm<Users.Model>()useImperativeHandle(ref, () => ({show(id) {setId(id)form.resetFields()if (id) {Users.getMeInfo().then((res) => {const data = res.data || {}form.setFieldsValue({...data,password: undefined // 密码不显示})setAvatarUrl(data.avatar || '')})}setIsModalOpen(true)}}))const handleCancel = () => {setIsModalOpen(false)}const submit = useStateAsync(async (form: Users.Model) => {await Users.updatePersonalInfo({...form,password: form.password || undefined})personalStore.remove()message.success('修改成功,请重新登录')eventBus.emit('PersonalReload', id)logout()},{ immediate: false })const onFinish: FormProps<Users.Model>['onFinish'] = (values) => {submit.execute(values)}const onFinishFailed: FormProps<Users.Model>['onFinishFailed'] = (errorInfo) => {console.error('Failed:', errorInfo)}const onSubmit = () => {form.submit()}// 上传头像逻辑const importAvatar = useStateAsync(async (file: File) => {const res = await Users.importPersonalAvatar(file)const url = res?.data || ''if (url) {setAvatarUrl(url)form.setFieldsValue({ avatar: url })message.success('头像上传成功')} else {message.error('上传失败,请重试')}},{immediate: false})return (<Modaltitle="编辑个人信息"open={isModalOpen}onCancel={handleCancel}onOk={onSubmit}closable><Formstyle={{ marginTop: 16 }}form={form}layout="vertical"name="personal-form"onFinish={onFinish}onFinishFailed={onFinishFailed}autoComplete="off"><Form.Item label="头像" name="avatar"><div style={{ display: 'flex', alignItems: 'center', gap: 16 }}><Uploadaccept=""customRequest={() => {}}//beforeUpload={() => false} // 阻止默认上传showUploadList={false}onChange={(info) => {if (info.file.originFileObj) {importAvatar.execute(info.file.originFileObj)}}}maxCount={1}disabled={importAvatar.isLoading}name="file"><Avatarsize={64}icon={<UserOutlined />}src={avatarUrl}style={{ cursor: 'pointer' }}/></Upload><Text type="secondary">点击头像上传更换</Text></div></Form.Item><Form.Item<Users.Model>label="账户"name="username"rules={[{ required: true, message: '请输入账户名' }]}><Input placeholder="请输入账户名" /></Form.Item><Form.Item<Users.Model> label="手机号" name="mobile" extra="手机号一旦注册不可修改"><Input readOnly /></Form.Item><Form.Item<Users.Model> label="昵称" name="nickname"><Input placeholder="请输入昵称" /></Form.Item><Form.Item<Users.Model> label="密码" name="password"><Input.Password placeholder="如需修改请输入新密码" /></Form.Item></Form></Modal>)}))
}
  • 个人中心页(index.tsx) :用于展示用户信息。

  • 编辑弹窗模块(FormModal) :用于修改头像、昵称、密码等信息。

个人中心主页面

实现:

使用了antd pro(中后台管理)的组件PageContainer, ProCard, ProDescriptions

从状态管理中获取当前登录用户信息

通过 useRef 持有对弹窗组件的引用,以便调用 show(user_id) 弹出表单。

挂载
<PersonalModule.FormModal ref={formModal}></PersonalModule.FormModal>

点击编辑按钮时,调用 FormModal 暴露的 show 方法,打开弹窗。

通过 ProDescriptions 来渲染个人信息

个人中心的编辑弹窗

功能:

  • 支持修改昵称、密码、头像。

  • 上传头像后立即预览。

  • 保存后强制重新登录。

实现:

通过 useImperativeHandle 实现组件间命令式通信

useImperativeHandle(ref, () => ({show(id) {// 重置表单、加载用户数据}
}))

其中调用了获取用户信息接口去加载当前用户信息并填充到编辑表单中

头像上传
使用封装的 useStateAsync 来处理异步上传逻辑,增强可控性与状态追踪。
通过点击 Avatar,用户可直接上传图片替换头像,体验更友好。

更新个人信息后,清空用户状态并触发登出,强制用户重新登录以刷新身份数据。

然后遇到了很多问题

手机号不可修改

加下面注释

<Form.Item<Users.Model> label="手机号" name="mobile" extra="手机号一旦注册不可修改"><Input ></Input>{/* <div style={{ color: '  #C0C0C0', fontSize: '12px' }}>手机号一旦注册不可修改</div> */}
</Form.Item>

起初是直接给Input加了一个disabled
发现不行,不会被渲染初始值
因为:
Ant Design 的 Form.Item + Input disabled 组合有个限制:
被 disabled 的 Input 不会响应 setFieldsValue 的值变化,AntD 默认不更新它(这在受控组件中属于正常行为)。

改成了 readOnly

还有一个问题:<Form.Item> 内的 div 干扰了渲染

Ant Design 的 <Form.Item> 不支持你在其中直接放多个非表单组件子元素(如 <Input /> 之外的 <div>)。

这种用法会导致:

  • 表单字段渲染混乱(尤其是 setFieldsValue 失效或渲染错位);
  • 特别是你用的 form 是受控的,value 绑定会丢失。

可以使用 Form.Item 的 extra 属性,会在下方显示提示信息,渲染安全,不会干扰 Input

头像上传

首先这是又用了一个独立的接口,然后在Axios实例的请求拦截器那里要进行配置,在请求真正发出前对 config 进行处理。

instance.interceptors.request.use((config) => {// console.log(config)const { url } = configif (url && !whiteList.includes(url)) {const auth = accessStore.get().access_tokenif (auth) {config.headers.Authorization = `Bearer ${auth}`}} else {config.headers.Authorization = ''}if (url === Users.ApiUrls.ExportTemplate) {config.responseType = 'blob'config.headers['Content-Type'] = 'application/vnd.ms-csv'}if (url === Users.ApiUrls.ImportData|| url===Users.ApiUrls.ImportPersonalAvatar) {const formData = new FormData()formData.append('file', config.data.file)config.data = formDataconfig.headers['Content-Type'] = 'multipart/form-data'}return config},(error) => {return Promise.reject(error)}
)
  1. 鉴权处理:请求 URL 存在并且不在 whiteList 白名单中,请求头加 token

  2. 文件下载:设置响应类型与内容类型

  3. 文件上传:封装为 FormData

  4. 错误处理

组件的默认行为

完成后,发现了一个不知道哪里发出的 POST http://localhost:3000/personal 请求
发现了这个url是对应的个人中心页面,但应该是 GET 方法
所以应该是某个组件或 hook 在加载 /personal 页面时自动触发了一个不必要的 POST 请求。
然后发现是头像上传组件的 问题

<Form.Item label="头像" name="avatar"><div style={{ display: 'flex', alignItems: 'center', gap: 16 }}><Uploadaccept=""customRequest={() => {}}//beforeUpload={() => false} // 阻止默认上传showUploadList={false}onChange={(info) => {if (info.file.originFileObj) {importAvatar.execute(info.file.originFileObj)}}}maxCount={1}disabled={importAvatar.isLoading}name="file"><Avatarsize={64}icon={<UserOutlined />}src={avatarUrl}style={{ cursor: 'pointer' }}/></Upload><Text type="secondary">点击头像上传更换</Text></div>
</Form.Item>

如果 info.file.originFileObj 不存在,Upload 组件会默认走 action=“/personal” 来上传!
你没有显式指定 action 参数,所以 Ant Design 的 组件默认会使用当前页面地址 /personal 作为上传地址。

使用beforeUpload={() => false} // 阻止默认上传
加这个就不能上传本地图片了(都没有发起网络请求)

✅ 阻止 Ant Design Upload 组件的 自动上传行为

❌ 但不会触发 onChange 中的 info.file.originFileObj → 因为根本没触发上传流程

最后用customRequest={() => {}}
会触发onChange,适合手动上传

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/91106.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/91106.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/91106.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【7】串口编程三种模式(查询/中断/DMA)韦东山老师学习笔记(课程听不懂的话试着来看看我的学习笔记吧)

<1>前置概念补充在深入拆解三种模式前&#xff0c;先通过提供的 “函数对比表” 建立整体认知&#xff1a;这张表是串口收发的「武器库索引」&#xff0c;清晰标注了查询、中断、DMA 三种模式下&#xff0c;收发 / 回调函数的对应关系。后续会结合实际代码&#xff0c;讲…

【Kubernetes 指南】基础入门——Kubernetes 201(二)

二、滚动升级- 滚动升级&#xff08;Rolling Update&#xff09;通过逐个容器替代升级的方式来实现无中断的服务升级&#xff1a;- 在滚动升级的过程中&#xff0c;如果发现了失败或者配置错误&#xff0c;还可以随时回滚&#xff1a;- 需要注意的是&#xff0c; kubectl rolli…

网络资源模板--基于Android Studio 实现的图书商城App

目录 一、测试环境说明 二、项目简介 三、项目演示 四、部设计详情&#xff08;部分) 登录注册页 首页 五、项目源码 一、测试环境说明 电脑环境 Windows 11 编写语言 JAVA 开发软件 Android Studio (2020) 开发软件只要大于等于测试版本即可(近几年官网直接下载…

JavaWeb 进阶:Vue.js 与 Spring Boot 全栈开发实战(Java 开发者视角)

作为一名 Java 开发工程师&#xff0c;当你掌握了 HTML、CSS 和 JavaScript 的基础后&#xff0c;是时候接触现代前端框架了。Vue.js 以其简洁的 API、渐进式的设计和优秀的中文文档&#xff0c;成为众多 Java 开发者入门前端框架的首选。Vue.js 让你能快速构建响应式、组件化的…

智能体产品化的关键突破:企业智能化转型的“最后一公里”如何迈过?

智能体产品化的关键突破&#xff1a;企业智能化转型的“最后一公里”如何迈过&#xff1f; 在人工智能迅猛发展的当下&#xff0c;智能体&#xff08;Agent&#xff09;成为企业数字化转型的新引擎。无论是市场分析、客户服务&#xff0c;还是自动化办公&#xff0c;智能体都被…

Rust × Elasticsearch官方 `elasticsearch` crate 上手指南

1 为什么选择官方 Rust 客户端&#xff1f; 语义化兼容&#xff1a;客户端 主版本 与 ES 主版本 严格对应&#xff0c;8.x 客户端可对接任何 8.x 服务器&#xff1b;不存在跨主版本兼容承诺 (docs.rs)100% API 覆盖&#xff1a;稳定 API 全量映射&#xff0c;Beta/实验特性可按…

怎样画流程图?符号与流程解构教程

在数字化办公和项目管理日益复杂的当下&#xff0c;流程图早已不是工程师、项目经理的专属工具&#xff0c;它正快速成为每一位职场人提升表达效率、理清工作逻辑的利器。无论是软件开发中的流程规范、产品设计阶段的用户路径&#xff0c;还是企业内部的审批流程、团队协作机制…

vue3 + vite || Vue3 + Webpack创建项目

1.vue3 vite搭建项目方法 &#xff08;需要提前装node,js&#xff09; 1. 使用官方 create-vite 工具&#xff08;推荐&#xff09; 1.使用npm----------------------------- npm create vuelatest2.使用pnpm----------------------------- pnpm create vuelatest3.使用yarn--…

Vue2-封装一个含所有表单控件且支持动态增减行列的表格组件

效果1. 无编辑权限&#xff1a;显示普通表格2. 有编辑权限&#xff1a;根据配置显示编辑控件3. 可以动态新增行&#xff0c;也可以动态新增列 核心代码无权限情况的核心代码<!-- 无编辑权限时显示普通表格 --><el-tablev-if"!hasEditPermission"ref"ta…

网络原理 - TCP/IP(一)

目录 1. 应用层&#xff1a;用户与网络的 “交互窗口” 1.1 应用层协议&#xff1a;规范交互的 “通用语言” 1.2 自定义协议&#xff1a;适配特殊需求的 “专属规则” 1.3 应用层数据格式&#xff1a;让数据 “说得明白” 1.3.1 XML&#xff1a;结构化但繁琐的 “老…

Orange的运维学习日记--16.Linux时间管理

Orange的运维学习日记–16. Linux时间管理 文章目录Orange的运维学习日记--16. Linux时间管理系统与硬件时钟时钟类型对比查看内核支持的时钟源本地时间调整使用 date 查看与设置一次性同步&#xff1a;ntpdate同步到硬件时钟&#xff1a;hwclock基于 systemd 的 timedatectl交…

Git 与 GitHub 的对比与使用指南

Git 与 GitHub 的对比与使用指南 在软件开发中&#xff0c;Git 和 GitHub 是两个密切相关但本质不同的工具。下面我将逐步解释它们的定义、区别、核心概念以及如何协同使用&#xff0c;确保内容真实可靠&#xff0c;基于广泛的技术实践。 1. 什么是 Git&#xff1f; Git 是一个…

20250726-4-Kubernetes 网络-Service DNS名称解析_笔记

一、Service DNS名称 1. 例题:通信需求 通信场景:项目A中的Pod需要与项目B中的Pod进行通信,直接使用Pod IP不可行,因为Pod IP会随着Pod生命周期变化。 解决方案:通过Service提供的稳定IP地址进行通信,不受Pod重建、扩容/缩容等操作影响。 2. CoreDNS介绍  基本功能…

vscode 登录ssh记住密码直接登录设置

第一种情况在系统已经生成密钥对的情况下&#xff1a;点击这里的设置第二步&#xff1a;第三步&#xff1a;没有填写的给填写一下第四步骤&#xff1a;保存后进入选择这个点开第五步&#xff1a;去Linux终端下输入这个命令就OK了echo "ssh-rsa内容" >> ~/.ssh/…

Nginx 动静分离配置(详细版)

本文介绍了Nginx 动静分离相关配置&#xff0c;主要包括了配置文件创建、配置示例、配置原理解析以及重新启用配置文件等等 本文目录1. 创建 Nginx 配置文件2. 配置示例3. 配置原理解析4. 启用配置文件并重新加载 Nginx1. 创建 Nginx 配置文件 在 /etc/nginx/sites-available …

C# CAN通信上位机系统设计与实现

C# CAN通信上位机系统设计与实现 C# CAN通信上位机程序&#xff0c;支持多种CAN适配器&#xff0c;提供数据收发、协议解析、数据可视化等功能。 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; usi…

Ubuntu20.04子系统

常用 # 导出分发版到 E盘 wsl --export Ubuntu-20.04 E:\wsl-ubuntu20.04.tar # 注销原有分发版 wsl --unregister Ubuntu-20.04 # 导入到 E盘的新路径&#xff08;例如 E:\WSL\Ubuntu-20.04&#xff09; wsl --import Ubuntu-20.04 E:\WSL\Ubuntu-20.04 E:\wsl-ubuntu20.04.t…

【设计模式】状态模式 (状态对象(Objects for States))

状态模式&#xff08;State Pattern&#xff09;详解一、状态模式简介 状态模式&#xff08;State Pattern&#xff09; 是一种 行为型设计模式(对象行为型模式)&#xff0c;它允许一个对象在其内部状态改变时改变其行为。换句话说&#xff0c;对象看起来好像修改了它的类。 你…

工业前端组件库重构心法:如何让开发效率提升60%的交互模块设计逻辑

工业前端组件库重构心法&#xff1a;如何让开发效率提升60%的交互模块设计逻辑内容摘要在工业项目开发中&#xff0c;前端组件库是提升开发效率的关键。然而&#xff0c;许多团队的组件库存在设计不合理、维护困难等问题&#xff0c;导致开发效率低下。如果能够重构组件库&…

leetcode 74. 搜索二维矩阵

二分查找经典题目&#xff1b;根据矩阵的特点&#xff0c;不需要把矩阵拉成一维&#xff0c;二维转成一维映射关系为a[i]matrix[⌊i//n⌋][i%n]&#xff1b;然后开始二分查找&#xff0c;一直二分的收缩区间&#xff1b;class Solution:def searchMatrix(self, matrix: List[Li…