Jelajahi Sumber

feat(recommend): 实现用户喜欢列表分页和取消喜欢功能

- 添加offset参数支持分页查询喜欢的用户列表
- 实现取消喜欢功能,调用后端API删除喜欢记录
- 添加下拉刷新和上拉加载更多功能
- 修复我喜欢的用户数量显示为随机数的问题
- 添加加载状态提示和无更多数据提示
- 优化页面样式和布局,移除固定标题栏
- 实现真实的后端API数据获取替换模拟数据
李思佳 1 Minggu lalu
induk
melakukan
c94da2c83c

+ 21 - 1
LiangZhiYUMao/pages/mine/index.vue

@@ -408,12 +408,15 @@ export default {
 
         // 设置统计数据
         this.stats = {
-          myConnection: Math.floor(Math.random() * 8) + 2,
+          myConnection: 0,
           myFavorites: Math.floor(Math.random() * 15) + 5,
           whoViewedMe: Math.floor(Math.random() * 12) + 3,
           iViewed: Math.floor(Math.random() * 20) + 8
         }
 
+        // 获取我喜欢的用户真实数量
+        this.getLikedUsersCount()
+
         // 立即加载最新用户信息(不延迟)
         this.loadUserInfo()
 
@@ -1261,6 +1264,23 @@ export default {
       })
     },
     
+    // 获取我喜欢的用户真实数量
+    async getLikedUsersCount() {
+      try {
+        const userId = this.currentUserId
+        if (userId) {
+          // 调用后端API获取真实的喜欢用户数量
+          const count = await api.recommend.getLikedUsersCount(userId)
+          // 更新统计数据
+          this.stats.myConnection = count || 0
+        }
+      } catch (error) {
+        console.error('获取喜欢的用户数量失败:', error)
+        // 如果获取失败,使用默认值
+        this.stats.myConnection = 0
+      }
+    },
+    
     goToLikedMe() {
       // 跳转到喜欢我的页面
       uni.navigateTo({

+ 117 - 25
LiangZhiYUMao/pages/mine/liked-by-me.vue

@@ -1,10 +1,5 @@
 <template>
   <view class="like-page">
-    <!-- 页面标题栏 -->
-    <view class="page-header">
-      <text class="header-title">我喜欢的</text>
-    </view>
-    
     <!-- 数据加载状态 -->
     <view class="loading-container" v-if="loading">
       <uni-loading type="circle" color="#E91E63"></uni-loading>
@@ -21,7 +16,7 @@
     </view>
     
     <!-- 用户列表 -->
-    <scroll-view class="user-list" scroll-y="true">
+    <scroll-view class="user-list" scroll-y="true" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore" refresher-enabled="true" refresher-triggered="refreshing">
       <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"></image>
         <view class="user-info">
@@ -39,10 +34,16 @@
             <text class="location-text">{{ user.location }}</text>
           </view>
         </view>
-        <view class="like-status">
+        <view class="like-status" @click.stop="cancelLike(user.userId)">
           <text class="like-icon">❤️</text>
         </view>
       </view>
+      
+      <!-- 加载更多提示 -->
+      <view class="load-more" v-if="users.length > 0">
+        <text v-if="hasMore">加载中...</text>
+        <text v-else>没有更多数据了</text>
+      </view>
     </scroll-view>
   </view>
 </template>
@@ -55,6 +56,7 @@ export default {
     return {
       users: [],
       loading: true,
+      refreshing: false,
       pageNum: 1,
       pageSize: 20,
       hasMore: true
@@ -67,9 +69,36 @@ export default {
     // 加载我喜欢的用户列表
     async loadUsers() {
       try {
-        this.loading = true
-        // 不调用真实API,显示空数据
-        this.users = []
+        // 从本地存储获取当前用户ID
+        const userId = parseInt(uni.getStorageSync('userId') || 1)
+        // 计算偏移量
+        const offset = (this.pageNum - 1) * this.pageSize
+        // 调用后端API获取我喜欢的用户列表
+        const likedUsers = await api.recommend.getLikedUsers(userId, this.pageSize, offset)
+        // 处理返回的数据,确保字段名匹配页面需要的格式
+        const formattedUsers = likedUsers.map(user => ({
+          userId: user.userId,
+          nickname: user.nickname,
+          avatar: user.avatarUrl || '',
+          age: this.calculateAge(user.birthDate),
+          height: user.height,
+          weight: user.weight,
+          educationText: this.formatEducation(user.educationLevel),
+          salaryText: this.formatSalary(user.salaryRange),
+          location: this.formatLocation(user.provinceId, user.cityId, user.areaId)
+        }))
+        
+        // 根据是否是刷新操作来处理数据
+        if (this.pageNum === 1) {
+          // 首次加载或刷新,直接替换数据
+          this.users = formattedUsers
+        } else {
+          // 加载更多,追加数据
+          this.users = [...this.users, ...formattedUsers]
+        }
+        
+        // 判断是否还有更多数据
+        this.hasMore = formattedUsers.length >= this.pageSize
       } catch (error) {
         console.error('加载喜欢的用户失败:', error)
         uni.showToast({
@@ -78,6 +107,71 @@ export default {
         })
       } finally {
         this.loading = false
+        this.refreshing = false
+      }
+    },
+    
+    // 下拉刷新
+    async onRefresh() {
+      this.refreshing = true
+      this.pageNum = 1
+      this.hasMore = true
+      await this.loadUsers()
+    },
+    
+    // 上拉加载更多
+    async onLoadMore() {
+      if (this.loading || !this.hasMore) return
+      this.pageNum++
+      await this.loadUsers()
+    },
+    
+    // 计算年龄
+    calculateAge(birthDate) {
+      if (!birthDate) return ''
+      const birth = new Date(birthDate)
+      const now = new Date()
+      let age = now.getFullYear() - birth.getFullYear()
+      if (now.getMonth() < birth.getMonth() || (now.getMonth() === birth.getMonth() && now.getDate() < birth.getDate())) {
+        age--
+      }
+      return age
+    },
+    
+    // 格式化教育程度
+    formatEducation(educationLevel) {
+      if (!educationLevel) return ''
+      const map = {1:'高中',2:'大专',3:'本科',4:'硕士',5:'博士'}
+      return map[educationLevel] || ''
+    },
+    
+    // 格式化薪资
+    formatSalary(salaryRange) {
+      if (!salaryRange) return ''
+      const map = {1:'<5k',2:'5-10k',3:'10-20k',4:'20-50k',5:'50k+'}
+      return map[salaryRange] || ''
+    },
+    
+    // 格式化地理位置
+    formatLocation(provinceId, cityId, areaId) {
+      // 这里简化处理,实际可能需要根据ID查询名称
+      return ''
+    },
+    
+    // 取消喜欢
+    async cancelLike(targetUserId) {
+      try {
+        // 从本地存储获取当前用户ID
+        const userId = parseInt(uni.getStorageSync('userId') || 1)
+        // 调用后端API取消喜欢
+        await api.recommend.feedback({ userId, targetUserId, type: 'dislike' })
+        // 刷新页面,重新加载喜欢的用户列表
+        await this.onRefresh()
+        // 显示操作成功提示
+        uni.showToast({ title: '已取消喜欢', icon: 'success' })
+      } catch (error) {
+        console.error('取消喜欢失败:', error)
+        uni.showToast({ title: '取消喜欢失败', icon: 'none' })
       }
     },
     
@@ -109,20 +203,9 @@ export default {
 .like-page {
   min-height: 100vh;
   background-color: #F5F5F5;
-}
-
-.page-header {
-  height: 88rpx;
-  line-height: 88rpx;
-  background-color: #E91E63;
-  text-align: center;
-  position: relative;
-  
-  .header-title {
-    font-size: 36rpx;
-    font-weight: bold;
-    color: #FFFFFF;
-  }
+  padding: 0 5rpx;
+  box-sizing: border-box;
+  overflow-x: hidden;
 }
 
 .loading-container {
@@ -179,7 +262,16 @@ export default {
 
 .user-list {
   padding: 20rpx;
-  height: calc(100vh - 88rpx);
+  height: 100vh;
+  box-sizing: border-box;
+  overflow-x: hidden;
+}
+
+.load-more {
+  text-align: center;
+  padding: 30rpx 0;
+  font-size: 28rpx;
+  color: #999999;
 }
 
 .user-item {

+ 3 - 19
LiangZhiYUMao/pages/mine/liked-me.vue

@@ -1,10 +1,5 @@
 <template>
   <view class="like-page">
-    <!-- 页面标题栏 -->
-    <view class="page-header">
-      <text class="header-title">喜欢我的</text>
-    </view>
-    
     <!-- 数据加载状态 -->
     <view class="loading-container" v-if="loading">
       <uni-loading type="circle" color="#E91E63"></uni-loading>
@@ -109,20 +104,9 @@ export default {
 .like-page {
   min-height: 100vh;
   background-color: #F5F5F5;
-}
-
-.page-header {
-  height: 88rpx;
-  line-height: 88rpx;
-  background-color: #E91E63;
-  text-align: center;
-  position: relative;
-  
-  .header-title {
-    font-size: 36rpx;
-    font-weight: bold;
-    color: #FFFFFF;
-  }
+  padding: 0 5rpx;
+  box-sizing: border-box;
+  overflow-x: hidden;
 }
 
 .loading-container {

+ 0 - 42
LiangZhiYUMao/pages/mine/my-activities.vue

@@ -1,14 +1,5 @@
 <template>
 	<view class="my-activities-page">
-		<!-- 自定义导航栏 -->
-		<view class="custom-navbar">
-			<view class="navbar-back" @click="goBack">
-				<text class="back-icon">←</text>
-			</view>
-			<view class="navbar-title">我的活动</view>
-			<view class="navbar-placeholder"></view>
-		</view>
-		
 		<!-- 标签页切换 -->
 		<view class="tabs">
 			<view class="tab-item" :class="{ active: currentTab === 'registered' }" @click="switchTab('registered')">
@@ -319,39 +310,6 @@ export default {
 	background: #F5F5F5;
 }
 
-/* 自定义导航栏 */
-.custom-navbar {
-	height: 88rpx;
-	background: #E91E63;
-	display: flex;
-	align-items: center;
-	justify-content: space-between;
-	padding: 0 20rpx;
-	color: white;
-}
-
-.navbar-back {
-	padding: 10rpx 20rpx;
-}
-
-.back-icon {
-	font-size: 40rpx;
-	color: white;
-	font-weight: bold;
-}
-
-.navbar-title {
-	font-size: 32rpx;
-	font-weight: bold;
-	position: absolute;
-	left: 50%;
-	transform: translateX(-50%);
-}
-
-.navbar-placeholder {
-	width: 80rpx;
-}
-
 /* 标签页 */
 .tabs {
 	display: flex;

+ 0 - 44
LiangZhiYUMao/pages/mine/my-dynamics.vue

@@ -1,14 +1,5 @@
 <template>
   <view class="my-dynamics-page">
-    <!-- 顶部导航栏 -->
-    <view class="top-nav">
-      <view class="back-btn" @click="goBack">
-        <text class="back-icon">‹</text>
-      </view>
-      <view class="nav-title">我的动态</view>
-      <view class="nav-right"></view>
-    </view>
-
     <!-- 用户信息区域 -->
     <view class="user-info-section">
       <image class="avatar" :src="userInfo.avatar" mode="aspectFill"></image>
@@ -706,41 +697,6 @@ export default {
   padding-bottom: 100rpx;
 }
 
-/* 顶部导航栏 */
-.top-nav {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  padding: 40rpx 30rpx 20rpx;
-  background: #FFFFFF;
-  position: relative;
-  z-index: 10;
-
-  .back-btn {
-    width: 60rpx;
-    height: 60rpx;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-
-    .back-icon {
-      font-size: 48rpx;
-      color: #333333;
-      font-weight: bold;
-    }
-  }
-
-  .nav-title {
-    font-size: 36rpx;
-    font-weight: bold;
-    color: #333333;
-  }
-
-  .nav-right {
-    width: 60rpx;
-  }
-}
-
 /* 用户信息区域 */
 .user-info-section {
   display: flex;

+ 3 - 19
LiangZhiYUMao/pages/mine/visited-by-me.vue

@@ -1,10 +1,5 @@
 <template>
   <view class="like-page">
-    <!-- 页面标题栏 -->
-    <view class="page-header">
-      <text class="header-title">我浏览的</text>
-    </view>
-    
     <!-- 数据加载状态 -->
     <view class="loading-container" v-if="loading">
       <uni-loading type="circle" color="#E91E63"></uni-loading>
@@ -132,20 +127,9 @@ export default {
 .like-page {
   min-height: 100vh;
   background-color: #F5F5F5;
-}
-
-.page-header {
-  height: 88rpx;
-  line-height: 88rpx;
-  background-color: #E91E63;
-  text-align: center;
-  position: relative;
-  
-  .header-title {
-    font-size: 36rpx;
-    font-weight: bold;
-    color: #FFFFFF;
-  }
+  padding: 0 5rpx;
+  box-sizing: border-box;
+  overflow-x: hidden;
 }
 
 .loading-container {

+ 6 - 2
LiangZhiYUMao/utils/api.js

@@ -535,8 +535,12 @@ export default {
       url: '/recommend/today' 
     }),
     // 获取用户喜欢的列表
-    getLikedUsers: (userId, limit) => request({
-      url: `/recommend/liked-users?userId=${userId}${limit ? `&limit=${limit}` : ''}`
+    getLikedUsers: (userId, limit, offset) => request({
+      url: `/recommend/liked-users?userId=${userId}${limit ? `&limit=${limit}` : ''}${offset ? `&offset=${offset}` : ''}`
+    }),
+    // 获取用户喜欢的用户数量
+    getLikedUsersCount: (userId) => request({
+      url: `/recommend/liked-users-count?userId=${userId}`
     })
   },
 

+ 18 - 1
service/Recommend/src/main/java/com/zhentao/controller/RecommendController.java

@@ -103,6 +103,8 @@ public class RecommendController {
             } else if ("dislike".equalsIgnoreCase(type)) {
                 stringRedisTemplate.opsForSet().add(dislikeKey, String.valueOf(targetUserId));
                 stringRedisTemplate.opsForSet().remove(likeKey, String.valueOf(targetUserId));
+                // 从数据库删除喜欢记录
+                recommendService.deleteUserLike(userId, targetUserId);
             } else {
                 return Result.error(400, "type only supports like|dislike");
             }
@@ -261,16 +263,31 @@ public class RecommendController {
     @GetMapping("/liked-users")
     public Result<List<RecommendUserVO>> getLikedUsers(
             @RequestParam("userId") Integer userId,
+            @RequestParam(value = "offset", required = false, defaultValue = "0") Integer offset,
             @RequestParam(value = "limit", required = false, defaultValue = "20") Integer limit
     ) {
         try {
-            List<RecommendUserVO> list = recommendService.getLikedUsers(userId, limit);
+            List<RecommendUserVO> list = recommendService.getLikedUsers(userId, offset, limit);
             return Result.success(list);
         } catch (Exception e) {
             e.printStackTrace();
             return Result.error("获取喜欢的用户列表失败: " + e.getMessage());
         }
     }
+    
+    // 获取用户喜欢的用户数量
+    @GetMapping("/liked-users-count")
+    public Result<Integer> getLikedUsersCount(
+            @RequestParam("userId") Integer userId
+    ) {
+        try {
+            Integer count = recommendService.countLikedUsers(userId);
+            return Result.success(count);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("获取喜欢的用户数量失败: " + e.getMessage());
+        }
+    }
 
 }
 

+ 7 - 1
service/Recommend/src/main/java/com/zhentao/mapper/RecommendMapper.java

@@ -40,8 +40,14 @@ public interface RecommendMapper {
     // 插入用户喜欢记录
     int insertUserLike(@Param("userId") Integer userId, @Param("likeUserId") Integer likeUserId);
     
+    // 删除用户喜欢记录
+    int deleteUserLike(@Param("userId") Integer userId, @Param("likeUserId") Integer likeUserId);
+    
     // 查询用户喜欢的列表
-    List<RecommendUserVO> selectLikedUsers(@Param("userId") Integer userId, @Param("limit") Integer limit);
+    List<RecommendUserVO> selectLikedUsers(@Param("userId") Integer userId, @Param("offset") Integer offset, @Param("limit") Integer limit);
+    
+    // 查询用户喜欢的用户数量
+    Integer countLikedUsers(@Param("userId") Integer userId);
 }
 
 

+ 43 - 0
service/Recommend/src/main/java/com/zhentao/pojo/UserLikesUser.java

@@ -0,0 +1,43 @@
+package com.zhentao.pojo;
+
+import java.util.Date;
+
+public class UserLikesUser {
+    private Integer id;
+    private Integer userId;
+    private Integer likeUserId;
+    private Date createTime;
+
+    // getter和setter方法
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
+
+    public Integer getLikeUserId() {
+        return likeUserId;
+    }
+
+    public void setLikeUserId(Integer likeUserId) {
+        this.likeUserId = likeUserId;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+}

+ 7 - 1
service/Recommend/src/main/java/com/zhentao/service/RecommendService.java

@@ -17,8 +17,14 @@ public interface RecommendService {
     // 保存用户喜欢记录
     boolean saveUserLike(Integer userId, Integer likeUserId);
     
+    // 删除用户喜欢记录
+    boolean deleteUserLike(Integer userId, Integer likeUserId);
+    
     // 获取用户喜欢列表
-    List<RecommendUserVO> getLikedUsers(Integer userId, Integer limit);
+    List<RecommendUserVO> getLikedUsers(Integer userId, Integer offset, Integer limit);
+    
+    // 获取用户喜欢的用户数量
+    Integer countLikedUsers(Integer userId);
 }
 
 

+ 32 - 2
service/Recommend/src/main/java/com/zhentao/service/impl/RecommendServiceImpl.java

@@ -457,20 +457,50 @@ public class RecommendServiceImpl implements RecommendService {
     }
 
     @Override
-    public List<RecommendUserVO> getLikedUsers(Integer userId, Integer limit) {
+    public boolean deleteUserLike(Integer userId, Integer likeUserId) {
+        if (userId == null || likeUserId == null) {
+            throw new IllegalArgumentException("userId and likeUserId cannot be null");
+        }
+        try {
+            int result = recommendMapper.deleteUserLike(userId, likeUserId);
+            return result > 0;
+        } catch (Exception ex) {
+            log.error("deleteUserLike failed", ex);
+            return false;
+        }
+    }
+
+    @Override
+    public List<RecommendUserVO> getLikedUsers(Integer userId, Integer offset, Integer limit) {
         if (userId == null) {
             throw new IllegalArgumentException("userId cannot be null");
         }
         if (limit == null || limit <= 0) {
             limit = 20;
         }
+        if (offset == null || offset < 0) {
+            offset = 0;
+        }
         try {
-            return recommendMapper.selectLikedUsers(userId, limit);
+            return recommendMapper.selectLikedUsers(userId, offset, limit);
         } catch (Exception ex) {
             log.error("getLikedUsers failed", ex);
             return java.util.Collections.emptyList();
         }
     }
+
+    @Override
+    public Integer countLikedUsers(Integer userId) {
+        if (userId == null) {
+            throw new IllegalArgumentException("userId cannot be null");
+        }
+        try {
+            return recommendMapper.countLikedUsers(userId);
+        } catch (Exception ex) {
+            log.error("countLikedUsers failed", ex);
+            return 0;
+        }
+    }
 }
 
 

+ 21 - 1
service/Recommend/src/main/resources/mapper/RecommendMapper.xml

@@ -556,6 +556,14 @@
         ]]>
     </insert>
     
+    <!-- 删除用户喜欢记录 -->
+    <delete id="deleteUserLike">
+        <![CDATA[
+        DELETE FROM user_likes_user 
+        WHERE user_id = #{userId} AND like_user_id = #{likeUserId}
+        ]]>
+    </delete>
+    
     <!-- 查询用户喜欢的列表 -->
     <select id="selectLikedUsers" resultMap="RecommendUserMap">
         <![CDATA[
@@ -563,6 +571,7 @@
           u.user_id,
           u.nickname,
           u.gender,
+          u.birth_date,
           p.province_id,
           p.city_id,
           p.area_id,
@@ -584,7 +593,18 @@
         WHERE ulu.user_id = #{userId}
           AND u.status = 1
         ORDER BY ulu.create_time DESC
-        LIMIT #{limit}
+        LIMIT #{offset}, #{limit}
+        ]]>
+    </select>
+    
+    <!-- 查询用户喜欢的用户数量 -->
+    <select id="countLikedUsers" resultType="java.lang.Integer">
+        <![CDATA[
+        SELECT COUNT(*) 
+        FROM user_likes_user ulu
+        JOIN users u ON u.user_id = ulu.like_user_id
+        WHERE ulu.user_id = #{userId}
+          AND u.status = 1
         ]]>
     </select>
 </mapper>