refactor(user-profile): 调整个人信息页面布局优化结构

- 调整用户ID位置至头像之上以强化信息层次
- 统一并优化代码格式排版,提升代码可读性
- 合并重复的用户ID展示,避免冗余
- 保持功能一致,未改动交互逻辑
- 优化注释及空白行,代码风格更规范
This commit is contained in:
2026-07-17 11:42:04 +08:00
parent ceba1cabec
commit 955b156399

View File

@@ -1,229 +1,207 @@
import React, { useState, useEffect } from 'react'
import { View, Text, Image, Input, Button as TaroButton } from '@tarojs/components'
import React, {useState, useEffect} from 'react'
import {View, Text, Image, Input, Button as TaroButton} from '@tarojs/components'
import Taro from '@tarojs/taro'
import BottomButton from '@/components/common/BottomButton'
import { useUser } from '@/hooks/useUser'
import { updateUser } from '@/api/system/user'
import type { User } from '@/api/system/user/model'
import { TenantId } from '@/config/app'
import {useUser} from '@/hooks/useUser'
import {updateUser} from '@/api/system/user'
import type {User} from '@/api/system/user/model'
import {TenantId} from '@/config/app'
definePageConfig({
navigationBarTitleText: '个人信息',
navigationBarTitleText: '个人信息',
})
const genderOptions = ['保密', '男', '女']
const ProfilePage: React.FC = () => {
const { user, refreshUser } = useUser()
const [form, setForm] = useState<Partial<User>>({})
const [loading, setLoading] = useState(false)
const [showGenderSheet, setShowGenderSheet] = useState(false)
const {user, refreshUser} = useUser()
const [form, setForm] = useState<Partial<User>>({})
const [loading, setLoading] = useState(false)
const [showGenderSheet, setShowGenderSheet] = useState(false)
useEffect(() => {
if (user) {
setForm({
userId: (user as any)?.userId || (user as any)?.id,
nickname: (user as any)?.nickname || '',
avatar: (user as any)?.avatar || '',
gender: (user as any)?.gender ?? 0,
phone: (user as any)?.phone || '',
merchantName: (user as any)?.merchantName || '',
address: (user as any)?.address || '',
})
}
}, [user])
// 微信头像选择回调open-type="chooseAvatar"
const handleWechatAvatar = (e: any) => {
const { avatarUrl } = e.detail
if (!avatarUrl) return
Taro.showLoading({ title: '上传中...' })
Taro.uploadFile({
url: 'https://server.websoft.top/api/oss/upload',
filePath: avatarUrl,
name: 'file',
header: {
'content-type': 'application/json',
TenantId,
},
success: (uploadRes) => {
const data = JSON.parse(uploadRes.data)
if (data.code === 0 && data.data?.url) {
setForm(prev => ({ ...prev, avatar: data.data.url }))
Taro.showToast({ title: '上传成功', icon: 'success' })
} else {
Taro.showToast({ title: data.message || '上传失败', icon: 'none' })
useEffect(() => {
if (user) {
setForm({
userId: (user as any)?.userId || (user as any)?.id,
nickname: (user as any)?.nickname || '',
avatar: (user as any)?.avatar || '',
gender: (user as any)?.gender ?? 0,
phone: (user as any)?.phone || '',
merchantName: (user as any)?.merchantName || '',
address: (user as any)?.address || '',
})
}
},
fail: () => {
Taro.showToast({ title: '上传失败', icon: 'none' })
},
complete: () => {
Taro.hideLoading()
},
})
}
}, [user])
// 选择性别
const handleGenderSelect = (index: number) => {
setForm(prev => ({ ...prev, gender: index }))
setShowGenderSheet(false)
}
// 保存
const handleSave = async () => {
if (!form.nickname?.trim()) {
Taro.showToast({ title: '昵称不能为空', icon: 'none' })
return
// 微信头像选择回调open-type="chooseAvatar"
const handleWechatAvatar = (e: any) => {
const {avatarUrl} = e.detail
if (!avatarUrl) return
Taro.showLoading({title: '上传中...'})
Taro.uploadFile({
url: 'https://server.websoft.top/api/oss/upload',
filePath: avatarUrl,
name: 'file',
header: {
'content-type': 'application/json',
TenantId,
},
success: (uploadRes) => {
const data = JSON.parse(uploadRes.data)
if (data.code === 0 && data.data?.url) {
setForm(prev => ({...prev, avatar: data.data.url}))
Taro.showToast({title: '上传成功', icon: 'success'})
} else {
Taro.showToast({title: data.message || '上传失败', icon: 'none'})
}
},
fail: () => {
Taro.showToast({title: '上传失败', icon: 'none'})
},
complete: () => {
Taro.hideLoading()
},
})
}
setLoading(true)
try {
await updateUser(form as User)
await refreshUser()
Taro.showToast({ title: '保存成功', icon: 'success' })
} catch (error) {
console.error('保存失败:', error)
Taro.showToast({ title: '保存失败', icon: 'none' })
} finally {
setLoading(false)
// 选择性别
const handleGenderSelect = (index: number) => {
setForm(prev => ({...prev, gender: index}))
setShowGenderSheet(false)
}
}
const displayId = (user as any)?.userId || (user as any)?.id || ''
const registerTime = (user as any)?.createTime || (user as any)?.createdAt || ''
// 保存
const handleSave = async () => {
if (!form.nickname?.trim()) {
Taro.showToast({title: '昵称不能为空', icon: 'none'})
return
}
setLoading(true)
try {
await updateUser(form as User)
await refreshUser()
Taro.showToast({title: '保存成功', icon: 'success'})
} catch (error) {
console.error('保存失败:', error)
Taro.showToast({title: '保存失败', icon: 'none'})
} finally {
setLoading(false)
}
}
return (
<View className='min-h-screen bg-gray-50 pb-32'>
{/* 头像 - 使用 Taro 原生 Button 支持 open-type="chooseAvatar" */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'>
<Text className='text-sm text-gray-700 w-20'></Text>
<View className='flex items-center gap-2'>
<TaroButton
openType='chooseAvatar'
onChooseAvatar={handleWechatAvatar}
plain
style={{
padding: 0,
margin: 0,
background: 'transparent',
border: 'none',
lineHeight: '40px',
width: 'auto',
minHeight: 0,
}}
>
{form.avatar ? (
<Image className='w-10 h-10 rounded-full' src={form.avatar} mode='aspectFill' />
) : (
<View className='w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center'>
<Text className='text-gray-300 text-xs'></Text>
</View>
const displayId = (user as any)?.userId || (user as any)?.id || ''
const registerTime = (user as any)?.createTime || (user as any)?.createdAt || ''
return (
<View className='min-h-screen bg-gray-50 pb-32'>
{/* 用户ID */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'>
<Text className='text-sm text-gray-700 w-20'>ID</Text>
<Text className='text-sm text-gray-600'>{displayId || '未设置'}</Text>
</View>
{/* 头像 - 使用 Taro 原生 Button 支持 open-type="chooseAvatar" */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'>
<Text className='text-sm text-gray-700 w-20'></Text>
<View className='flex items-center gap-2'>
<TaroButton
openType='chooseAvatar'
onChooseAvatar={handleWechatAvatar}
plain
style={{
padding: 0,
margin: 0,
background: 'transparent',
border: 'none',
lineHeight: '40px',
width: 'auto',
minHeight: 0,
}}
>
{form.avatar ? (
<Image className='w-10 h-10 rounded-full' src={form.avatar} mode='aspectFill'/>
) : (
<View className='w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center'>
<Text className='text-gray-300 text-xs'></Text>
</View>
)}
</TaroButton>
<Text className='text-gray-300 text-sm'>{'>'}</Text>
</View>
</View>
{/* 昵称 - 支持获取微信昵称 */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'>
<Text className='text-sm text-gray-700 w-20'></Text>
<Input
className='flex-1 text-right text-sm text-gray-600'
placeholder='请输入昵称'
value={form.nickname || ''}
onInput={(e: any) => setForm(prev => ({...prev, nickname: e.detail.value}))}
maxlength={20}
type='nickname'
/>
</View>
{/* 性别 */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'
onClick={() => setShowGenderSheet(true)}>
<Text className='text-sm text-gray-700 w-20'></Text>
<View className='flex items-center gap-2'>
<Text className='text-sm text-gray-600'>{genderOptions[form.gender ?? 0]}</Text>
<Text className='text-gray-300 text-sm'>{'>'}</Text>
</View>
</View>
{/* 手机号 - 不可编辑 */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'>
<Text className='text-sm text-gray-700 w-20'></Text>
<Text className='text-sm text-gray-600'>{form.phone || '未绑定'}</Text>
</View>
{/* 注册时间 */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'>
<Text className='text-sm text-gray-700 w-20'></Text>
<Text className='text-sm text-gray-600'>{registerTime || '未知'}</Text>
</View>
{/* 保存按钮 */}
<BottomButton
text='保存'
onClick={handleSave}
loading={loading}
disabled={loading}
/>
{/* 性别选择弹窗 */}
{showGenderSheet && (
<View className='fixed inset-0 z-50'>
<View className='absolute inset-0 bg-black bg-opacity-50'
onClick={() => setShowGenderSheet(false)}/>
<View className='absolute bottom-0 left-0 right-0 bg-white rounded-t-xl'>
<View className='px-4 py-3 border-b border-gray-50'>
<Text className='text-base font-medium text-gray-800'></Text>
</View>
{genderOptions.map((label, index) => (
<View
key={index}
className='px-4 py-3 border-b border-gray-50 flex items-center justify-between'
onClick={() => handleGenderSelect(index)}
>
<Text
className={`text-sm ${(form.gender ?? 0) === index ? 'text-green-600 font-medium' : 'text-gray-700'}`}>
{label}
</Text>
{(form.gender ?? 0) === index && <Text className='text-green-600'></Text>}
</View>
))}
<View className='px-4 py-3' onClick={() => setShowGenderSheet(false)}>
<Text className='text-sm text-gray-400 text-center block'></Text>
</View>
</View>
</View>
)}
</TaroButton>
<Text className='text-gray-300 text-sm'>{'>'}</Text>
</View>
</View>
{/* 昵称 - 支持获取微信昵称 */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'>
<Text className='text-sm text-gray-700 w-20'></Text>
<Input
className='flex-1 text-right text-sm text-gray-600'
placeholder='请输入昵称'
value={form.nickname || ''}
onInput={(e: any) => setForm(prev => ({ ...prev, nickname: e.detail.value }))}
maxlength={20}
type='nickname'
/>
</View>
{/* 性别 */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50' onClick={() => setShowGenderSheet(true)}>
<Text className='text-sm text-gray-700 w-20'></Text>
<View className='flex items-center gap-2'>
<Text className='text-sm text-gray-600'>{genderOptions[form.gender ?? 0]}</Text>
<Text className='text-gray-300 text-sm'>{'>'}</Text>
</View>
</View>
{/* 手机号 - 不可编辑 */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'>
<Text className='text-sm text-gray-700 w-20'></Text>
<Text className='text-sm text-gray-600'>{form.phone || '未绑定'}</Text>
</View>
{/* 用户ID */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'>
<Text className='text-sm text-gray-700 w-20'>ID</Text>
<Text className='text-sm text-gray-600'>{displayId || '未设置'}</Text>
</View>
{/* 注册时间 */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'>
<Text className='text-sm text-gray-700 w-20'></Text>
<Text className='text-sm text-gray-600'>{registerTime || '未知'}</Text>
</View>
{/* 门店名称 */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'>
<Text className='text-sm text-gray-700 w-20'></Text>
<Input
className='flex-1 text-right text-sm text-gray-600'
placeholder='请输入门店名称'
value={(form as any)?.merchantName || ''}
onInput={(e: any) => setForm(prev => ({ ...prev, merchantName: e.detail.value }))}
maxlength={50}
/>
</View>
{/* 门店地址 */}
<View className='flex items-center justify-between px-4 py-3 bg-white border-b border-gray-50'>
<Text className='text-sm text-gray-700 w-20'></Text>
<Input
className='flex-1 text-right text-sm text-gray-600'
placeholder='请输入门店地址'
value={(form as any)?.address || ''}
onInput={(e: any) => setForm(prev => ({ ...prev, address: e.detail.value }))}
maxlength={100}
/>
</View>
{/* 保存按钮 */}
<BottomButton
text='保存'
onClick={handleSave}
loading={loading}
disabled={loading}
/>
{/* 性别选择弹窗 */}
{showGenderSheet && (
<View className='fixed inset-0 z-50'>
<View className='absolute inset-0 bg-black bg-opacity-50' onClick={() => setShowGenderSheet(false)} />
<View className='absolute bottom-0 left-0 right-0 bg-white rounded-t-xl'>
<View className='px-4 py-3 border-b border-gray-50'>
<Text className='text-base font-medium text-gray-800'></Text>
</View>
{genderOptions.map((label, index) => (
<View
key={index}
className='px-4 py-3 border-b border-gray-50 flex items-center justify-between'
onClick={() => handleGenderSelect(index)}
>
<Text className={`text-sm ${(form.gender ?? 0) === index ? 'text-green-600 font-medium' : 'text-gray-700'}`}>
{label}
</Text>
{(form.gender ?? 0) === index && <Text className='text-green-600'></Text>}
</View>
))}
<View className='px-4 py-3' onClick={() => setShowGenderSheet(false)}>
<Text className='text-sm text-gray-400 text-center block'></Text>
</View>
</View>
</View>
)}
</View>
)
)
}
export default ProfilePage