基本功能实现

Change-Id: I4fb8dfa2eed093c13d7c1e4304c4b4d012512ba9
diff --git a/src/views/auth/ProfileView.vue b/src/views/auth/ProfileView.vue
new file mode 100644
index 0000000..e1e2544
--- /dev/null
+++ b/src/views/auth/ProfileView.vue
@@ -0,0 +1,1343 @@
+<template>
+  <div class="profile-page">
+    <div class="page-container">
+      <!-- 个人信息卡片 -->
+      <div class="profile-header">
+        <div class="user-avatar-section">
+          <div class="avatar-container">
+            <el-avatar :size="120" :src="userProfile.avatar">
+              {{ userProfile.username.charAt(0).toUpperCase() }}
+            </el-avatar>
+            <el-button 
+              type="primary" 
+              size="small" 
+              class="change-avatar-btn"
+              @click="showAvatarDialog = true"
+            >
+              更换头像
+            </el-button>
+          </div>
+          
+          <div class="user-basic-info">
+            <h1 class="username">{{ userProfile.username }}</h1>
+            <div class="user-title">
+              <el-tag :type="getUserTitleType(userProfile.userLevel)" size="large">
+                {{ userProfile.userTitle }}
+              </el-tag>
+            </div>
+            <div class="join-info">
+              <el-icon><Calendar /></el-icon>
+              <span>加入时间:{{ formatDate(userProfile.joinDate) }}</span>
+            </div>
+            <div class="last-login">
+              <el-icon><Clock /></el-icon>
+              <span>最后登录:{{ formatTime(userProfile.lastLogin) }}</span>
+            </div>
+          </div>
+        </div>
+        
+        <div class="user-stats-overview">
+          <div class="stats-grid">
+            <div class="stat-card">
+              <div class="stat-icon upload">
+                <el-icon size="32"><Upload /></el-icon>
+              </div>
+              <div class="stat-info">
+                <h3>{{ userProfile.stats.uploaded }}</h3>
+                <p>上传量</p>
+              </div>
+            </div>
+            
+            <div class="stat-card">
+              <div class="stat-icon download">
+                <el-icon size="32"><Download /></el-icon>
+              </div>
+              <div class="stat-info">
+                <h3>{{ userProfile.stats.downloaded }}</h3>
+                <p>下载量</p>
+              </div>
+            </div>
+            
+            <div class="stat-card">
+              <div class="stat-icon ratio" :class="getRatioClass(userProfile.stats.ratio)">
+                <el-icon size="32"><TrendCharts /></el-icon>
+              </div>
+              <div class="stat-info">
+                <h3>{{ userProfile.stats.ratio }}</h3>
+                <p>分享率</p>
+              </div>
+            </div>
+            
+            <div class="stat-card">
+              <div class="stat-icon points">
+                <el-icon size="32"><Star /></el-icon>
+              </div>
+              <div class="stat-info">
+                <h3>{{ userProfile.stats.points }}</h3>
+                <p>积分</p>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 详细信息选项卡 -->
+      <div class="profile-content">
+        <el-tabs v-model="activeTab" type="border-card">
+          <!-- 个人信息 -->
+          <el-tab-pane label="个人信息" name="info">
+            <div class="info-section">
+              <el-form
+                ref="profileFormRef"
+                :model="editProfile"
+                :rules="profileRules"
+                label-width="120px"
+                size="large"
+              >
+                <div class="form-section">
+                  <h3>基本信息</h3>
+                  <el-form-item label="用户名">
+                    <el-input v-model="editProfile.username" disabled>
+                      <template #suffix>
+                        <el-tooltip content="用户名不可修改">
+                          <el-icon><QuestionFilled /></el-icon>
+                        </el-tooltip>
+                      </template>
+                    </el-input>
+                  </el-form-item>
+                  
+                  <el-form-item label="邮箱地址" prop="email">
+                    <el-input v-model="editProfile.email" type="email" />
+                  </el-form-item>
+                  
+                  <el-form-item label="真实姓名" prop="realName">
+                    <el-input v-model="editProfile.realName" placeholder="可选填写" />
+                  </el-form-item>
+                  
+                  <el-form-item label="所在地区">
+                    <el-cascader
+                      v-model="editProfile.location"
+                      :options="locationOptions"
+                      placeholder="请选择地区"
+                      clearable
+                    />
+                  </el-form-item>
+                </div>
+                
+                <div class="form-section">
+                  <h3>个人介绍</h3>
+                  <el-form-item label="个人签名">
+                    <el-input
+                      v-model="editProfile.signature"
+                      type="textarea"
+                      :rows="3"
+                      maxlength="200"
+                      show-word-limit
+                      placeholder="介绍一下自己吧..."
+                    />
+                  </el-form-item>
+                  
+                  <el-form-item label="个人网站">
+                    <el-input v-model="editProfile.website" placeholder="https://" />
+                  </el-form-item>
+                  
+                  <el-form-item label="兴趣爱好">
+                    <div class="interests-input">
+                      <el-tag
+                        v-for="interest in editProfile.interests"
+                        :key="interest"
+                        closable
+                        @close="removeInterest(interest)"
+                        class="interest-tag"
+                      >
+                        {{ interest }}
+                      </el-tag>
+                      <el-input
+                        v-if="interestInputVisible"
+                        ref="interestInputRef"
+                        v-model="interestInputValue"
+                        size="small"
+                        @keyup.enter="addInterest"
+                        @blur="addInterest"
+                        style="width: 120px;"
+                      />
+                      <el-button
+                        v-else
+                        size="small"
+                        @click="showInterestInput"
+                      >
+                        + 添加兴趣
+                      </el-button>
+                    </div>
+                  </el-form-item>
+                </div>
+                
+                <div class="form-section">
+                  <h3>隐私设置</h3>
+                  <el-form-item label="邮箱公开">
+                    <el-switch v-model="editProfile.emailPublic" />
+                    <span class="setting-tip">是否在个人资料中显示邮箱</span>
+                  </el-form-item>
+                  
+                  <el-form-item label="统计公开">
+                    <el-switch v-model="editProfile.statsPublic" />
+                    <span class="setting-tip">是否公开上传下载统计</span>
+                  </el-form-item>
+                  
+                  <el-form-item label="活动记录">
+                    <el-switch v-model="editProfile.activityPublic" />
+                    <span class="setting-tip">是否公开活动记录</span>
+                  </el-form-item>
+                </div>
+                
+                <div class="form-actions">
+                  <el-button @click="resetProfile">重置</el-button>
+                  <el-button type="primary" @click="saveProfile" :loading="saving">
+                    保存修改
+                  </el-button>
+                </div>
+              </el-form>
+            </div>
+          </el-tab-pane>
+          
+          <!-- 数据统计 -->
+          <el-tab-pane label="数据统计" name="stats">
+            <div class="stats-section">
+              <div class="stats-overview">
+                <div class="overview-grid">
+                  <div class="overview-card">
+                    <h3>上传统计</h3>
+                    <div class="stat-details">
+                      <div class="detail-item">
+                        <span class="label">总上传量:</span>
+                        <span class="value">{{ userProfile.detailedStats.totalUploaded }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">上传种子:</span>
+                        <span class="value">{{ userProfile.detailedStats.uploadedTorrents }} 个</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">平均大小:</span>
+                        <span class="value">{{ userProfile.detailedStats.avgUploadSize }}</span>
+                      </div>
+                    </div>
+                  </div>
+                  
+                  <div class="overview-card">
+                    <h3>下载统计</h3>
+                    <div class="stat-details">
+                      <div class="detail-item">
+                        <span class="label">总下载量:</span>
+                        <span class="value">{{ userProfile.detailedStats.totalDownloaded }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">下载种子:</span>
+                        <span class="value">{{ userProfile.detailedStats.downloadedTorrents }} 个</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">完成种子:</span>
+                        <span class="value">{{ userProfile.detailedStats.completedTorrents }} 个</span>
+                      </div>
+                    </div>
+                  </div>
+                  
+                  <div class="overview-card">
+                    <h3>做种统计</h3>
+                    <div class="stat-details">
+                      <div class="detail-item">
+                        <span class="label">正在做种:</span>
+                        <span class="value">{{ userProfile.detailedStats.seeding }} 个</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">做种时间:</span>
+                        <span class="value">{{ userProfile.detailedStats.seedingTime }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">做种排名:</span>
+                        <span class="value">第 {{ userProfile.detailedStats.seedingRank }} 名</span>
+                      </div>
+                    </div>
+                  </div>
+                  
+                  <div class="overview-card">
+                    <h3>积分记录</h3>
+                    <div class="stat-details">
+                      <div class="detail-item">
+                        <span class="label">当前积分:</span>
+                        <span class="value">{{ userProfile.stats.points }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">累计获得:</span>
+                        <span class="value">{{ userProfile.detailedStats.totalEarnedPoints }}</span>
+                      </div>
+                      <div class="detail-item">
+                        <span class="label">累计消费:</span>
+                        <span class="value">{{ userProfile.detailedStats.totalSpentPoints }}</span>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+              
+              <!-- 数据图表 -->
+              <div class="charts-section">
+                <div class="chart-card">
+                  <h3>上传下载趋势</h3>
+                  <div class="chart-placeholder">
+                    <el-icon size="48" color="#e4e7ed"><TrendCharts /></el-icon>
+                    <p>图表功能开发中...</p>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </el-tab-pane>
+          
+          <!-- 我的种子 -->
+          <el-tab-pane label="我的种子" name="torrents">
+            <div class="torrents-section">
+              <div class="section-header">
+                <h3>我上传的种子</h3>
+                <el-button type="primary" :icon="Upload" @click="$router.push('/upload')">
+                  上传新种子
+                </el-button>
+              </div>
+              
+              <el-table :data="userTorrents" stripe>
+                <el-table-column label="种子名称" min-width="300">
+                  <template #default="{ row }">
+                    <div class="torrent-info">
+                      <el-tag :type="getCategoryType(row.category)" size="small">
+                        {{ getCategoryName(row.category) }}
+                      </el-tag>
+                      <span class="torrent-title">{{ row.title }}</span>
+                    </div>
+                  </template>
+                </el-table-column>
+                
+                <el-table-column label="大小" prop="size" width="100" />
+                <el-table-column label="做种" prop="seeders" width="80" align="center" />
+                <el-table-column label="下载" prop="leechers" width="80" align="center" />
+                <el-table-column label="完成" prop="downloads" width="80" align="center" />
+                <el-table-column label="上传时间" width="120">
+                  <template #default="{ row }">
+                    {{ formatDate(row.uploadTime) }}
+                  </template>
+                </el-table-column>
+                
+                <el-table-column label="操作" width="120" align="center">
+                  <template #default="{ row }">
+                    <el-button 
+                      type="primary" 
+                      size="small" 
+                      @click="$router.push(`/torrent/${row.id}`)"
+                    >
+                      查看
+                    </el-button>
+                  </template>
+                </el-table-column>
+              </el-table>
+              
+              <div class="pagination-wrapper">
+                <el-pagination
+                  v-model:current-page="torrentsPage"
+                  :page-size="10"
+                  :total="userTorrents.length"
+                  layout="prev, pager, next"
+                  small
+                />
+              </div>
+            </div>
+          </el-tab-pane>
+          
+          <!-- 活动记录 -->
+          <el-tab-pane label="活动记录" name="activity">
+            <div class="activity-section">
+              <div class="activity-filters">
+                <el-select v-model="activityFilter" placeholder="活动类型">
+                  <el-option label="全部活动" value="" />
+                  <el-option label="上传种子" value="upload" />
+                  <el-option label="下载种子" value="download" />
+                  <el-option label="论坛发帖" value="post" />
+                  <el-option label="积分变动" value="points" />
+                </el-select>
+              </div>
+              
+              <div class="activity-timeline">
+                <el-timeline>
+                  <el-timeline-item
+                    v-for="activity in filteredActivities"
+                    :key="activity.id"
+                    :timestamp="formatTime(activity.time)"
+                    :type="getActivityType(activity.type)"
+                  >
+                    <div class="activity-content">
+                      <div class="activity-header">
+                        <el-icon>
+                          <component :is="getActivityIcon(activity.type)" />
+                        </el-icon>
+                        <span class="activity-title">{{ activity.title }}</span>
+                      </div>
+                      <div class="activity-description">{{ activity.description }}</div>
+                    </div>
+                  </el-timeline-item>
+                </el-timeline>
+              </div>
+            </div>
+          </el-tab-pane>
+          
+          <!-- 安全设置 -->
+          <el-tab-pane label="安全设置" name="security">
+            <div class="security-section">
+              <div class="security-card">
+                <h3>修改密码</h3>
+                <el-form
+                  ref="passwordFormRef"
+                  :model="passwordForm"
+                  :rules="passwordRules"
+                  label-width="120px"
+                >
+                  <el-form-item label="当前密码" prop="currentPassword">
+                    <el-input
+                      v-model="passwordForm.currentPassword"
+                      type="password"
+                      show-password
+                      placeholder="请输入当前密码"
+                    />
+                  </el-form-item>
+                  
+                  <el-form-item label="新密码" prop="newPassword">
+                    <el-input
+                      v-model="passwordForm.newPassword"
+                      type="password"
+                      show-password
+                      placeholder="请输入新密码"
+                    />
+                  </el-form-item>
+                  
+                  <el-form-item label="确认密码" prop="confirmPassword">
+                    <el-input
+                      v-model="passwordForm.confirmPassword"
+                      type="password"
+                      show-password
+                      placeholder="请再次输入新密码"
+                    />
+                  </el-form-item>
+                  
+                  <el-form-item>
+                    <el-button type="primary" @click="changePassword" :loading="changingPassword">
+                      修改密码
+                    </el-button>
+                  </el-form-item>
+                </el-form>
+              </div>
+              
+              <div class="security-card">
+                <h3>登录记录</h3>
+                <el-table :data="loginHistory" stripe>
+                  <el-table-column label="登录时间" width="180">
+                    <template #default="{ row }">
+                      {{ formatDateTime(row.time) }}
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="IP地址" prop="ip" width="150" />
+                  <el-table-column label="设备信息" prop="device" />
+                  <el-table-column label="登录结果" width="100">
+                    <template #default="{ row }">
+                      <el-tag :type="row.success ? 'success' : 'danger'" size="small">
+                        {{ row.success ? '成功' : '失败' }}
+                      </el-tag>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </div>
+            </div>
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+    </div>
+
+    <!-- 更换头像对话框 -->
+    <el-dialog v-model="showAvatarDialog" title="更换头像" width="400px">
+      <div class="avatar-upload">
+        <el-upload
+          ref="avatarUploadRef"
+          :auto-upload="false"
+          :limit="1"
+          accept="image/*"
+          :on-change="handleAvatarChange"
+          list-type="picture-card"
+          class="avatar-uploader"
+        >
+          <el-icon><Plus /></el-icon>
+        </el-upload>
+        <div class="upload-tips">
+          <p>支持 JPG、PNG 格式</p>
+          <p>建议尺寸 200x200 像素</p>
+          <p>文件大小不超过 2MB</p>
+        </div>
+      </div>
+      
+      <template #footer>
+        <el-button @click="showAvatarDialog = false">取消</el-button>
+        <el-button type="primary" @click="uploadAvatar" :loading="uploadingAvatar">
+          上传头像
+        </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { ref, reactive, computed, onMounted, nextTick } from 'vue'
+import { useRouter } from 'vue-router'
+import { ElMessage } from 'element-plus'
+import {
+  Calendar,
+  Clock,
+  Upload,
+  Download,
+  TrendCharts,
+  Star,
+  QuestionFilled,
+  Plus,
+  ChatDotRound,
+  Flag,
+  Coin
+} from '@element-plus/icons-vue'
+
+export default {
+  name: 'ProfileView',
+  setup() {
+    const router = useRouter()
+    const profileFormRef = ref(null)
+    const passwordFormRef = ref(null)
+    const avatarUploadRef = ref(null)
+    const interestInputRef = ref(null)
+    
+    const activeTab = ref('info')
+    const showAvatarDialog = ref(false)
+    const saving = ref(false)
+    const changingPassword = ref(false)
+    const uploadingAvatar = ref(false)
+    const interestInputVisible = ref(false)
+    const interestInputValue = ref('')
+    const activityFilter = ref('')
+    const torrentsPage = ref(1)
+    
+    const userProfile = ref({
+      username: 'MovieExpert',
+      email: 'movieexpert@example.com',
+      realName: '',
+      avatar: '',
+      userLevel: 5,
+      userTitle: '资深会员',
+      joinDate: '2023-01-15T10:00:00',
+      lastLogin: '2025-06-03T14:30:00',
+      location: ['北京市', '朝阳区'],
+      signature: '热爱电影,分享快乐!',
+      website: 'https://movieblog.com',
+      interests: ['电影', '音乐', '科技', '摄影'],
+      emailPublic: false,
+      statsPublic: true,
+      activityPublic: true,
+      stats: {
+        uploaded: '256.8 GB',
+        downloaded: '89.6 GB',
+        ratio: '2.87',
+        points: '15,680'
+      },
+      detailedStats: {
+        totalUploaded: '256.8 GB',
+        uploadedTorrents: 45,
+        avgUploadSize: '5.7 GB',
+        totalDownloaded: '89.6 GB',
+        downloadedTorrents: 123,
+        completedTorrents: 118,
+        seeding: 32,
+        seedingTime: '1,245 小时',
+        seedingRank: 86,
+        totalEarnedPoints: '28,940',
+        totalSpentPoints: '13,260'
+      }
+    })
+    
+    const editProfile = reactive({
+      username: '',
+      email: '',
+      realName: '',
+      location: [],
+      signature: '',
+      website: '',
+      interests: [],
+      emailPublic: false,
+      statsPublic: true,
+      activityPublic: true
+    })
+    
+    const passwordForm = reactive({
+      currentPassword: '',
+      newPassword: '',
+      confirmPassword: ''
+    })
+    
+    const profileRules = {
+      email: [
+        { required: true, message: '请输入邮箱地址', trigger: 'blur' },
+        { type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }
+      ]
+    }
+    
+    const passwordRules = {
+      currentPassword: [
+        { required: true, message: '请输入当前密码', trigger: 'blur' }
+      ],
+      newPassword: [
+        { required: true, message: '请输入新密码', trigger: 'blur' },
+        { min: 6, message: '密码长度至少6个字符', trigger: 'blur' }
+      ],
+      confirmPassword: [
+        { required: true, message: '请确认新密码', trigger: 'blur' },
+        {
+          validator: (rule, value, callback) => {
+            if (value !== passwordForm.newPassword) {
+              callback(new Error('两次输入的密码不一致'))
+            } else {
+              callback()
+            }
+          },
+          trigger: 'blur'
+        }
+      ]
+    }
+    
+    const locationOptions = [
+      {
+        value: '北京市',
+        label: '北京市',
+        children: [
+          { value: '朝阳区', label: '朝阳区' },
+          { value: '海淀区', label: '海淀区' },
+          { value: '丰台区', label: '丰台区' }
+        ]
+      },
+      {
+        value: '上海市',
+        label: '上海市',
+        children: [
+          { value: '浦东新区', label: '浦东新区' },
+          { value: '黄浦区', label: '黄浦区' },
+          { value: '静安区', label: '静安区' }
+        ]
+      }
+    ]
+    
+    const userTorrents = ref([
+      {
+        id: 1,
+        title: '[4K蓝光原盘] 阿凡达:水之道',
+        category: 'movie',
+        size: '85.6 GB',
+        seeders: 45,
+        leechers: 12,
+        downloads: 234,
+        uploadTime: '2025-05-15T10:00:00'
+      },
+      {
+        id: 2,
+        title: '[FLAC] 古典音乐合集',
+        category: 'music',
+        size: '2.3 GB',
+        seeders: 23,
+        leechers: 5,
+        downloads: 89,
+        uploadTime: '2025-04-20T15:30:00'
+      }
+    ])
+    
+    const activities = ref([
+      {
+        id: 1,
+        type: 'upload',
+        title: '上传种子',
+        description: '上传了《阿凡达:水之道》4K蓝光原盘',
+        time: '2025-06-03T10:30:00'
+      },
+      {
+        id: 2,
+        type: 'download',
+        title: '下载种子',
+        description: '下载了《星际穿越》IMAX版本',
+        time: '2025-06-02T14:20:00'
+      },
+      {
+        id: 3,
+        type: 'post',
+        title: '发布主题',
+        description: '在电影讨论区发布了新主题',
+        time: '2025-06-01T16:45:00'
+      },
+      {
+        id: 4,
+        type: 'points',
+        title: '积分变动',
+        description: '做种奖励获得 +50 积分',
+        time: '2025-05-31T09:15:00'
+      }
+    ])
+    
+    const loginHistory = ref([
+      {
+        time: '2025-06-03T14:30:00',
+        ip: '192.168.1.100',
+        device: 'Windows 11 / Chrome 120',
+        success: true
+      },
+      {
+        time: '2025-06-02T09:15:00',
+        ip: '192.168.1.100',
+        device: 'Windows 11 / Chrome 120',
+        success: true
+      },
+      {
+        time: '2025-06-01T22:30:00',
+        ip: '192.168.1.100',
+        device: 'Android / Chrome Mobile',
+        success: true
+      }
+    ])
+    
+    const filteredActivities = computed(() => {
+      if (!activityFilter.value) return activities.value
+      return activities.value.filter(activity => activity.type === activityFilter.value)
+    })
+    
+    onMounted(() => {
+      loadUserProfile()
+    })
+    
+    const loadUserProfile = () => {
+      // 加载用户资料到编辑表单
+      Object.assign(editProfile, {
+        username: userProfile.value.username,
+        email: userProfile.value.email,
+        realName: userProfile.value.realName,
+        location: userProfile.value.location,
+        signature: userProfile.value.signature,
+        website: userProfile.value.website,
+        interests: [...userProfile.value.interests],
+        emailPublic: userProfile.value.emailPublic,
+        statsPublic: userProfile.value.statsPublic,
+        activityPublic: userProfile.value.activityPublic
+      })
+    }
+    
+    const formatDate = (dateString) => {
+      const date = new Date(dateString)
+      return date.toLocaleDateString('zh-CN')
+    }
+    
+    const formatTime = (timeString) => {
+      const date = new Date(timeString)
+      const now = new Date()
+      const diff = now - date
+      const hours = Math.floor(diff / (1000 * 60 * 60))
+      
+      if (hours < 1) return '刚刚'
+      if (hours < 24) return `${hours}小时前`
+      const days = Math.floor(hours / 24)
+      if (days < 7) return `${days}天前`
+      
+      return date.toLocaleDateString('zh-CN')
+    }
+    
+    const formatDateTime = (dateString) => {
+      const date = new Date(dateString)
+      return date.toLocaleString('zh-CN')
+    }
+    
+    const getUserTitleType = (level) => {
+      if (level >= 8) return 'danger'  // 管理员
+      if (level >= 6) return 'warning' // 资深会员
+      if (level >= 4) return 'success' // 正式会员
+      if (level >= 2) return 'info'    // 初级会员
+      return 'default' // 新手
+    }
+    
+    const getRatioClass = (ratio) => {
+      const r = parseFloat(ratio)
+      if (r >= 2) return 'excellent'
+      if (r >= 1) return 'good'
+      return 'warning'
+    }
+    
+    const getCategoryType = (category) => {
+      const types = {
+        'movie': 'primary',
+        'tv': 'info',
+        'music': 'success',
+        'software': 'warning',
+        'game': 'danger'
+      }
+      return types[category] || 'default'
+    }
+    
+    const getCategoryName = (category) => {
+      const names = {
+        'movie': '电影',
+        'tv': '电视剧',
+        'music': '音乐',
+        'software': '软件',
+        'game': '游戏'
+      }
+      return names[category] || category
+    }
+    
+    const getActivityType = (type) => {
+      const types = {
+        'upload': 'success',
+        'download': 'primary',
+        'post': 'warning',
+        'points': 'info'
+      }
+      return types[type] || 'primary'
+    }
+    
+    const getActivityIcon = (type) => {
+      const icons = {
+        'upload': 'Upload',
+        'download': 'Download',
+        'post': 'ChatDotRound',
+        'points': 'Coin'
+      }
+      return icons[type] || 'Star'
+    }
+    
+    const showInterestInput = () => {
+      interestInputVisible.value = true
+      nextTick(() => {
+        interestInputRef.value?.focus()
+      })
+    }
+    
+    const addInterest = () => {
+      const interest = interestInputValue.value.trim()
+      if (interest && !editProfile.interests.includes(interest)) {
+        editProfile.interests.push(interest)
+      }
+      interestInputVisible.value = false
+      interestInputValue.value = ''
+    }
+    
+    const removeInterest = (interest) => {
+      const index = editProfile.interests.indexOf(interest)
+      if (index > -1) {
+        editProfile.interests.splice(index, 1)
+      }
+    }
+    
+    const saveProfile = async () => {
+      try {
+        await profileFormRef.value?.validate()
+        
+        saving.value = true
+        
+        // 模拟保存过程
+        await new Promise(resolve => setTimeout(resolve, 1500))
+        
+        // 更新用户资料
+        Object.assign(userProfile.value, editProfile)
+        
+        ElMessage.success('个人资料保存成功')
+        
+      } catch (error) {
+        console.error('表单验证失败:', error)
+      } finally {
+        saving.value = false
+      }
+    }
+    
+    const resetProfile = () => {
+      loadUserProfile()
+      ElMessage.info('已重置为原始数据')
+    }
+    
+    const changePassword = async () => {
+      try {
+        await passwordFormRef.value?.validate()
+        
+        changingPassword.value = true
+        
+        // 模拟密码修改过程
+        await new Promise(resolve => setTimeout(resolve, 1500))
+        
+        // 重置表单
+        passwordFormRef.value?.resetFields()
+        Object.assign(passwordForm, {
+          currentPassword: '',
+          newPassword: '',
+          confirmPassword: ''
+        })
+        
+        ElMessage.success('密码修改成功')
+        
+      } catch (error) {
+        console.error('表单验证失败:', error)
+      } finally {
+        changingPassword.value = false
+      }
+    }
+    
+    const handleAvatarChange = (file) => {
+      const isImage = file.raw.type.startsWith('image/')
+      const isLt2M = file.raw.size / 1024 / 1024 < 2
+      
+      if (!isImage) {
+        ElMessage.error('只能上传图片文件!')
+        return false
+      }
+      if (!isLt2M) {
+        ElMessage.error('图片大小不能超过 2MB!')
+        return false
+      }
+      
+      return true
+    }
+    
+    const uploadAvatar = async () => {
+      const files = avatarUploadRef.value?.uploadFiles
+      if (!files || files.length === 0) {
+        ElMessage.warning('请选择头像文件')
+        return
+      }
+      
+      uploadingAvatar.value = true
+      try {
+        // 模拟上传过程
+        await new Promise(resolve => setTimeout(resolve, 2000))
+        
+        // 更新头像URL
+        userProfile.value.avatar = URL.createObjectURL(files[0].raw)
+        
+        ElMessage.success('头像上传成功')
+        showAvatarDialog.value = false
+        avatarUploadRef.value?.clearFiles()
+        
+      } catch (error) {
+        ElMessage.error('头像上传失败')
+      } finally {
+        uploadingAvatar.value = false
+      }
+    }
+    
+    return {
+      activeTab,
+      showAvatarDialog,
+      saving,
+      changingPassword,
+      uploadingAvatar,
+      interestInputVisible,
+      interestInputValue,
+      activityFilter,
+      torrentsPage,
+      userProfile,
+      editProfile,
+      passwordForm,
+      profileRules,
+      passwordRules,
+      locationOptions,
+      userTorrents,
+      filteredActivities,
+      loginHistory,
+      profileFormRef,
+      passwordFormRef,
+      avatarUploadRef,
+      interestInputRef,
+      formatDate,
+      formatTime,
+      formatDateTime,
+      getUserTitleType,
+      getRatioClass,
+      getCategoryType,
+      getCategoryName,
+      getActivityType,
+      getActivityIcon,
+      showInterestInput,
+      addInterest,
+      removeInterest,
+      saveProfile,
+      resetProfile,
+      changePassword,
+      handleAvatarChange,
+      uploadAvatar,
+      Calendar,
+      Clock,
+      Upload,
+      Download,
+      TrendCharts,
+      Star,
+      QuestionFilled,
+      Plus,
+      ChatDotRound,
+      Flag,
+      Coin
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.profile-page {
+  max-width: 1200px;
+  margin: 0 auto;
+  padding: 24px;
+  background: #f5f5f5;
+  min-height: 100vh;
+}
+
+.profile-header {
+  background: #fff;
+  border-radius: 12px;
+  padding: 32px;
+  margin-bottom: 24px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+  
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 32px;
+  
+  .user-avatar-section {
+    display: flex;
+    gap: 24px;
+    
+    .avatar-container {
+      text-align: center;
+      
+      .change-avatar-btn {
+        margin-top: 12px;
+      }
+    }
+    
+    .user-basic-info {
+      flex: 1;
+      
+      .username {
+        font-size: 28px;
+        font-weight: 600;
+        color: #2c3e50;
+        margin: 0 0 12px 0;
+      }
+      
+      .user-title {
+        margin-bottom: 16px;
+      }
+      
+      .join-info, .last-login {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        font-size: 14px;
+        color: #7f8c8d;
+        margin-bottom: 8px;
+      }
+    }
+  }
+  
+  .user-stats-overview {
+    .stats-grid {
+      display: grid;
+      grid-template-columns: repeat(2, 1fr);
+      gap: 16px;
+      
+      .stat-card {
+        background: #f8f9fa;
+        border-radius: 8px;
+        padding: 20px;
+        display: flex;
+        align-items: center;
+        gap: 16px;
+        
+        .stat-icon {
+          width: 48px;
+          height: 48px;
+          border-radius: 50%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          
+          &.upload { background: rgba(103, 194, 58, 0.1); color: #67c23a; }
+          &.download { background: rgba(64, 158, 255, 0.1); color: #409eff; }
+          &.ratio {
+            &.excellent { background: rgba(103, 194, 58, 0.1); color: #67c23a; }
+            &.good { background: rgba(230, 162, 60, 0.1); color: #e6a23c; }
+            &.warning { background: rgba(245, 108, 108, 0.1); color: #f56c6c; }
+          }
+          &.points { background: rgba(245, 108, 108, 0.1); color: #f56c6c; }
+        }
+        
+        .stat-info {
+          h3 {
+            font-size: 20px;
+            font-weight: 600;
+            color: #2c3e50;
+            margin: 0 0 4px 0;
+          }
+          
+          p {
+            font-size: 14px;
+            color: #7f8c8d;
+            margin: 0;
+          }
+        }
+      }
+    }
+  }
+}
+
+.profile-content {
+  background: #fff;
+  border-radius: 12px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
+  
+  :deep(.el-tabs__content) {
+    padding: 24px;
+  }
+}
+
+.info-section {
+  .form-section {
+    margin-bottom: 32px;
+    
+    h3 {
+      font-size: 18px;
+      font-weight: 600;
+      color: #2c3e50;
+      margin: 0 0 20px 0;
+      padding-bottom: 8px;
+      border-bottom: 2px solid #f0f0f0;
+    }
+  }
+  
+  .interests-input {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8px;
+    align-items: center;
+    
+    .interest-tag {
+      margin: 0;
+    }
+  }
+  
+  .setting-tip {
+    margin-left: 12px;
+    font-size: 12px;
+    color: #909399;
+  }
+  
+  .form-actions {
+    text-align: center;
+    margin-top: 32px;
+    
+    .el-button {
+      margin: 0 8px;
+      min-width: 100px;
+    }
+  }
+}
+
+.stats-section {
+  .stats-overview {
+    margin-bottom: 32px;
+    
+    .overview-grid {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+      gap: 20px;
+      
+      .overview-card {
+        background: #f8f9fa;
+        border-radius: 8px;
+        padding: 24px;
+        
+        h3 {
+          font-size: 16px;
+          font-weight: 600;
+          color: #2c3e50;
+          margin: 0 0 16px 0;
+        }
+        
+        .stat-details {
+          .detail-item {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            margin-bottom: 12px;
+            
+            .label {
+              font-size: 14px;
+              color: #7f8c8d;
+            }
+            
+            .value {
+              font-size: 14px;
+              font-weight: 600;
+              color: #2c3e50;
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  .charts-section {
+    .chart-card {
+      background: #f8f9fa;
+      border-radius: 8px;
+      padding: 24px;
+      
+      h3 {
+        font-size: 16px;
+        font-weight: 600;
+        color: #2c3e50;
+        margin: 0 0 20px 0;
+      }
+      
+      .chart-placeholder {
+        text-align: center;
+        padding: 60px 0;
+        color: #909399;
+        
+        p {
+          margin: 12px 0 0 0;
+        }
+      }
+    }
+  }
+}
+
+.torrents-section {
+  .section-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20px;
+    
+    h3 {
+      font-size: 18px;
+      font-weight: 600;
+      color: #2c3e50;
+      margin: 0;
+    }
+  }
+  
+  .torrent-info {
+    display: flex;
+    align-items: center;
+    gap: 12px;
+    
+    .torrent-title {
+      font-weight: 500;
+    }
+  }
+  
+  .pagination-wrapper {
+    margin-top: 16px;
+    text-align: center;
+  }
+}
+
+.activity-section {
+  .activity-filters {
+    margin-bottom: 24px;
+    
+    .el-select {
+      width: 150px;
+    }
+  }
+  
+  .activity-timeline {
+    .activity-content {
+      .activity-header {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+        margin-bottom: 8px;
+        
+        .activity-title {
+          font-weight: 600;
+          color: #2c3e50;
+        }
+      }
+      
+      .activity-description {
+        font-size: 14px;
+        color: #7f8c8d;
+        line-height: 1.5;
+      }
+    }
+  }
+}
+
+.security-section {
+  .security-card {
+    background: #f8f9fa;
+    border-radius: 8px;
+    padding: 24px;
+    margin-bottom: 24px;
+    
+    h3 {
+      font-size: 18px;
+      font-weight: 600;
+      color: #2c3e50;
+      margin: 0 0 20px 0;
+    }
+  }
+}
+
+.avatar-upload {
+  text-align: center;
+  
+  .avatar-uploader {
+    margin-bottom: 16px;
+  }
+  
+  .upload-tips {
+    font-size: 12px;
+    color: #909399;
+    
+    p {
+      margin: 4px 0;
+    }
+  }
+}
+
+@media (max-width: 768px) {
+  .profile-page {
+    padding: 16px;
+  }
+  
+  .profile-header {
+    grid-template-columns: 1fr;
+    gap: 24px;
+    
+    .user-avatar-section {
+      flex-direction: column;
+      text-align: center;
+    }
+    
+    .user-stats-overview .stats-grid {
+      grid-template-columns: 1fr;
+    }
+  }
+  
+  .stats-overview .overview-grid {
+    grid-template-columns: 1fr;
+  }
+  
+  .torrents-section .section-header {
+    flex-direction: column;
+    gap: 16px;
+    align-items: flex-start;
+  }
+}
+</style>
\ No newline at end of file