Преглед изворни кода

feat(mine): 实现真实统计数据和用户列表功能

- 移除随机生成的统计数据,改为调用真实API获取数据
- 添加获取喜欢我的用户真实数量功能
- 重构喜欢我的用户页面,实现真实用户列表加载
- 添加年龄计算、教育程度和薪资范围转换功能
- 优化用户列表界面样式和响应式布局
- 添加地理位置信息显示功能
- 实现用户详情页面跳转功能
- 后端增加地理位置名称字段映射
李思佳 пре 4 дана
родитељ
комит
8b6e9bb1c7

+ 33 - 7
LiangZhiYUMao/pages/mine/index.vue

@@ -409,13 +409,16 @@ export default {
         // 设置统计数据
         this.stats = {
           myConnection: 0,
-          myFavorites: Math.floor(Math.random() * 15) + 5,
-          whoViewedMe: Math.floor(Math.random() * 12) + 3,
-          iViewed: Math.floor(Math.random() * 20) + 8
+          myFavorites: 0,
+          whoViewedMe: 0,
+          iViewed: 0
         }
 
         // 获取我喜欢的用户真实数量
         this.getLikedUsersCount()
+        
+        // 获取喜欢我的用户真实数量
+        this.getLikedMeUsersCount()
 
         // 立即加载最新用户信息(不延迟)
         this.loadUserInfo()
@@ -1224,11 +1227,15 @@ export default {
      
       // 设置统计数据
       this.stats = {
-        myConnection: Math.floor(Math.random() * 10),
-        myFavorites: Math.floor(Math.random() * 20),
-        whoViewedMe: Math.floor(Math.random() * 15),
-        iViewed: Math.floor(Math.random() * 25)
+        myConnection: 0,
+        myFavorites: 0,
+        whoViewedMe: 0,
+        iViewed: 0
       }
+      
+      // 获取真实数量
+      this.getLikedUsersCount()
+      this.getLikedMeUsersCount()
 
       // 显示提示
       uni.showToast({
@@ -1282,6 +1289,25 @@ export default {
       }
     },
     
+    // 获取喜欢我的用户真实数量
+    async getLikedMeUsersCount() {
+      try {
+        const userId = this.currentUserId
+        if (userId) {
+          // 调用后端API获取真实的喜欢我的用户数量
+          const result = await api.recommend.getLikedMeUsersCount(userId)
+          // 确保获取的是数字值
+          const count = typeof result === 'number' ? result : (result.data || 0)
+          // 更新统计数据
+          this.stats.myFavorites = count || 0
+        }
+      } catch (error) {
+        console.error('获取喜欢我的用户数量失败:', error)
+        // 如果获取失败,使用默认值
+        this.stats.myFavorites = 0
+      }
+    },
+    
     goToLikedMe() {
       // 跳转到喜欢我的页面
       uni.navigateTo({

+ 166 - 270
LiangZhiYUMao/pages/mine/liked-me.vue

@@ -1,11 +1,13 @@
 <template>
   <view class="like-page">
-    <view v-if="loading && users.length === 0" class="loading-container">
+    <!-- 数据加载状态 -->
+    <view class="loading-container" v-if="loading">
       <uni-loading type="circle" color="#E91E63"></uni-loading>
       <text class="loading-text">加载中...</text>
     </view>
     
-    <view v-else-if="users.length === 0" class="empty-container">
+    <!-- 空数据状态 -->
+    <view class="empty-container" v-else-if="users.length === 0">
       <text class="empty-icon">😊</text>
       <text class="empty-text">暂无用户喜欢您</text>
       <view class="empty-action">
@@ -13,9 +15,10 @@
       </view>
     </view>
     
-    <scroll-view v-else class="user-list" scroll-y="true" @scrolltolower="loadMoreUsers">
+    <!-- 用户列表 -->
+    <scroll-view class="user-list" scroll-y="true">
       <view class="user-item" v-for="user in users" :key="user.userId" @click="goUserDetail(user.userId)">
-        <image class="user-avatar" :src="user.avatar" mode="aspectFill" @error="handleAvatarError(user)"></image>
+        <image class="user-avatar" :src="user.avatar" mode="aspectFill"></image>
         <view class="user-info">
           <view class="user-basic">
             <text class="user-nickname">{{ user.nickname }}</text>
@@ -31,18 +34,6 @@
             <text class="location-text">{{ user.location }}</text>
           </view>
         </view>
-        <view class="like-status">
-          <text class="like-icon">💖</text>
-        </view>
-      </view>
-      
-      <view v-if="loadingMore" class="loading-more">
-        <uni-loading type="circle" color="#E91E63" size="20"></uni-loading>
-        <text class="loading-more-text">加载更多...</text>
-      </view>
-      
-      <view v-else-if="!hasMore && users.length > 0" class="no-more">
-        <text class="no-more-text">没有更多了</text>
       </view>
     </scroll-view>
   </view>
@@ -51,220 +42,139 @@
 <script>
 import api from '@/utils/api.js'
 
-const EDUCATION_MAP = {
-  1: '初中',
-  2: '高中',
-  3: '大专',
-  4: '本科',
-  5: '硕士',
-  6: '博士'
-}
-
-const SALARY_MAP = {
-  1: '3k以下',
-  2: '3k-5k',
-  3: '5k-8k',
-  4: '8k-12k',
-  5: '12k-20k',
-  6: '20k-50k',
-  7: '50k以上'
-}
-
 export default {
   data() {
     return {
       users: [],
       loading: true,
-      loadingMore: false,
       pageNum: 1,
       pageSize: 20,
-      hasMore: true,
-      currentUserId: null
+      hasMore: true
     }
   },
   onLoad() {
-    this.getCurrentUserId()
-  },
-  onShow() {
-    if (!this.users.length || this.users.length === 0) {
-      this.loadUsers()
-    }
+    this.loadUsers()
   },
   methods: {
-    getCurrentUserId() {
+    // 计算年龄
+    calculateAge(birthDate) {
+      console.log('calculateAge方法被调用,birthDate:', birthDate, '类型:', typeof birthDate)
+      if (!birthDate) {
+        console.log('birthDate为空,返回0')
+        return 0
+      }
       try {
-        const userInfo = uni.getStorageSync('userInfo')
-        if (userInfo) {
-          this.currentUserId = userInfo.userId || userInfo.id
+        const birth = new Date(birthDate)
+        console.log('转换后的Date对象:', birth, '时间戳:', birth.getTime())
+        // 检查日期是否有效
+        if (isNaN(birth.getTime())) {
+          console.log('日期无效,返回0')
+          return 0
         }
-        if (!this.currentUserId) {
-          const token = uni.getStorageSync('token')
-          if (token) {
-            console.warn('无法获取当前用户ID,请检查登录状态')
-          }
+        const now = new Date()
+        let age = now.getFullYear() - birth.getFullYear()
+        const monthDiff = now.getMonth() - birth.getMonth()
+        if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < birth.getDate())) {
+          age--
         }
-      } catch (e) {
-        console.error('获取用户信息失败:', e)
+        console.log('计算出的年龄:', age)
+        return age
+      } catch (error) {
+        console.error('计算年龄失败:', error)
+        return 0
       }
     },
     
-    transformUserData(user) {
-      if (!user) return null
-      
-      const birthDate = user.birthDate || user.birth_date
-      let age = ''
-      if (birthDate) {
-        try {
-          const birth = new Date(birthDate)
-          if (!isNaN(birth.getTime())) {
-            const today = new Date()
-            let userAge = today.getFullYear() - birth.getFullYear()
-            const monthDiff = today.getMonth() - birth.getMonth()
-            if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
-              userAge--
-            }
-            age = userAge > 0 ? userAge : ''
-          }
-        } catch (e) {
-          console.error('计算年龄失败:', e)
-        }
-      }
-      
-      let educationText = ''
-      if (user.educationLevel !== undefined && user.educationLevel !== null) {
-        educationText = EDUCATION_MAP[user.educationLevel] || ''
-      }
-      
-      let salaryText = ''
-      if (user.salaryRange !== undefined && user.salaryRange !== null) {
-        salaryText = SALARY_MAP[user.salaryRange] || ''
-      }
-      
-      let location = ''
-      if (user.cityId || user.provinceId) {
-        location = this.formatLocation(user.provinceId, user.cityId)
-      }
-      
-      return {
-        userId: user.userId || user.user_id,
-        nickname: user.nickname || '匿名用户',
-        avatar: user.avatarUrl || user.avatar_url || user.avatar || '/static/default-avatar.png',
-        age: age,
-        height: user.height || '',
-        weight: user.weight || '',
-        educationText: educationText,
-        salaryText: salaryText,
-        location: location,
-        gender: user.gender
+    // 获取教育程度文本
+    getEducationText(level) {
+      const educationMap = {
+        1: '小学',
+        2: '初中',
+        3: '高中/中专',
+        4: '大专',
+        5: '本科',
+        6: '硕士',
+        7: '博士',
+        8: '博士后'
       }
+      return educationMap[level] || ''
     },
     
-    formatLocation(provinceId, cityId) {
-      if (!provinceId && !cityId) return ''
-      const locationMap = this.$const.LOCATION_MAP
-      if (locationMap && locationMap.length > 0) {
-        try {
-          const province = locationMap.find(p => p.id === provinceId)
-          const city = province && province.children ? province.children.find(c => c.id === cityId) : null
-          if (city) {
-            return city.name || ''
-          }
-        } catch (e) {
-          console.error('获取位置信息失败:', e)
-        }
+    // 获取薪资范围文本
+    getSalaryText(range) {
+      const salaryMap = {
+        1: '3k以下',
+        2: '3k-5k',
+        3: '5k-8k',
+        4: '8k-12k',
+        5: '12k-15k',
+        6: '15k-20k',
+        7: '20k-30k',
+        8: '30k-50k',
+        9: '50k以上'
       }
-      return ''
+      return salaryMap[range] || ''
     },
     
+    // 加载喜欢我的用户列表
     async loadUsers() {
-      if (!this.currentUserId) {
-        this.getCurrentUserId()
-        if (!this.currentUserId) {
+      try {
+        this.loading = true
+        // 获取当前用户ID
+        const userInfo = uni.getStorageSync('userInfo')
+        if (!userInfo || !userInfo.userId) {
           uni.showToast({
             title: '请先登录',
             icon: 'none'
           })
-          this.loading = false
           return
         }
-      }
-      
-      try {
-        this.loading = true
-        const res = await api.recommend.getLikedMeUsers(this.currentUserId, this.pageSize, 0)
         
+        // 调用API获取喜欢我的用户列表
+        const res = await api.recommend.getLikedMeUsers(
+          userInfo.userId,
+          this.pageSize,
+          (this.pageNum - 1) * this.pageSize
+        )
+        
+        // 处理返回数据,转换为前端需要的格式
         if (res && Array.isArray(res)) {
-          this.users = res.map(user => this.transformUserData(user))
-          this.hasMore = res.length >= this.pageSize
+          this.users = res.map(user => {
+            console.log('原始用户数据:', user)
+            return {
+              userId: user.userId,
+              nickname: user.nickname || '',
+              avatar: user.avatarUrl || '',
+              age: this.calculateAge(user.birthDate || user.birth_date),
+              height: user.height,
+              weight: user.weight,
+              educationText: this.getEducationText(user.educationLevel),
+              salaryText: this.getSalaryText(user.salaryRange),
+              location: `${user.provinceName || ''}${user.cityName || ''}${user.areaName || ''}`,
+              // 保留原始数据用于其他用途
+              originalData: user
+            }
+          })
         } else {
           this.users = []
-          this.hasMore = false
-        }
-        
-        if (this.users.length === 0) {
-          console.log('没有喜欢我的用户')
         }
+        console.log('加载喜欢我的用户成功:', this.users)
       } catch (error) {
         console.error('加载喜欢我的用户失败:', error)
-        this.users = []
         uni.showToast({
           title: '加载失败,请重试',
           icon: 'none'
         })
+        this.users = []
       } finally {
         this.loading = false
       }
     },
     
-    async loadMoreUsers() {
-      if (this.loadingMore || !this.hasMore) {
-        return
-      }
-      
-      if (!this.currentUserId) {
-        return
-      }
-      
-      try {
-        this.loadingMore = true
-        const offset = (this.pageNum) * this.pageSize
-        const res = await api.recommend.getLikedMeUsers(this.currentUserId, this.pageSize, offset)
-        
-        if (res && Array.isArray(res) && res.length > 0) {
-          const newUsers = res.map(user => this.transformUserData(user))
-          this.users = this.users.concat(newUsers)
-          this.pageNum++
-          this.hasMore = res.length >= this.pageSize
-        } else {
-          this.hasMore = false
-        }
-      } catch (error) {
-        console.error('加载更多用户失败:', error)
-        uni.showToast({
-          title: '加载更多失败',
-          icon: 'none'
-        })
-      } finally {
-        this.loadingMore = false
-      }
-    },
-    
-    handleAvatarError(user) {
-      if (user) {
-        user.avatar = '/static/default-avatar.png'
-      }
-    },
-    
+    // 跳转到用户详情页
     goUserDetail(userId) {
-      if (!userId) {
-        uni.showToast({
-          title: '用户信息不完整',
-          icon: 'none'
-        })
-        return
-      }
       uni.navigateTo({
-        url: `/pages/recommend/index?userId=${userId}`,
+        url: `/pages/recommend/user-detail?userId=${userId}`,
         fail: (err) => {
           console.error('跳转用户详情失败:', err)
           uni.showToast({
@@ -275,6 +185,7 @@ export default {
       })
     },
     
+    // 跳转到推荐页面
     goRecommend() {
       uni.navigateTo({
         url: '/pages/profile/index'
@@ -347,105 +258,90 @@ export default {
 
 .user-list {
   padding: 20rpx;
-  height: calc(100vh - 88rpx);
+  height: 100vh;
+  box-sizing: border-box;
+  overflow-x: hidden;
 }
 
 .user-item {
-  display: flex;
-  align-items: center;
-  background-color: #FFFFFF;
-  border-radius: 20rpx;
-  padding: 20rpx;
-  margin-bottom: 20rpx;
-  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
-  transition: all 0.3s ease;
-  
-  &:active {
-    transform: translateY(2rpx);
-    box-shadow: 0 1rpx 5rpx rgba(0, 0, 0, 0.05);
-  }
-  
-  .user-avatar {
-    width: 120rpx;
-    height: 120rpx;
-    border-radius: 50%;
-    margin-right: 20rpx;
-    border: 4rpx solid #FFF9F9;
-    box-shadow: 0 4rpx 15rpx rgba(233, 30, 99, 0.2);
-  }
-  
-  .user-info {
-    flex: 1;
+    display: flex;
+    align-items: center;
+    background-color: #FFFFFF;
+    border-radius: 20rpx;
+    padding: 20rpx;
+    margin-bottom: 20rpx;
+    box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
+    transition: all 0.3s ease;
+    width: 100%;
+    box-sizing: border-box;
+    overflow: hidden;
     
-    .user-basic {
-      display: flex;
-      align-items: center;
-      margin-bottom: 10rpx;
+    &:active {
+      transform: translateY(2rpx);
+      box-shadow: 0 1rpx 5rpx rgba(0, 0, 0, 0.05);
+    }
+    
+    .user-avatar {
+      width: 120rpx;
+      height: 120rpx;
+      border-radius: 50%;
+      margin-right: 20rpx;
+      border: 4rpx solid #FFF9F9;
+      box-shadow: 0 4rpx 15rpx rgba(233, 30, 99, 0.2);
+      flex-shrink: 0;
+    }
+    
+    .user-info {
+      flex: 1;
+      min-width: 0;
       
-      .user-nickname {
-        font-size: 32rpx;
-        font-weight: bold;
-        color: #333333;
-        margin-right: 15rpx;
+      .user-basic {
+        display: flex;
+        align-items: center;
+        margin-bottom: 10rpx;
+        
+        .user-nickname {
+          font-size: 32rpx;
+          font-weight: bold;
+          color: #333333;
+          margin-right: 15rpx;
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+        
+        .user-age {
+          font-size: 28rpx;
+          color: #666666;
+          white-space: nowrap;
+        }
       }
       
-      .user-age {
-        font-size: 28rpx;
-        color: #666666;
+      .user-details {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 15rpx;
+        margin-bottom: 10rpx;
+        
+        .detail-item {
+          font-size: 24rpx;
+          color: #999999;
+          background-color: #F5F5F5;
+          padding: 6rpx 15rpx;
+          border-radius: 15rpx;
+          white-space: nowrap;
+        }
       }
-    }
-    
-    .user-details {
-      display: flex;
-      flex-wrap: wrap;
-      gap: 15rpx;
-      margin-bottom: 10rpx;
       
-      .detail-item {
-        font-size: 24rpx;
-        color: #999999;
-        background-color: #F5F5F5;
-        padding: 6rpx 15rpx;
-        border-radius: 15rpx;
-      }
-    }
-    
-    .user-location {
-      .location-text {
-        font-size: 24rpx;
-        color: #999999;
+      .user-location {
+        .location-text {
+          font-size: 24rpx;
+          color: #999999;
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
       }
     }
   }
-  
-  .like-status {
-    .like-icon {
-      font-size: 40rpx;
-      color: #E91E63;
-    }
-  }
-}
-
-.loading-more {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  padding: 30rpx 0;
-  
-  .loading-more-text {
-    margin-left: 16rpx;
-    font-size: 26rpx;
-    color: #999999;
-  }
-}
-
-.no-more {
-  text-align: center;
-  padding: 30rpx 0;
-  
-  .no-more-text {
-    font-size: 26rpx;
-    color: #999999;
-  }
-}
 </style>

+ 3 - 0
service/Recommend/src/main/java/com/zhentao/vo/RecommendUserVO.java

@@ -13,6 +13,9 @@ public class RecommendUserVO {
     private Integer provinceId;
     private Integer cityId;
     private Integer areaId;
+    private String provinceName;
+    private String cityName;
+    private String areaName;
     private Integer height;
     private Integer weight;
     private Integer educationLevel;

+ 12 - 0
service/Recommend/src/main/resources/mapper/RecommendMapper.xml

@@ -6,9 +6,14 @@
         <result column="user_id" property="userId"/>
         <result column="nickname" property="nickname"/>
         <result column="gender" property="gender"/>
+        <result column="birth_date" property="birthDate"/>
+        <result column="birthDate" property="birthDate"/>
         <result column="province_id" property="provinceId"/>
         <result column="city_id" property="cityId"/>
         <result column="area_id" property="areaId"/>
+        <result column="province_name" property="provinceName"/>
+        <result column="city_name" property="cityName"/>
+        <result column="area_name" property="areaName"/>
         <result column="height" property="height"/>
         <result column="weight" property="weight"/>
         <result column="education_level" property="educationLevel"/>
@@ -618,9 +623,13 @@
           u.nickname,
           u.gender,
           u.birth_date,
+          DATE_FORMAT(u.birth_date, '%Y-%m-%d') AS birthDate,
           p.province_id,
           p.city_id,
           p.area_id,
+          pr.name AS province_name,
+          ct.name AS city_name,
+          ar.name AS area_name,
           p.height,
           p.weight,
           p.education_level,
@@ -636,6 +645,9 @@
         FROM user_likes_user ulu
         JOIN users u ON u.user_id = ulu.user_id
         LEFT JOIN user_profile p ON p.user_id = u.user_id
+        LEFT JOIN province pr ON pr.id = p.province_id
+        LEFT JOIN city ct ON ct.id = p.city_id
+        LEFT JOIN area ar ON ar.id = p.area_id
         WHERE ulu.like_user_id = #{userId}
           AND u.status = 1
         ORDER BY ulu.create_time DESC