index.tsx 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. import { useState } from 'react'
  2. import { View, Text, 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. import { Button } from '@/components/ui/button'
  8. import { Navbar } from '@/components/ui/navbar'
  9. import { AvatarUpload } from '@/components/ui/avatar-upload'
  10. import { type UploadResult } from '@/utils/minio'
  11. import TDesignUserCenterCard from '@/components/tdesign/user-center-card'
  12. import TDesignOrderGroup from '@/components/tdesign/order-group'
  13. import TDesignCellGroup from '@/components/tdesign/cell-group'
  14. import TDesignCell from '@/components/tdesign/cell'
  15. import TDesignPopup from '@/components/tdesign/popup'
  16. import TDesignIcon from '@/components/tdesign/icon'
  17. import './index.css'
  18. const ProfilePage: React.FC = () => {
  19. const { user: userProfile, logout, isLoading: loading, updateUser } = useAuth()
  20. const [updatingAvatar, setUpdatingAvatar] = useState(false)
  21. const [showCustomerService, setShowCustomerService] = useState(false)
  22. const handleLogout = async () => {
  23. try {
  24. Taro.showModal({
  25. title: '退出登录',
  26. content: '确定要退出登录吗?',
  27. success: async (res) => {
  28. if (res.confirm) {
  29. Taro.showLoading({ title: '退出中...' })
  30. await logout()
  31. Taro.hideLoading()
  32. Taro.showToast({
  33. title: '已退出登录',
  34. icon: 'success',
  35. duration: 1500
  36. })
  37. setTimeout(() => {
  38. Taro.reLaunch({ url: '/pages/index/index' })
  39. }, 1500)
  40. }
  41. }
  42. })
  43. } catch (error) {
  44. Taro.hideLoading()
  45. Taro.showToast({
  46. title: '退出失败,请重试',
  47. icon: 'none'
  48. })
  49. }
  50. }
  51. const handleAvatarUpload = async (result: UploadResult) => {
  52. try {
  53. setUpdatingAvatar(true)
  54. Taro.showLoading({ title: '更新头像...' })
  55. // 这里应该调用更新用户头像的API
  56. // 假设有一个更新用户信息的API
  57. console.log('头像上传成功:', result)
  58. // 更新本地用户数据
  59. if (userProfile) {
  60. const updatedUser = {
  61. ...userProfile,
  62. avatarFileId: result.fileId
  63. }
  64. updateUser(updatedUser)
  65. }
  66. Taro.hideLoading()
  67. Taro.showToast({
  68. title: '头像更新成功',
  69. icon: 'success'
  70. })
  71. } catch (error) {
  72. console.error('更新头像失败:', error)
  73. Taro.hideLoading()
  74. Taro.showToast({
  75. title: '更新头像失败',
  76. icon: 'none'
  77. })
  78. } finally {
  79. setUpdatingAvatar(false)
  80. }
  81. }
  82. const handleAvatarUploadError = (error: Error) => {
  83. console.error('头像上传失败:', error)
  84. Taro.showToast({
  85. title: '上传失败,请重试',
  86. icon: 'none'
  87. })
  88. }
  89. const handleEditProfile = () => {
  90. Taro.showToast({
  91. title: '功能开发中...',
  92. icon: 'none'
  93. })
  94. }
  95. const handleSettings = () => {
  96. Taro.showToast({
  97. title: '功能开发中...',
  98. icon: 'none'
  99. })
  100. }
  101. const handleCustomerService = () => {
  102. setShowCustomerService(true)
  103. }
  104. const handleCloseCustomerService = () => {
  105. setShowCustomerService(false)
  106. }
  107. const handleCallCustomerService = () => {
  108. Taro.makePhoneCall({
  109. phoneNumber: '400-123-4567'
  110. })
  111. }
  112. // 订单状态数据
  113. const orderTagInfos = [
  114. { title: '待付款', iconName: 'clock', orderNum: 0 },
  115. { title: '待发货', iconName: 'package', orderNum: 0 },
  116. { title: '待收货', iconName: 'truck', orderNum: 0 },
  117. { title: '待评价', iconName: 'star', orderNum: 0 }
  118. ]
  119. // 菜单数据 - 按照 tcb-shop-demo 的结构
  120. const menuData = [
  121. [
  122. { title: '收货地址', icon: 'location', type: 'address' },
  123. { title: '联系客服', icon: 'service', type: 'service' }
  124. ],
  125. [
  126. { title: '关于我们', icon: 'info-circle', type: 'about' },
  127. { title: '隐私政策', icon: 'shield-check', type: 'privacy' }
  128. ]
  129. ]
  130. const handleCellClick = (type: string) => {
  131. switch (type) {
  132. case 'address':
  133. Taro.showToast({ title: '收货地址功能开发中...', icon: 'none' })
  134. break
  135. case 'service':
  136. handleCustomerService()
  137. break
  138. case 'about':
  139. Taro.showToast({ title: '关于我们功能开发中...', icon: 'none' })
  140. break
  141. case 'privacy':
  142. Taro.showToast({ title: '隐私政策功能开发中...', icon: 'none' })
  143. break
  144. default:
  145. Taro.showToast({ title: '功能开发中...', icon: 'none' })
  146. }
  147. }
  148. if (loading) {
  149. return (
  150. <TabBarLayout activeKey="profile">
  151. <View className="flex-1 flex items-center justify-center">
  152. <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
  153. </View>
  154. </TabBarLayout>
  155. )
  156. }
  157. if (!userProfile) {
  158. return (
  159. <TabBarLayout activeKey="profile">
  160. <Navbar
  161. title="个人中心"
  162. leftIcon=""
  163. />
  164. <View className="flex-1 flex flex-col items-center justify-center">
  165. <View className="flex flex-col items-center">
  166. <View className="i-heroicons-exclamation-circle-20-solid w-12 h-12 text-gray-400 mx-auto mb-4" />
  167. <Text className="text-gray-600 mb-4">请先登录</Text>
  168. <Button
  169. variant="default"
  170. size="lg"
  171. onClick={() => Taro.navigateTo({ url: '/pages/login/index' })}
  172. >
  173. 去登录
  174. </Button>
  175. </View>
  176. </View>
  177. </TabBarLayout>
  178. )
  179. }
  180. return (
  181. <TabBarLayout activeKey="profile">
  182. {/* 用户中心卡片 - 使用固定定位 */}
  183. <TDesignUserCenterCard
  184. avatar={userProfile.avatarFile?.fullUrl}
  185. nickname={userProfile.username}
  186. isLoggedIn={!!userProfile}
  187. onUserEdit={handleEditProfile}
  188. className="tdesign-user-center-card-profile"
  189. />
  190. {/* 内容区域 - 使用 margin-top 定位 */}
  191. <ScrollView className="flex-1 bg-gray-50 tdesign-user-center-content">
  192. {/* 订单状态卡片 */}
  193. <View className="px-4 pt-4">
  194. <TDesignOrderGroup
  195. orderTagInfos={orderTagInfos}
  196. title="我的订单"
  197. desc="全部订单"
  198. onTopClick={() => Taro.showToast({ title: '查看全部订单', icon: 'none' })}
  199. onItemClick={(item) => Taro.showToast({ title: `查看${item.title}订单`, icon: 'none' })}
  200. />
  201. </View>
  202. {/* 功能菜单 */}
  203. <View className="px-4 pt-4">
  204. {menuData.map((group, groupIndex) => (
  205. <View key={groupIndex} className="mb-4">
  206. <TDesignCellGroup>
  207. {group.map((item, itemIndex) => (
  208. <TDesignCell
  209. key={itemIndex}
  210. title={item.title}
  211. arrow
  212. bordered={itemIndex < group.length - 1}
  213. onClick={() => handleCellClick(item.type)}
  214. noteSlot={
  215. <TDesignIcon
  216. name={item.icon}
  217. size="48rpx"
  218. color="#6a6a6a"
  219. />
  220. }
  221. />
  222. ))}
  223. </TDesignCellGroup>
  224. </View>
  225. ))}
  226. </View>
  227. {/* 退出登录按钮 */}
  228. <View className="px-4 pt-6 pb-8">
  229. <Button
  230. variant="destructive"
  231. size="lg"
  232. className="w-full"
  233. onClick={handleLogout}
  234. >
  235. <View className="flex items-center justify-center">
  236. <View className="i-heroicons-arrow-left-on-rectangle-20-solid w-5 h-5 mr-2" />
  237. 退出登录
  238. </View>
  239. </Button>
  240. </View>
  241. {/* 版本信息 */}
  242. <View className="pb-8">
  243. <Text className="text-center text-xs text-gray-400">
  244. v1.0.0 - 小程序版
  245. </Text>
  246. </View>
  247. </ScrollView>
  248. {/* 客服弹窗 */}
  249. <TDesignPopup
  250. visible={showCustomerService}
  251. placement="bottom"
  252. onVisibleChange={(visible) => setShowCustomerService(visible)}
  253. onClose={handleCloseCustomerService}
  254. >
  255. <View className="popup-content">
  256. <View className="popup-title border-bottom-1px">
  257. 服务时间: 9:00-18:00
  258. </View>
  259. <View
  260. className="popup-phone border-bottom-1px"
  261. onClick={handleCallCustomerService}
  262. >
  263. 电话客服
  264. </View>
  265. <button
  266. className="popup-phone border-bottom-1px online"
  267. open-type="contact"
  268. >
  269. 在线客服
  270. </button>
  271. <View
  272. className="popup-close"
  273. onClick={handleCloseCustomerService}
  274. >
  275. 取消
  276. </View>
  277. </View>
  278. </TDesignPopup>
  279. </TabBarLayout>
  280. )
  281. }
  282. export default ProfilePage