个人信息管理页面的设计与实现:从展示到编辑
在现代应用开发中,用户信息管理是一个核心功能。本文将详细介绍如何使用Taro框架开发一个完整的个人信息管理模块,包含展示页面和编辑页面。
复制
- 我的页面(people.jsx)
└── 个人信息展示
└── 功能入口
└── 进入编辑页面入口
- 编辑页面(peopleDetail.jsx)
└── 头像上传
└── 昵称编辑
└── 性别选择
└── 生日选择
└── 保存功能
import { View, Text, Image } from '@tarojs/components'
import { Button } from '@taroify/core'
import { useLoad } from '@tarojs/taro'
import Taro from '@tarojs/taro'
import { useState } from 'react'
import bgweek from '../../assets/week-btn.png'
import bgactivity from '../../assets/activity-btn.png'
import 'normalize.css'
import './people.scss'
import request from '../../utils/request'
const People = () => {
const [userInfo, setUserInfo] = useState({})
// 获取用户信息const getUserInfo = async() => {
try {
const res = await request.get('/user/getUserInfo')
if (res.data.code === 200) {
setUserInfo(res.data.data)
Taro.setStorageSync('userInfo', res.data.data)
}
} catch (error) {
console.error('获取用户信息失败:', error)
Taro.showToast({ title: '数据加载失败', icon: 'none' })
}
}
// 页面加载钩子useLoad(() => {
getUserInfo()
});
// 退出登录处理const handleLogout = () => {
try {
// 清理存储Taro.removeStorageSync('token');
Taro.removeStorageSync('userInfo');
// 返回首页Taro.reLaunch({ url: '/pages/index/index' });
// 提示用户Taro.showToast({
title: '已退出登录',
icon: 'success',
duration: 1000
});
} catch (error) {
console.error('退出登录失败:', error);
Taro.showToast({ title: '退出登录失败', icon: 'none' });
}
};
return (
{/* 用户信息头部 */}
{userInfo.nickname || '未设置昵称'}
破壳日:{userInfo.birthday || '未设置'}
Taro.navigateTo({ url: '/pages/peopleDetail/peopleDetail' })}
>
编辑信息 >
{/* 功能入口 */}
周报
Taro.switchTab({ url: '/pages/activityList/activityList' })}
>
活动
{/* 退出登录按钮 */}
);
};
export default People;
页面样式(people.scss)
.header {
width: 90%;
height: 200px;
display: flex;
align-items: center;
background-color: rgb(255, 255, 255);
border-radius: 20px;
margin: auto;
padding: 5px;
top: 10px;
box-shadow: 0px 0px 2px #00000007;
}
.avatar {
width: 100px;
height: 100px;
border-radius: 50%;
margin-left: 10%;
}
.middle {
width: 50%;
}
.nickname {
font-weight: bold;
font-size: 35px;
margin-left: 30px;
}
.unknown {
display: block;
font-size: 25px;
color: rgb(145, 145, 145);
margin-left: 30px;
margin-top: 20px;
}
.seemore {
font-size: small;
color: rgb(172, 172, 172);
}
.log-button {
position: fixed;
bottom: 10%;
background-color: #fcb391;
color: white;
border-radius: 8px;
font-weight: bold;
border: none;
padding: 12px;
width: 90%;
margin-left: 5%;
&:active {
background-color: #8B4513;
}
}
.btn {
display: flex;
align-items: center;
justify-content: center;
margin: auto;
top: 20px;
}
.week-analysis, .activity {
// position: relative; /* 定位基准 */
float: left;
width: 40%;
height: 150px;
margin: auto;
margin-top: 10px;
margin-left: 5%;
margin-right: 5%;
background-size: 100% 100%;
border-radius: 10px;
background-repeat: no-repeat;
justify-content: center;
}
.week-content {
font-family: 'summerpoem';
font-size: 80px;
color: #343357;
font-weight: bold;
margin-left: 55px;
margin-top: 40px;
}
.activity-content {
position: relative;
font-family: 'summerpoem';
font-size: 80px;
color: #ffffff;
font-weight: bold;
padding-left: 50px;
margin-top: 60px;
}
import { View, Text, Image, Input } from '@tarojs/components'
import { Button, Field, Radio, Cell } from '@taroify/core'
import { useLoad } from '@tarojs/taro'
import Taro from '@tarojs/taro'
import { useState } from 'react'
import { Popup, DatetimePicker } from '@taroify/core'
import dayjs from 'dayjs'
import 'normalize.css'
import './peopleDetail.scss'
import request from '../../utils/request'
const PeopleDetail = () => {
// 表单状态管理const [avatar, setAvatar] = useState("")
const [nickname, setNickname] = useState("")
const [gender, setGender] = useState("0")
const [birthday, setBirthday] = useState("")
// UI状态const [showDatePicker, setShowDatePicker] = useState(false)
const [tempDate, setTempDate] = useState(new Date())
// 初始化表单数据useLoad(() => {
const storedUserInfo = Taro.getStorageSync('userInfo') || {}
setAvatar(storedUserInfo.avatar || '')
setNickname(storedUserInfo.nickname || '')
setGender(String(storedUserInfo.gender || '0'))
setBirthday(storedUserInfo.birthday || '')
if (storedUserInfo.birthday) {
setTempDate(new Date(storedUserInfo.birthday))
}
})
// 头像上传处理const uploadAvatar = async () => {
try {
// 选择图片const res = await Taro.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera']
})
if (!res.tempFilePaths.length) return
// 显示加载状态Taro.showLoading({ title: '上传中...', mask: true })
// 上传图片const uploadRes = await Taro.uploadFile({
url: '',
filePath: res.tempFilePaths[0],
name: 'file',
formData: {
timestamp: Date.now()
},
})
Taro.hideLoading()
// 处理响应const data = JSON.parse(uploadRes.data)
if (data.msg) {
setAvatar(data.msg)
Taro.showToast({ title: '头像上传成功', icon: 'success' })
} else {
Taro.showToast({ title: '上传失败,请重试', icon: 'none' })
}
} catch (error) {
Taro.hideLoading()
console.error('图片上传失败:', error)
Taro.showToast({ title: '图片上传失败', icon: 'error' })
}
}
// 处理日期变更const handleDateChange = (date) => {
setTempDate(date)
}
// 确认日期选择const handleDateConfirm = () => {
const formattedDate = dayjs(tempDate).format('YYYY-MM-DD')
setBirthday(formattedDate)
setShowDatePicker(false)
}
// 保存修改const handleSave = async () => {
// 基本验证if (!nickname.trim()) {
Taro.showToast({ title: '昵称不能为空', icon: 'none' })
return
}
try {
const updateData = {
avatar,
nickname,
gender: Number(gender),
birthday
}
Taro.showLoading({ title: '保存中...', mask: true })
// 提交修改const res = await request.post('/user/updateUserInfo', updateData)
Taro.hideLoading()
if (res.data.code === 200) {
// 更新本地存储const newUserInfo = {
...(Taro.getStorageSync('userInfo') || {}),
...updateData
}
Taro.setStorageSync('userInfo', newUserInfo)
Taro.showToast({
title: '保存成功',
icon: 'success',
duration: 1500,
complete: () => Taro.navigateBack()
})
} else {
Taro.showToast({
title: res.data.msg || '保存失败',
icon: 'none',
duration: 2000
})
}
} catch (error) {
Taro.hideLoading()
console.error('保存失败:', error)
Taro.showToast({
title: '网络错误,请重试',
icon: 'none',
duration: 2000
})
}
}
return (
{/* 表单区域 */}
{/* 头像区域 */}
"}
/>
点击更换
|
}
/>
{/* 昵称区域 */}
setNickname(e.detail.value)}
className="nickname-input"
/>
{/* 性别区域 */}
setGender(value)}
direction="horizontal"
className="gender-group"
>
女
男
{/* 生日区域 */}
setShowDatePicker(true)}
className="birthday-field"
>
{birthday || '请选择生日'}
{/* 保存按钮 */}
{/* 日期选择器弹窗 */}
setShowDatePicker(false)}
className="date-popup"
>
)
}
export default PeopleDetail
编辑页样式(peopleDetail.scss)
.people-detail-container {
padding: 16px;
min-height: 100vh;
.avatar-image {
width: 80px;
height: 80px;
border-radius: 50%;
background-color: #eee;
}
.taroify-field__label {
flex: none;
width: 80px;
}
.save-button {
width: 90%;
background-color: #fcb391;
color: white;
border-radius: 8px;
font-weight: bold;
border: none;
padding: 12px;
margin: auto;
margin-top: 40px;
&:active {
background-color: #8B4513;
}
}
}
头像上传涉及几个关键步骤:
const uploadAvatar = async () => {
// 1. 选择图片const res = await Taro.chooseImage({...})
// 2. 显示加载状态Taro.showLoading({...})
// 3. 上传文件const uploadRes = await Taro.uploadFile({...})
// 4. 处理响应if (responseOk) {
setAvatar(newUrl)
} else {
showError()
}
}
技术要点:
Taro.chooseImage
获取用户选择的图片Taro.uploadFile
执行文件上传使用Radio.Group实现单选效果:
女 男
说明:
使用DatetimePicker组件实现生日选择:
{/* 生日字段 */}
setShowDatePicker(true)}>
{birthday || '请选择生日'}
{/* 日期选择器弹窗 */}
关键点:
tempDate
存储当前选择的日期使用React状态管理表单数据:
// 状态声明const [avatar, setAvatar] = useState("")
const [nickname, setNickname] = useState("")
const [gender, setGender] = useState("0")
const [birthday, setBirthday] = useState("")
// 保存处理const handleSave = async () => {
// 表单验证if (!nickname) return
// 构造数据const updateData = {
avatar,
nickname,
gender: Number(gender),
birthday
}
// 提交保存...
}
本实现方案提供了一个完整的用户信息管理模块,包含展示和编辑两大功能,采用了Taro框架结合Taroify UI库进行开发。读者可以根据实际需求进行调整,例如添加更多的用户信息字段,或者结合Redux等状态管理库实现更复杂的应用状态。