Переглянути джерело

feat(matchmaker): 实现红娘签到功能及我的活动页面

- 新增红娘本月签到记录接口
- 实现每日签到弹窗界面与交互逻辑
- 添加签到统计展示(连续签到、累计签到)
- 完善日历签到状态显示(今日签到标记)
- 创建我的活动列表页面
- 增加获取用户活动列表接口
- 修改账户设置为关于我们并调整跳转逻辑
- 更新默认头像逻辑,支持自定义头像URL
- 网关路由配置增加签到相关路径转发
李思佳 1 місяць тому
батько
коміт
042a15cd05

+ 7 - 0
LiangZhiYUMao/pages.json

@@ -133,6 +133,13 @@
 				"navigationStyle": "custom"
 			}
 		},
+		{
+			"path": "pages/matchmaker-workbench/my-activities",
+			"style": {
+				"navigationBarTitleText": "我的活动",
+				"navigationStyle": "custom"
+			}
+		},
 		{
 			"path": "pages/page3/page3",
 			"style": {

+ 10 - 0
LiangZhiYUMao/pages/matchmaker-workbench/index.vue

@@ -310,6 +310,16 @@ export default {
 				uni.navigateTo({
 					url: '/pages/matchmaker-workbench/mine'
 				})
+			},
+			// 返回用户端首页
+			openExitPopup() {
+				uni.navigateTo({
+					url: '/pages/index/index'
+				})
+			},
+			// 搜索
+			handleSearch() {
+				console.log('搜索')
 			}
 		}
 	}

+ 309 - 119
LiangZhiYUMao/pages/matchmaker-workbench/mine.vue

@@ -72,8 +72,8 @@
           </view>
 
           <view class="settings-item" @click="handleAccountSettings">
-            <view class="settings-icon gear"></view>
-            <text class="settings-text">账户设置</text>
+            <view class="settings-icon info"></view>
+            <text class="settings-text">关于我们</text>
             <view class="arrow-right"></view>
           </view>
 
@@ -116,14 +116,27 @@
     <uni-popup ref="signInPopup" type="center" :mask-click="false">
       <view class="sign-in-popup">
         <view class="popup-header">
-          <text class="popup-title">日签到</text>
+          <text class="popup-title">日签到</text>
           <view class="close-btn" @click="closeSignInPopup"></view>
         </view>
         <view class="popup-content">
+          <!-- 签到统计 -->
+          <view class="checkin-stats">
+            <view class="stats-item">
+              <text class="stats-label">已连续签到</text>
+              <text class="stats-value">{{ continuousDays }}</text>
+              <text class="stats-unit">天</text>
+            </view>
+            <view class="stats-item">
+              <text class="stats-label">累计签到</text>
+              <text class="stats-value">{{ totalDays }}</text>
+              <text class="stats-unit">天</text>
+            </view>
+          </view>
+          
           <view class="calendar-container">
             <view class="calendar-header">
               <text class="calendar-title">{{ calendarTitle }}</text>
-              <view class="calendar-icon"></view>
             </view>
             <view class="calendar-week">
               <text class="week-day">日</text>
@@ -142,14 +155,16 @@
 									'calendar-day',
 									{ 'other-month': !day.isCurrentMonth },
 									{ 'today': day.isToday },
-									{ 'checked': day.isChecked }
+									{ 'checked': day.isChecked },
+									{ 'today-checked': day.isToday && day.isChecked }
 								]"
               >
                 <text class="day-text">{{ day.date }}</text>
-                <view v-if="day.isChecked" class="check-mark"></view>
+                <view v-if="day.isChecked" class="check-mark"></view>
               </view>
             </view>
           </view>
+          
           <view class="sign-in-reward">
             <text class="reward-title">签到奖励</text>
             <text class="reward-points">+5积分</text>
@@ -195,7 +210,8 @@ export default {
       totalDays: 0,
       currentDate: new Date(),
       calendarTitle: '',
-      calendarDays: []
+      calendarDays: [],
+      signedDays: [] // 存储本月已签到的日期
     }
   },
   onLoad() {
@@ -245,11 +261,13 @@ export default {
       const isCurrentMonth = today.getFullYear() === year && today.getMonth() === month
       for (let i = 1; i <= lastDayDate; i++) {
         const isToday = isCurrentMonth && today.getDate() === i
+        // 检查该日期是否已签到
+        const isChecked = isToday && this.isSignedToday || this.signedDays.includes(i)
         days.push({
           date: i,
           isCurrentMonth: true,
           isToday: isToday,
-          isChecked: isToday && this.isSignedToday
+          isChecked: isChecked
         })
       }
       // 添加下个月的日期,补满6行
@@ -287,7 +305,7 @@ export default {
             male: 'http://115.190.125.125:9000/dynamic-comments/dynamics/5c645152-9940-41d3-83a9-69ee6e0c0aaa.png',
             female: 'http://115.190.125.125:9000/dynamic-comments/dynamics/c7fb04d7-ee4d-4b3d-bcef-f246da9c841f.png'
           }
-          const avatarUrl = matchmakerInfo.gender === 1 ? defaultAvatars.male : defaultAvatars.female
+          const avatarUrl = matchmakerInfo.avatarUrl || matchmakerInfo.avatar_url || (matchmakerInfo.gender === 1 ? defaultAvatars.male : defaultAvatars.female)
           console.log('获取到的红娘信息:', matchmakerInfo)
           this.profile = {
             realName: matchmakerInfo.real_name || '',
@@ -323,49 +341,144 @@ export default {
     async checkSignInStatus() {
       try {
         const userInfo = uni.getStorageSync('userInfo')
-        const userId = userInfo && userInfo.userId ? userInfo.userId : null
-        if (userId) {
-          const res = await api.matchmaker.checkinStatus(userId)
-          console.log('检查签到状态返回结果:', res)
-          let signed = false
-          if (typeof res === 'boolean') {
-            signed = res
-          } else if (typeof res === 'string') {
-            signed = res === 'true'
-          } else if (typeof res === 'number') {
-            signed = res === 1
-          } else if (res && typeof res === 'object') {
-            if (typeof res.todaySigned === 'boolean') signed = res.todaySigned
-            else if (typeof res.signed === 'boolean') signed = res.signed
-            else if (typeof res.isSignedToday === 'boolean') signed = res.isSignedToday
-            else if (typeof res.data === 'object' && res.data) {
-              if (typeof res.data.todaySigned === 'boolean') signed = res.data.todaySigned
-              else if (typeof res.data.signed === 'boolean') signed = res.data.signed
-              else if (typeof res.data.isSignedToday === 'boolean') signed = res.data.isSignedToday
-            }
-          }
-          this.isSignedToday = !!signed
-          const stats = await api.matchmaker.checkinStats(userId)
-          console.log('获取签到统计返回结果:', stats)
-          if (stats) {
-            this.continuousDays = stats.continuousDays || 0
-            this.totalDays = stats.totalDays || 0
-          }
-        } else {
+        if (!userInfo || !userInfo.userId) {
           this.isSignedToday = false
           this.continuousDays = 0
           this.totalDays = 0
+          this.signedDays = []
           console.warn('没有有效的userId,无法检查签到状态')
+          return
+        }
+        
+        // 首先获取红娘信息,以获取makerId
+        const matchmakerInfo = await api.matchmaker.getByUserId(userInfo.userId)
+        if (!matchmakerInfo || !matchmakerInfo.matchmakerId && !matchmakerInfo.matchmaker_id) {
+          this.isSignedToday = false
+          this.continuousDays = 0
+          this.totalDays = 0
+          this.signedDays = []
+          console.warn('没有有效的makerId,无法检查签到状态')
+          return
         }
+        
+        const makerId = matchmakerInfo.matchmakerId || matchmakerInfo.matchmaker_id
+        
+        // 检查今日是否已签到
+        const res = await api.matchmaker.checkinStatus(makerId)
+        console.log('检查签到状态返回结果:', res)
+        let signed = false
+        if (typeof res === 'boolean') {
+          signed = res
+        } else if (typeof res === 'string') {
+          signed = res === 'true'
+        } else if (typeof res === 'number') {
+          signed = res === 1
+        } else if (res && typeof res === 'object') {
+          if (typeof res.todaySigned === 'boolean') signed = res.todaySigned
+          else if (typeof res.signed === 'boolean') signed = res.signed
+          else if (typeof res.isSignedToday === 'boolean') signed = res.isSignedToday
+          else if (typeof res.data === 'object' && res.data) {
+            if (typeof res.data.todaySigned === 'boolean') signed = res.data.todaySigned
+            else if (typeof res.data.signed === 'boolean') signed = res.data.signed
+            else if (typeof res.data.isSignedToday === 'boolean') signed = res.data.isSignedToday
+          }
+        }
+        this.isSignedToday = !!signed
+        
+        // 获取签到统计
+        const stats = await api.matchmaker.checkinStats(makerId)
+        console.log('获取签到统计返回结果:', stats)
+        
+        // 初始化签到记录数组
+        this.signedDays = []
+        
+        if (stats) {
+          this.continuousDays = stats.continuousDays || 0
+          this.totalDays = stats.totalDays || 0
+          
+          // 检查stats中是否包含本月的签到记录
+          let signedDays = []
+          
+          // 情况1: stats直接包含signedDays数组
+          if (stats.signedDays && Array.isArray(stats.signedDays)) {
+            signedDays = stats.signedDays
+          }
+          // 情况2: stats.data包含signedDays数组
+          else if (stats.data && stats.data.signedDays && Array.isArray(stats.data.signedDays)) {
+            signedDays = stats.data.signedDays
+          }
+          // 情况3: stats是对象,直接包含日期属性
+          else if (typeof stats === 'object') {
+            // 处理stats对象
+            const processStats = (obj) => {
+              const days = []
+              // 遍历对象,寻找日期相关的属性
+              for (const key in obj) {
+                if (obj.hasOwnProperty(key)) {
+                  // 检查是否是日期相关的属性
+                  const value = obj[key]
+                  // 检查是否是日期格式
+                  if (typeof value === 'object' && value !== null && typeof value.isChecked === 'boolean') {
+                    // 如果是包含isChecked属性的对象,且isChecked为true
+                    if (value.isChecked && typeof value.date === 'number') {
+                      days.push(value.date)
+                    }
+                  }
+                }
+              }
+              return days
+            }
+            
+            // 先处理stats对象本身
+            signedDays = processStats(stats)
+            
+            // 如果stats.data存在,也处理一下
+            if (stats.data && typeof stats.data === 'object') {
+              signedDays = [...signedDays, ...processStats(stats.data)]
+            }
+          }
+          
+          // 检查签到状态响应中是否包含签到记录
+          if (signedDays.length === 0 && res && typeof res === 'object') {
+            const processRes = (obj) => {
+              const days = []
+              if (obj.signedDays && Array.isArray(obj.signedDays)) {
+                days.push(...obj.signedDays)
+              }
+              return days
+            }
+            
+            signedDays = processRes(res)
+            
+            if (res.data && typeof res.data === 'object') {
+              signedDays = [...signedDays, ...processRes(res.data)]
+            }
+          }
+          
+          // 检查今日是否已签到,如果已签到但不在signedDays中,添加进去
+          const today = new Date().getDate()
+          if (this.isSignedToday && !signedDays.includes(today)) {
+            signedDays.push(today)
+          }
+          
+          // 去重并排序
+          this.signedDays = [...new Set(signedDays)].sort((a, b) => a - b)
+        }
+        
+        console.log('最终的signedDays:', this.signedDays)
       } catch (error) {
         console.error('检查签到状态失败:', error)
         this.isSignedToday = false
         this.continuousDays = 0
         this.totalDays = 0
+        this.signedDays = []
       }
     },
     // 显示签到弹框
-    showSignInPopup() {
+    async showSignInPopup() {
+      // 显示弹窗前刷新签到状态和记录
+      await this.checkSignInStatus()
+      this.generateCalendar()
       this.$refs.signInPopup.open()
     },
     // 关闭签到弹框
@@ -376,49 +489,7 @@ export default {
     async doSignIn() {
       try {
         const userInfo = uni.getStorageSync('userInfo')
-        const userId = userInfo && userInfo.userId ? userInfo.userId : null
-        if (userId) {
-          const res = await api.matchmaker.doCheckin(userId)
-          console.log('签到API返回结果:', res)
-          let success = false
-          let alreadySigned = false
-          if (typeof res === 'boolean') {
-            success = res
-          } else if (typeof res === 'string') {
-            success = res === 'true' || res === 'ok' || res === 'success'
-          } else if (res && typeof res === 'object') {
-            if (typeof res.success === 'boolean') success = res.success
-            if (typeof res.todaySigned === 'boolean') alreadySigned = res.todaySigned
-            else if (typeof res.signed === 'boolean') alreadySigned = res.signed
-            else if (typeof res.isSignedToday === 'boolean') alreadySigned = res.isSignedToday
-            if (res.data && typeof res.data === 'object') {
-              if (typeof res.data.success === 'boolean') success = res.data.success
-              if (typeof res.data.todaySigned === 'boolean') alreadySigned = res.data.todaySigned
-              else if (typeof res.data.signed === 'boolean') alreadySigned = res.data.signed
-              else if (typeof res.data.isSignedToday === 'boolean') alreadySigned = res.data.isSignedToday
-            }
-          }
-          if (success) {
-            uni.showToast({
-              title: '签到成功',
-              icon: 'success'
-            })
-            this.isSignedToday = true
-            this.generateCalendar()
-            this.loadProfileData()
-            this.closeSignInPopup()
-          } else if (alreadySigned) {
-            uni.showToast({
-              title: '今日已签到',
-              icon: 'none'
-            })
-          } else {
-            uni.showToast({
-              title: '签到失败,请稍后重试',
-              icon: 'none'
-            })
-          }
-        } else {
+        if (!userInfo || !userInfo.userId) {
           uni.showToast({
             title: '请先登录',
             icon: 'none'
@@ -428,6 +499,69 @@ export default {
               url: '/pages/page3/page3'
             })
           }, 1000)
+          return
+        }
+        
+        // 首先获取红娘信息,以获取makerId
+        const matchmakerInfo = await api.matchmaker.getByUserId(userInfo.userId)
+        if (!matchmakerInfo || !matchmakerInfo.matchmakerId && !matchmakerInfo.matchmaker_id) {
+          uni.showToast({
+            title: '没有有效的makerId,无法签到',
+            icon: 'none'
+          })
+          return
+        }
+        
+        const makerId = matchmakerInfo.matchmakerId || matchmakerInfo.matchmaker_id
+        
+        // 执行签到
+        const res = await api.matchmaker.doCheckin(makerId)
+        console.log('签到API返回结果:', res)
+        let success = false
+        let alreadySigned = false
+        if (typeof res === 'boolean') {
+          success = res
+        } else if (typeof res === 'string') {
+          success = res === 'true' || res === 'ok' || res === 'success'
+        } else if (res && typeof res === 'object') {
+          if (typeof res.success === 'boolean') success = res.success
+          if (typeof res.todaySigned === 'boolean') alreadySigned = res.todaySigned
+          else if (typeof res.signed === 'boolean') alreadySigned = res.signed
+          else if (typeof res.isSignedToday === 'boolean') alreadySigned = res.isSignedToday
+          if (res.data && typeof res.data === 'object') {
+            if (typeof res.data.success === 'boolean') success = res.data.success
+            if (typeof res.data.todaySigned === 'boolean') alreadySigned = res.data.todaySigned
+            else if (typeof res.data.signed === 'boolean') alreadySigned = res.data.signed
+            else if (typeof res.data.isSignedToday === 'boolean') alreadySigned = res.data.isSignedToday
+          }
+        }
+        
+        if (success) {
+          uni.showToast({
+            title: '签到成功',
+            icon: 'success'
+          })
+          this.isSignedToday = true
+          // 更新签到记录
+          const today = new Date()
+          const todayDate = today.getDate()
+          if (!this.signedDays.includes(todayDate)) {
+            this.signedDays.push(todayDate)
+            this.signedDays.sort((a, b) => a - b)
+          }
+          this.generateCalendar()
+          this.loadProfileData()
+          this.closeSignInPopup()
+        } else if (alreadySigned) {
+          uni.showToast({
+            title: '今日已签到',
+            icon: 'none'
+          })
+        } else {
+          uni.showToast({
+            title: '签到失败,请稍后重试',
+            icon: 'none'
+          })
         }
       } catch (error) {
         console.error('签到失败:', error)
@@ -447,18 +581,16 @@ export default {
         url: '/pages/matchmaker-workbench/my-resources'
       })
     },
-    // 活动中心
+    // 我的活动
     handleActivityCenter() {
       uni.navigateTo({
-        url: '/pages/activities/list'
+        url: '/pages/matchmaker-workbench/my-activities'
       })
     },
     // 积分商城
     handlePointsMall() {
-      console.log('积分商城')
-      uni.showToast({
-        title: '积分商城开发中',
-        icon: 'none'
+      uni.navigateTo({
+        url: '/pages/matchmaker-workbench/points-mall'
       })
     },
     // 编辑资料
@@ -468,12 +600,10 @@ export default {
         url: '/pages/matchmaker-workbench/edit-profile'
       })
     },
-    // 账户设置
+    // 关于我们
     handleAccountSettings() {
-      console.log('账户设置')
-      uni.showToast({
-        title: '账户设置开发中',
-        icon: 'none'
+      uni.navigateTo({
+        url: '/pages/settings/about'
       })
     },
     // 退出登录
@@ -818,6 +948,10 @@ export default {
     .settings-icon.logout {
       background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%239C27B0"><path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"/></svg>');
     }
+    
+    .settings-icon.info {
+      background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%239C27B0"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>');
+    }
 
     .settings-text {
       flex: 1;
@@ -947,18 +1081,19 @@ export default {
     justify-content: space-between;
     align-items: center;
     padding: 30rpx;
-    border-bottom: 1rpx solid #F0F0F0;
+    background: #FFEBEE;
+    border-bottom: none;
 
     .popup-title {
-      font-size: 32rpx;
+      font-size: 36rpx;
       font-weight: bold;
-      color: #333;
+      color: #E91E63;
     }
 
     .close-btn {
       width: 40rpx;
       height: 40rpx;
-      background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23999"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>');
+      background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%23E91E63"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>');
       background-size: contain;
       background-repeat: no-repeat;
       background-position: center;
@@ -966,17 +1101,51 @@ export default {
   }
 
   .popup-content {
-    padding: 30rpx;
+    padding: 20rpx 30rpx 30rpx;
 
+    /* 签到统计 */
+    .checkin-stats {
+      display: flex;
+      justify-content: space-around;
+      margin-bottom: 30rpx;
+      padding: 20rpx;
+      background: #FFF3E0;
+      border-radius: 15rpx;
+      
+      .stats-item {
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        gap: 10rpx;
+        
+        .stats-label {
+          font-size: 24rpx;
+          color: #666;
+        }
+        
+        .stats-value {
+          font-size: 48rpx;
+          font-weight: bold;
+          color: #E91E63;
+        }
+        
+        .stats-unit {
+          font-size: 24rpx;
+          color: #666;
+        }
+      }
+    }
+    
     .calendar-container {
-      background: #F8F9FA;
+      background: #FFFFFF;
       border-radius: 15rpx;
       padding: 20rpx;
       margin-bottom: 30rpx;
+      box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
 
       .calendar-header {
         display: flex;
-        justify-content: space-between;
+        justify-content: center;
         align-items: center;
         margin-bottom: 20rpx;
 
@@ -985,22 +1154,13 @@ export default {
           font-weight: bold;
           color: #333;
         }
-
-        .calendar-icon {
-          width: 40rpx;
-          height: 40rpx;
-          background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="%239C27B0"><path d="M3 17h18v-2H3v2zm0 4h18v-2H3v2zM3 3v2h18V3H3zm0 6h18V7H3v2z"/></svg>');
-          background-size: contain;
-          background-repeat: no-repeat;
-          background-position: center;
-        }
       }
 
       .calendar-week {
         display: grid;
         grid-template-columns: repeat(7, 1fr);
         gap: 10rpx;
-        margin-bottom: 15rpx;
+        margin-bottom: 20rpx;
 
         .week-day {
           text-align: center;
@@ -1013,21 +1173,24 @@ export default {
       .calendar-days {
         display: grid;
         grid-template-columns: repeat(7, 1fr);
-        gap: 10rpx;
+        gap: 15rpx;
 
         .calendar-day {
           text-align: center;
-          font-size: 26rpx;
+          font-size: 28rpx;
           color: #333;
-          background: #FFFFFF;
-          border-radius: 50%;
-          height: 60rpx;
+          background: #F5F5F5;
+          border-radius: 8rpx;
+          height: 70rpx;
           display: flex;
+          flex-direction: column;
           align-items: center;
           justify-content: center;
+          position: relative;
 
           &.other-month {
             color: #CCCCCC;
+            background: #F9F9F9;
           }
 
           &.today {
@@ -1037,10 +1200,36 @@ export default {
           }
 
           &.checked {
-            background: #E8F5E9;
-            color: #4CAF50;
+            background: #FFCDD2;
+            color: #E91E63;
+            font-weight: bold;
+          }
+          
+          &.today-checked {
+            background: #E91E63;
+            color: #FFFFFF;
             font-weight: bold;
           }
+          
+          .check-mark {
+            position: absolute;
+            bottom: 5rpx;
+            right: 5rpx;
+            font-size: 18rpx;
+            color: #FFFFFF;
+            background: #E91E63;
+            border-radius: 50%;
+            width: 24rpx;
+            height: 24rpx;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+          }
+          
+          .today-checked .check-mark {
+            background: #FFFFFF;
+            color: #E91E63;
+          }
         }
       }
     }
@@ -1070,15 +1259,16 @@ export default {
     .sign-in-btn {
       width: 100%;
       height: 80rpx;
-      background: #9C27B0;
+      background: #E91E63;
       color: #FFFFFF;
       font-size: 32rpx;
       border-radius: 40rpx;
       border: none;
       font-weight: bold;
+      transition: all 0.3s;
 
       &:hover {
-        background: #7B1FA2;
+        background: #C2185B;
       }
 
       &.signed {

+ 314 - 0
LiangZhiYUMao/pages/matchmaker-workbench/my-activities.vue

@@ -0,0 +1,314 @@
+<template>
+  <view class="my-activities-page">
+    <!-- 自定义导航栏 -->
+    <view class="custom-navbar">
+      <view class="navbar-left" @click="goBack">
+        <text class="back-icon">←</text>
+      </view>
+      <view class="navbar-title">我的活动</view>
+      <view class="navbar-right"></view>
+    </view>
+
+    <!-- 活动列表 -->
+    <view class="activity-grid">
+      <view class="activity-card" v-for="(item, index) in activityList" :key="index"
+        @click="goToDetail(item.id)">
+        <image :src="item.cover_image" class="activity-image" mode="aspectFill"></image>
+        <view class="activity-info">
+          <view class="activity-name">{{ item.name }}</view>
+          <view class="activity-meta">
+            <text class="activity-location">📍 {{ item.location }}</text>
+          </view>
+          <view class="activity-footer">
+            <view class="activity-points">
+              <text class="points-symbol">💎</text>
+              <text class="points-value">{{ item.points }}</text>
+            </view>
+            <text class="activity-participants">{{ item.participants || 0 }}人参加</text>
+          </view>
+          <view class="activity-btn" @click.stop="goToDetail(item.id)">查看详情</view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 空状态 -->
+    <view class="empty-state" v-if="activityList.length === 0 && !loading">
+      <image src="https://img.icons8.com/color/96/000000/calendar--v1.png" class="empty-icon"></image>
+      <text class="empty-text">暂无报名活动</text>
+      <text class="empty-subtext">快去报名参加更多精彩活动吧</text>
+    </view>
+
+    <!-- 加载更多 -->
+    <view class="load-more" v-if="hasMore">
+      <text class="load-text">{{ loading ? '加载中...' : '上拉加载更多' }}</text>
+    </view>
+    <view class="no-more" v-else-if="activityList.length > 0">
+      <text class="no-more-text">没有更多了</text>
+    </view>
+  </view>
+</template>
+
+<script>
+import api from '../../utils/api.js'
+export default {
+  data() {
+    return {
+      activityList: [],
+      pageNum: 1,
+      pageSize: 10,
+      hasMore: true,
+      loading: false,
+      // 模拟数据,当API返回为空时使用
+      mockActivities: [
+        {
+          id: 1,
+          name: '姻缘一线牵·大型相亲会',
+          location: '雄安新区',
+          points: 299,
+          participants: 18,
+          cover_image: 'https://images.unsplash.com/photo-1511671782779-c97d3d27a1d4?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60'
+        },
+        {
+          id: 2,
+          name: '一期一会·红娘心得分享',
+          location: '北京',
+          points: 389,
+          participants: 54,
+          cover_image: 'https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60'
+        }
+      ]
+    }
+  },
+
+  onLoad() {
+    this.loadMyActivityList()
+  },
+
+  onReachBottom() {
+    if (this.hasMore && !this.loading) {
+      this.pageNum++
+      this.loadMyActivityList()
+    }
+  },
+
+  methods: {
+    // 加载我的活动列表
+    async loadMyActivityList() {
+      if (this.loading) return
+      this.loading = true
+
+      try {
+        const userInfo = uni.getStorageSync('userInfo')
+        const testUserId = 19
+        const userId = userInfo && userInfo.userId ? userInfo.userId : testUserId
+        
+        const data = await api.activity.getMyActivities({
+          pageNum: this.pageNum,
+          pageSize: this.pageSize,
+          userId: userId
+        })
+
+        if (data && data.length > 0) {
+          this.activityList = [...this.activityList, ...data]
+          this.hasMore = data.length >= this.pageSize
+        } else {
+          // 如果API返回为空,使用模拟数据
+          this.hasMore = false
+          if (this.pageNum === 1) {
+            this.activityList = this.mockActivities
+          }
+        }
+      } catch (error) {
+        console.error('加载我的活动列表失败:', error)
+        // 当API调用失败时,使用模拟数据
+        if (this.pageNum === 1) {
+          this.activityList = this.mockActivities
+        }
+        this.hasMore = false
+      } finally {
+        this.loading = false
+      }
+    },
+
+    // 跳转到详情
+    goToDetail(id) {
+      uni.navigateTo({
+        url: `/pages/activities/detail?id=${id}`
+      })
+    },
+
+    // 返回
+    goBack() {
+      uni.navigateBack()
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+  .my-activities-page {
+    min-height: 100vh;
+    background-color: #FFF9F9;
+    padding-top: 90rpx;
+  }
+
+  /* 自定义导航栏 */
+  .custom-navbar {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 90rpx;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 0 20rpx;
+    background: linear-gradient(135deg, #FCE4EC 0%, #F8BBD0 100%);
+    z-index: 999;
+
+    .navbar-left,
+    .navbar-right {
+      width: 80rpx;
+    }
+
+    .back-icon {
+      font-size: 40rpx;
+      color: #333333;
+      font-weight: bold;
+    }
+
+    .navbar-title {
+      flex: 1;
+      text-align: center;
+      font-size: 32rpx;
+      font-weight: bold;
+      color: #333333;
+    }
+  }
+
+  /* 活动列表 - 两列布局 */
+  .activity-grid {
+    display: grid;
+    grid-template-columns: 1fr 1fr;
+    gap: 20rpx;
+    padding: 20rpx;
+
+    .activity-card {
+      position: relative;
+      background-color: #FFFFFF;
+      border-radius: 20rpx;
+      overflow: hidden;
+      box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
+
+      .activity-image {
+        width: 100%;
+        height: 240rpx;
+        background-color: #F5F5F5;
+      }
+
+      .activity-info {
+        padding: 20rpx;
+
+        .activity-name {
+          font-size: 28rpx;
+          font-weight: bold;
+          color: #333333;
+          margin-bottom: 10rpx;
+          display: -webkit-box;
+          -webkit-box-orient: vertical;
+          -webkit-line-clamp: 2;
+          overflow: hidden;
+        }
+
+        .activity-meta {
+          margin-bottom: 15rpx;
+
+          .activity-location {
+            font-size: 24rpx;
+            color: #666666;
+          }
+        }
+
+        .activity-footer {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: 15rpx;
+
+          .activity-points {
+            display: flex;
+            align-items: center;
+
+            .points-symbol {
+              font-size: 24rpx;
+              margin-right: 5rpx;
+            }
+
+            .points-value {
+              font-size: 26rpx;
+              color: #FF6B8A;
+              font-weight: bold;
+            }
+          }
+
+          .activity-participants {
+            font-size: 22rpx;
+            color: #999999;
+          }
+        }
+
+        .activity-btn {
+          width: 100%;
+          height: 60rpx;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          background-color: #9C27B0;
+          color: #FFFFFF;
+          border-radius: 30rpx;
+          font-size: 26rpx;
+        }
+      }
+    }
+  }
+
+  /* 空状态 */
+  .empty-state {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    padding: 100rpx 0;
+
+    .empty-icon {
+      width: 120rpx;
+      height: 120rpx;
+      opacity: 0.5;
+      margin-bottom: 20rpx;
+    }
+
+    .empty-text {
+      font-size: 32rpx;
+      color: #666666;
+      margin-bottom: 10rpx;
+    }
+
+    .empty-subtext {
+      font-size: 26rpx;
+      color: #999999;
+    }
+  }
+
+  /* 加载更多 */
+  .load-more,
+  .no-more {
+    padding: 30rpx 0;
+    text-align: center;
+
+    .load-text,
+    .no-more-text {
+      font-size: 24rpx;
+      color: #999999;
+    }
+  }
+</style>

+ 12 - 0
LiangZhiYUMao/utils/api.js

@@ -206,6 +206,13 @@ export default {
     cancelRegister: (activityId) => request({ 
       url: `/activity/cancel/${activityId}`, 
       method: 'POST' 
+    }),
+    
+    // 获取我的活动列表
+    getMyActivities: (params) => request({ 
+      url: '/activity/my',
+      method: 'GET',
+      data: params 
     })
   },
 
@@ -345,6 +352,11 @@ export default {
             url: `/matchmaker/update/${matchmakerId}`,
             method: 'PUT',
             data
+        }),
+        
+        // 获取本月签到记录
+        checkinList: (makerId, year, month) => request({
+            url: `/matchmaker/checkin/list?makerId=${makerId}&year=${year}&month=${month}`
         })
   },
 

+ 1 - 1
gateway/src/main/resources/application.yml

@@ -133,7 +133,7 @@ spring:
         - id: essential-route
           uri: http://localhost:1005
           predicates:
-            - Path=/api/user/**
+            - Path=/api/user/**, /api/checkin/**
           filters:
             - StripPrefix=0
         

+ 8 - 0
service/homePage/src/main/java/com/zhentao/controller/MatchmakerController.java

@@ -148,6 +148,14 @@ public class MatchmakerController {
             if (matchmaker.getStatus() == null) {
                 matchmaker.setStatus(1); // 1-正常
             }
+
+            if (matchmaker.getGender()== 1){
+                matchmaker.setAvatarUrl("http://115.190.125.125:9000/dynamic-comments/dynamics/5c645152-9940-41d3-83a9-69ee6e0c0aaa.png");
+            }
+
+            if (matchmaker.getGender()== 2){
+                matchmaker.setAvatarUrl("http://115.190.125.125:9000/dynamic-comments/dynamics/c7fb04d7-ee4d-4b3d-bcef-f246da9c841f.png");
+            }
             
             // 保存红娘到数据库
             boolean success = matchmakerService.save(matchmaker);