refactor(user-profile): 调整个人信息页面布局优化结构
- 调整用户ID位置至头像之上以强化信息层次 - 统一并优化代码格式排版,提升代码可读性 - 合并重复的用户ID展示,避免冗余 - 保持功能一致,未改动交互逻辑 - 优化注释及空白行,代码风格更规范
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user