Procházet zdrojové kódy

✨ feat(profile): 按照mini-demo样式规范重构个人中心页面

- 重构用户信息区域为渐变背景样式(#4A90C2 → #357ABD)
- 添加会员信息卡片组件,包含积分和累计消费展示
- 重构功能菜单为网格布局,添加描述文字
- 添加客服与帮助区域,包含联系客服、常见问题、意见反馈
- 实现精确的样式迁移,包括颜色、渐变、阴影、圆角、间距
- 添加会员等级和积分展示(普通会员,积分:0,累计消费:¥0)
- 实现会员进度条组件,显示升级进度
- 添加版本信息展示(去看出行 v1.0.0)
- 按照mini-demo样式规范重新设计退出登录按钮

♻️ refactor(components): 更新AvatarUpload组件支持className属性

- 添加className属性以支持自定义样式
- 修复TypeScript错误:箭头符号语法问题
yourname před 3 měsíci
rodič
revize
a24a9c320b

+ 24 - 9
docs/stories/005.010.personal-center-management.story.md

@@ -27,15 +27,15 @@ In Progress - MVP Basic UI Completed
   - [x] 实现基础功能菜单
   - [x] 实现退出登录功能
   - [x] 实现账号信息展示
-- [ ] 按照 mini-demo 样式规范重构页面 (AC: 1, 3, 4, 5)
-  - [ ] 重构用户信息区域为渐变背景样式
-  - [ ] 添加会员信息卡片组件
-  - [ ] 重构功能菜单为网格布局
-  - [ ] 添加客服与帮助区域
-  - [ ] 实现精确的样式迁移
-  - [ ] 添加会员等级和积分展示
-  - [ ] 实现会员进度条组件
-  - [ ] 添加版本信息展示
+- [x] 按照 mini-demo 样式规范重构页面 (AC: 1, 3, 4, 5)
+  - [x] 重构用户信息区域为渐变背景样式
+  - [x] 添加会员信息卡片组件
+  - [x] 重构功能菜单为网格布局
+  - [x] 添加客服与帮助区域
+  - [x] 实现精确的样式迁移
+  - [x] 添加会员等级和积分展示
+  - [x] 实现会员进度条组件
+  - [x] 添加版本信息展示
 - [ ] 集成后端API完成功能 (AC: 2)
   - [ ] 创建用户信息API端点
   - [ ] 实现用户头像上传和更新功能
@@ -367,12 +367,27 @@ export default app;
 *此部分由开发代理在实施过程中填写*
 
 ### Agent Model Used
+- Claude Sonnet 4.5 (2025-09-29)
 
 ### Debug Log References
+- 修复 TypeScript 错误:箭头符号语法问题
+- 更新 AvatarUpload 组件以支持 className 属性
+- 按照 mini-demo 样式规范精确迁移所有样式
 
 ### Completion Notes List
+- ✅ 用户信息区域重构为渐变背景样式(#4A90C2 → #357ABD)
+- ✅ 添加会员信息卡片组件,包含积分和累计消费展示
+- ✅ 重构功能菜单为网格布局,添加描述文字
+- ✅ 添加客服与帮助区域,包含联系客服、常见问题、意见反馈
+- ✅ 实现精确的样式迁移,包括颜色、渐变、阴影、圆角、间距
+- ✅ 添加会员等级和积分展示(普通会员,积分:0,累计消费:¥0)
+- ✅ 实现会员进度条组件,显示升级进度
+- ✅ 添加版本信息展示(去看出行 v1.0.0)
+- ✅ 按照 mini-demo 样式规范重新设计退出登录按钮
 
 ### File List
+- [mini/src/pages/profile/index.tsx](mini/src/pages/profile/index.tsx) - 个人中心页面重构
+- [mini/src/components/ui/avatar-upload.tsx](mini/src/components/ui/avatar-upload.tsx) - 更新 AvatarUpload 组件支持 className
 
 ## QA Results
 *此部分由QA代理在审查完成后填写*

+ 9 - 6
mini/src/components/ui/avatar-upload.tsx

@@ -11,14 +11,16 @@ interface AvatarUploadProps {
   onUploadError?: (error: Error) => void
   size?: number
   editable?: boolean
+  className?: string
 }
 
-export function AvatarUpload({ 
-  currentAvatar, 
-  onUploadSuccess, 
+export function AvatarUpload({
+  currentAvatar,
+  onUploadSuccess,
   onUploadError,
   size = 96,
-  editable = true 
+  editable = true,
+  className
 }: AvatarUploadProps) {
   const [uploading, setUploading] = useState(false)
   const [progress, setProgress] = useState(0)
@@ -81,12 +83,13 @@ export function AvatarUpload({
       className="relative inline-block"
       onClick={handleChooseImage}
     >
-      <View 
+      <View
         className={cn(
           "relative overflow-hidden rounded-full",
           "border-4 border-white shadow-lg",
           editable && "cursor-pointer active:scale-95 transition-transform duration-150",
-          uploading && "opacity-75"
+          uploading && "opacity-75",
+          className
         )}
         style={{ width: avatarSize, height: avatarSize }}
       >

+ 93 - 63
mini/src/pages/profile/index.tsx

@@ -176,108 +176,138 @@ const ProfilePage: React.FC = () => {
         onClickRight={handleSettings}
         leftIcon=""
       />
-      <ScrollView className="flex-1 bg-gray-50">
-        {/* 用户信息卡片 */}
-        <View className="bg-white rounded-b-3xl shadow-sm pb-8">
-          <View className="flex flex-col items-center pt-8 pb-6">
-            <View className="relative">
+      <ScrollView className="flex-1 bg-[#F8F9FA] pb-10">
+        {/* 用户信息区域 - 渐变背景 */}
+        <View className="bg-gradient-to-br from-[#4A90C2] to-[#357ABD] p-[40rpx_32rpx_32rpx_32rpx] text-white">
+          <View className="flex items-center">
+            <View className="relative mr-[32rpx]">
               <AvatarUpload
                 currentAvatar={userProfile.avatarFile?.fullUrl}
                 onUploadSuccess={handleAvatarUpload}
                 onUploadError={handleAvatarUploadError}
-                size={96}
+                size={100}
                 editable={!updatingAvatar}
+                className="border-3 border-white/30"
               />
             </View>
-            <Text className="text-xl font-bold text-gray-900 mt-4">{userProfile.username}</Text>
-            {userProfile.email && (
-              <Text className="text-sm text-gray-600 mt-1">{userProfile.email}</Text>
-            )}
-            <View className="flex items-center mt-2">
-              <View className="i-heroicons-calendar-20-solid w-4 h-4 text-gray-400 mr-1" />
-              <Text className="text-xs text-gray-500">
-                注册于 {new Date(userProfile.createdAt).toLocaleDateString('zh-CN')}
+
+            <View className="flex-1">
+              <Text className="text-[32rpx] font-bold text-white">{userProfile.username}</Text>
+              <Text className="text-[22rpx] opacity-80 mt-1">
+                ID: {String(userProfile.id).slice(-4)}
               </Text>
             </View>
           </View>
+        </View>
+
+        {/* 会员信息卡片 */}
+        <View className="m-[24rpx_32rpx]">
+          <View
+            className="bg-gradient-to-br from-[#667eea] to-[#764ba2] rounded-[20rpx] p-[24rpx] text-white shadow-[0_6rpx_24rpx_rgba(102,126,234,0.3)]"
+            onClick={() => Taro.showToast({ title: '会员功能开发中...', icon: 'none' })}
+          >
+            <View className="flex justify-between items-center mb-[20rpx]">
+              <View className="flex items-center">
+                <View className="i-heroicons-star-20-solid text-[32rpx] mr-[10rpx]" />
+                <Text className="text-[32rpx] font-bold">普通会员</Text>
+              </View>
+              <View className="text-[28rpx] opacity-80">{'>'}</View>
+            </View>
 
-          {/* 统计信息 */}
-          <View className="px-6">
-            <View className="grid grid-cols-3 gap-4 text-center">
-              <View className="bg-gray-50 rounded-xl p-4">
-                <Text className="text-2xl font-bold text-blue-500">0</Text>
-                <Text className="text-xs text-gray-600 mt-1">收藏</Text>
+            <View className="flex justify-around">
+              <View className="text-center">
+                <Text className="text-[32rpx] font-bold block mb-[6rpx]">0</Text>
+                <Text className="text-[22rpx] opacity-80">积分</Text>
               </View>
-              <View className="bg-gray-50 rounded-xl p-4">
-                <Text className="text-2xl font-bold text-green-500">0</Text>
-                <Text className="text-xs text-gray-600 mt-1">点赞</Text>
+              <View className="text-center">
+                <Text className="text-[32rpx] font-bold block mb-[6rpx]">¥0</Text>
+                <Text className="text-[22rpx] opacity-80">累计消费</Text>
               </View>
-              <View className="bg-gray-50 rounded-xl p-4">
-                <Text className="text-2xl font-bold text-purple-500">0</Text>
-                <Text className="text-xs text-gray-600 mt-1">关注</Text>
+            </View>
+
+            {/* 会员进度条 */}
+            <View className="bg-white/10 rounded-[16rpx] p-[20rpx] mt-[20rpx]">
+              <Text className="text-[24rpx] opacity-90 mb-[12rpx] text-center">
+                距离升级还需消费 ¥1000
+              </Text>
+              <View className="h-[8rpx] bg-white/20 rounded-[4rpx] overflow-hidden">
+                <View
+                  className="h-full bg-gradient-to-r from-[#FFD700] to-[#FFA500] rounded-[4rpx] transition-all duration-300"
+                  style={{ width: '30%' }}
+                />
               </View>
             </View>
           </View>
         </View>
 
         {/* 功能菜单 */}
-        <View className="px-4 pt-6">
-          <View className="bg-white rounded-2xl shadow-sm overflow-hidden">
+        <View className="m-[24rpx_32rpx]">
+          <Text className="text-[30rpx] font-bold text-[#333] mb-[20rpx]">我的服务</Text>
+          <View className="bg-white rounded-[20rpx] shadow-[0_4rpx_20rpx_rgba(0,0,0,0.08)] border border-[#E5E5EA] overflow-hidden">
             {menuItems.map((item, index) => (
               <View
                 key={index}
-                className="flex items-center px-4 py-4 active:bg-gray-50 transition-colors duration-150"
+                className="flex items-center p-[28rpx_32rpx] border-b-2 border-[#E5E5EA] active:bg-[#F8F9FA] transition-colors duration-300"
                 onClick={item.onClick}
               >
                 <View className={cn("w-6 h-6 mr-3", item.color, item.icon)} />
-                <Text className="flex-1 text-gray-800">{item.title}</Text>
-                <View className="i-heroicons-chevron-right-20-solid w-5 h-5 text-gray-400" />
+                <View className="flex-1 ml-0">
+                  <Text className="text-[30rpx] font-bold text-[#333] block">{item.title}</Text>
+                  <Text className="text-[24rpx] text-[#666] mt-[6rpx]">
+                    {item.title === '编辑资料' && '修改个人信息'}
+                    {item.title === '乘车人管理' && '管理乘车人信息'}
+                    {item.title === '设置' && '应用设置'}
+                    {item.title === '隐私政策' && '查看隐私政策'}
+                    {item.title === '帮助与反馈' && '获取帮助和反馈'}
+                  </Text>
+                </View>
+                <View className="text-[28rpx] text-[#ccc] font-bold">{'>'}</View>
               </View>
             ))}
           </View>
         </View>
 
-        {/* 账号信息 */}
-        <View className="px-4 pt-6">
-          <View className="bg-white rounded-2xl shadow-sm p-4">
-            <Text className="text-sm font-medium text-gray-700 mb-3">账号信息</Text>
-            <View className="space-y-3">
-              <View className="flex justify-between items-center">
-                <Text className="text-sm text-gray-600">用户ID</Text>
-                <Text className="text-sm text-gray-900 font-mono">{userProfile.id}</Text>
-              </View>
-              {userProfile.updatedAt && (
-                <View className="flex justify-between items-center">
-                  <Text className="text-sm text-gray-600">最近登录</Text>
-                  <Text className="text-sm text-gray-900">
-                    {new Date(userProfile.updatedAt).toLocaleString('zh-CN')}
-                  </Text>
+        {/* 客服与帮助区域 */}
+        <View className="m-[24rpx_32rpx]">
+          <Text className="text-[30rpx] font-bold text-[#333] mb-[20rpx]">客服与帮助</Text>
+          <View className="bg-white rounded-[20rpx] shadow-[0_4rpx_20rpx_rgba(0,0,0,0.08)] border border-[#E5E5EA] overflow-hidden">
+            {[
+              { title: '联系客服', desc: '7x24小时在线客服', icon: 'i-heroicons-phone-20-solid', color: 'text-blue-500' },
+              { title: '常见问题', desc: '查看常见问题解答', icon: 'i-heroicons-question-mark-circle-20-solid', color: 'text-green-500' },
+              { title: '意见反馈', desc: '提出宝贵意见', icon: 'i-heroicons-chat-bubble-left-ellipsis-20-solid', color: 'text-orange-500' }
+            ].map((item, index) => (
+              <View
+                key={index}
+                className="flex items-center p-[28rpx_32rpx] border-b-2 border-[#E5E5EA] active:bg-[#F8F9FA] transition-colors duration-300"
+                onClick={() => Taro.showToast({ title: `${item.title}功能开发中...`, icon: 'none' })}
+              >
+                <View className={cn("w-6 h-6 mr-3", item.color, item.icon)} />
+                <View className="flex-1 ml-0">
+                  <Text className="text-[30rpx] font-bold text-[#333] block">{item.title}</Text>
+                  <Text className="text-[24rpx] text-[#666]">{item.desc}</Text>
                 </View>
-              )}
-            </View>
+                <View className="text-[28rpx] text-[#ccc] font-bold">{'>'}</View>
+              </View>
+            ))}
           </View>
         </View>
 
         {/* 退出登录按钮 */}
-        <View className="px-4 pt-6 pb-8">
-          <Button
-            variant="destructive"
-            size="lg"
-            className="w-full"
-            onClick={handleLogout}
-          >
-            <View className="flex items-center justify-center">
-              <View className="i-heroicons-arrow-left-on-rectangle-20-solid w-5 h-5 mr-2" />
-              退出登录
+        <View className="m-[24rpx_32rpx]">
+          <View className="bg-white rounded-[20rpx] shadow-[0_4rpx_20rpx_rgba(0,0,0,0.08)] border border-[#E5E5EA] overflow-hidden">
+            <View
+              className="flex items-center justify-center p-[28rpx_32rpx] active:bg-[#F8F9FA] transition-colors duration-300"
+              onClick={handleLogout}
+            >
+              <View className="i-heroicons-arrow-left-on-rectangle-20-solid w-6 h-6 text-red-500 mr-3" />
+              <Text className="text-[30rpx] font-bold text-red-500">退出登录</Text>
             </View>
-          </Button>
+          </View>
         </View>
 
         {/* 版本信息 */}
-        <View className="pb-8">
-          <Text className="text-center text-xs text-gray-400">
-            v1.0.0 - 小程序版
-          </Text>
+        <View className="text-center py-[32rpx]">
+          <Text className="text-[22rpx] text-[#999]">去看出行 v1.0.0</Text>
         </View>
       </ScrollView>
     </TabBarLayout>