index.tsx 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import { useState, useEffect } from 'react'
  2. import { View, Text, Image, Button, ScrollView } from '@tarojs/components'
  3. import Taro from '@tarojs/taro'
  4. import { TabBarLayout } from '@/layouts/tab-bar-layout'
  5. import { useAuth } from '@/utils/auth'
  6. import { cn } from '@/utils/cn'
  7. interface UserProfile {
  8. id: number
  9. username: string
  10. email?: string
  11. avatar?: string
  12. createdAt: string
  13. lastLoginAt?: string
  14. }
  15. const ProfilePage: React.FC = () => {
  16. const [userProfile, setUserProfile] = useState<UserProfile | null>(null)
  17. const [loading, setLoading] = useState(true)
  18. const { user, logout } = useAuth()
  19. useEffect(() => {
  20. if (user) {
  21. setUserProfile({
  22. id: user.id,
  23. username: user.username,
  24. email: user.email,
  25. avatar: user.avatar || 'https://source.unsplash.com/200x200/?avatar,face',
  26. createdAt: new Date().toISOString(),
  27. lastLoginAt: new Date().toISOString()
  28. })
  29. }
  30. setLoading(false)
  31. }, [user])
  32. const handleLogout = async () => {
  33. try {
  34. Taro.showModal({
  35. title: '退出登录',
  36. content: '确定要退出登录吗?',
  37. success: async (res) => {
  38. if (res.confirm) {
  39. Taro.showLoading({ title: '退出中...' })
  40. await logout()
  41. Taro.hideLoading()
  42. Taro.showToast({
  43. title: '已退出登录',
  44. icon: 'success',
  45. duration: 1500
  46. })
  47. setTimeout(() => {
  48. Taro.reLaunch({ url: '/pages/index/index' })
  49. }, 1500)
  50. }
  51. }
  52. })
  53. } catch (error) {
  54. Taro.hideLoading()
  55. Taro.showToast({
  56. title: '退出失败,请重试',
  57. icon: 'none'
  58. })
  59. }
  60. }
  61. const handleEditProfile = () => {
  62. Taro.showToast({
  63. title: '功能开发中...',
  64. icon: 'none'
  65. })
  66. }
  67. const handleSettings = () => {
  68. Taro.showToast({
  69. title: '功能开发中...',
  70. icon: 'none'
  71. })
  72. }
  73. const menuItems = [
  74. {
  75. icon: 'i-heroicons-user-circle-20-solid',
  76. title: '编辑资料',
  77. onClick: handleEditProfile,
  78. color: 'text-blue-500'
  79. },
  80. {
  81. icon: 'i-heroicons-cog-6-tooth-20-solid',
  82. title: '设置',
  83. onClick: handleSettings,
  84. color: 'text-gray-500'
  85. },
  86. {
  87. icon: 'i-heroicons-shield-check-20-solid',
  88. title: '隐私政策',
  89. onClick: () => Taro.showToast({ title: '功能开发中...', icon: 'none' }),
  90. color: 'text-green-500'
  91. },
  92. {
  93. icon: 'i-heroicons-question-mark-circle-20-solid',
  94. title: '帮助与反馈',
  95. onClick: () => Taro.showToast({ title: '功能开发中...', icon: 'none' }),
  96. color: 'text-purple-500'
  97. }
  98. ]
  99. if (loading) {
  100. return (
  101. <TabBarLayout activeKey="profile">
  102. <View className="flex-1 flex items-center justify-center">
  103. <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
  104. </View>
  105. </TabBarLayout>
  106. )
  107. }
  108. if (!userProfile) {
  109. return (
  110. <TabBarLayout activeKey="profile">
  111. <View className="flex-1 flex items-center justify-center">
  112. <View className="text-center">
  113. <View className="i-heroicons-exclamation-circle-20-solid w-12 h-12 text-gray-400 mx-auto mb-4" />
  114. <Text className="text-gray-600 mb-4">请先登录</Text>
  115. <Button
  116. className="bg-blue-500 text-white px-6 py-2 rounded-lg"
  117. onClick={() => Taro.navigateTo({ url: '/pages/login/index' })}
  118. >
  119. 去登录
  120. </Button>
  121. </View>
  122. </View>
  123. </TabBarLayout>
  124. )
  125. }
  126. return (
  127. <TabBarLayout activeKey="profile">
  128. <ScrollView className="flex-1 bg-gray-50">
  129. {/* 用户信息卡片 */}
  130. <View className="bg-white rounded-b-3xl shadow-sm pb-8">
  131. <View className="flex flex-col items-center pt-8 pb-6">
  132. <View className="relative">
  133. <Image
  134. className="w-24 h-24 rounded-full border-4 border-white shadow-lg"
  135. src={userProfile.avatar || 'https://source.unsplash.com/200x200/?avatar'}
  136. mode="aspectFill"
  137. />
  138. <View className="absolute -bottom-2 -right-2 w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center shadow-md">
  139. <View className="i-heroicons-camera-20-solid w-4 h-4 text-white" />
  140. </View>
  141. </View>
  142. <Text className="text-xl font-bold text-gray-900 mt-4">{userProfile.username}</Text>
  143. {userProfile.email && (
  144. <Text className="text-sm text-gray-600 mt-1">{userProfile.email}</Text>
  145. )}
  146. <View className="flex items-center mt-2">
  147. <View className="i-heroicons-calendar-20-solid w-4 h-4 text-gray-400 mr-1" />
  148. <Text className="text-xs text-gray-500">
  149. 注册于 {new Date(userProfile.createdAt).toLocaleDateString('zh-CN')}
  150. </Text>
  151. </View>
  152. </View>
  153. {/* 统计信息 */}
  154. <View className="px-6">
  155. <View className="grid grid-cols-3 gap-4 text-center">
  156. <View className="bg-gray-50 rounded-xl p-4">
  157. <Text className="text-2xl font-bold text-blue-500">0</Text>
  158. <Text className="text-xs text-gray-600 mt-1">收藏</Text>
  159. </View>
  160. <View className="bg-gray-50 rounded-xl p-4">
  161. <Text className="text-2xl font-bold text-green-500">0</Text>
  162. <Text className="text-xs text-gray-600 mt-1">点赞</Text>
  163. </View>
  164. <View className="bg-gray-50 rounded-xl p-4">
  165. <Text className="text-2xl font-bold text-purple-500">0</Text>
  166. <Text className="text-xs text-gray-600 mt-1">关注</Text>
  167. </View>
  168. </View>
  169. </View>
  170. </View>
  171. {/* 功能菜单 */}
  172. <View className="px-4 pt-6">
  173. <View className="bg-white rounded-2xl shadow-sm overflow-hidden">
  174. {menuItems.map((item, index) => (
  175. <View
  176. key={index}
  177. className="flex items-center px-4 py-4 active:bg-gray-50 transition-colors duration-150"
  178. onClick={item.onClick}
  179. >
  180. <View className={cn("w-6 h-6 mr-3", item.color, item.icon)} />
  181. <Text className="flex-1 text-gray-800">{item.title}</Text>
  182. <View className="i-heroicons-chevron-right-20-solid w-5 h-5 text-gray-400" />
  183. </View>
  184. ))}
  185. </View>
  186. </View>
  187. {/* 账号信息 */}
  188. <View className="px-4 pt-6">
  189. <View className="bg-white rounded-2xl shadow-sm p-4">
  190. <Text className="text-sm font-medium text-gray-700 mb-3">账号信息</Text>
  191. <View className="space-y-3">
  192. <View className="flex justify-between items-center">
  193. <Text className="text-sm text-gray-600">用户ID</Text>
  194. <Text className="text-sm text-gray-900 font-mono">{userProfile.id}</Text>
  195. </View>
  196. {userProfile.lastLoginAt && (
  197. <View className="flex justify-between items-center">
  198. <Text className="text-sm text-gray-600">最近登录</Text>
  199. <Text className="text-sm text-gray-900">
  200. {new Date(userProfile.lastLoginAt).toLocaleString('zh-CN')}
  201. </Text>
  202. </View>
  203. )}
  204. </View>
  205. </View>
  206. </View>
  207. {/* 退出登录按钮 */}
  208. <View className="px-4 pt-6 pb-8">
  209. <Button
  210. className="w-full h-12 bg-red-500 text-white rounded-xl font-medium hover:bg-red-600 active:bg-red-700 transition-colors duration-200"
  211. onClick={handleLogout}
  212. >
  213. <View className="flex items-center justify-center">
  214. <View className="i-heroicons-arrow-left-on-rectangle-20-solid w-5 h-5 mr-2" />
  215. 退出登录
  216. </View>
  217. </Button>
  218. </View>
  219. {/* 版本信息 */}
  220. <View className="pb-8">
  221. <Text className="text-center text-xs text-gray-400">
  222. v1.0.0 - 小程序版
  223. </Text>
  224. </View>
  225. </ScrollView>
  226. </TabBarLayout>
  227. )
  228. }
  229. export default ProfilePage