Przeglądaj źródła

上线测试更改BUG

wangwenju 1 tydzień temu
rodzic
commit
20ba90e1d3
28 zmienionych plików z 2347 dodań i 806 usunięć
  1. 1004 0
      LiangZhiYUMao/package-lock.json
  2. 5 0
      LiangZhiYUMao/package.json
  3. 1 1
      LiangZhiYUMao/pages/astrology/bazi.vue
  4. 1 1
      LiangZhiYUMao/pages/astrology/constellation.vue
  5. 1 1
      LiangZhiYUMao/pages/astrology/zodiac.vue
  6. 121 84
      LiangZhiYUMao/pages/index/index.vue
  7. 2 2
      LiangZhiYUMao/pages/matchmakers/detail.vue
  8. 39 43
      LiangZhiYUMao/pages/mine/index.vue
  9. 319 169
      LiangZhiYUMao/pages/mine/my-activities.vue
  10. 71 1
      LiangZhiYUMao/pages/part-time-matchmaker/index.vue
  11. 348 330
      LiangZhiYUMao/pages/plaza/detail.vue
  12. 88 3
      LiangZhiYUMao/pages/profile/index.vue
  13. 33 26
      LiangZhiYUMao/pages/recommend/index.vue
  14. 13 13
      LiangZhiYUMao/pages/recommend/user-detail.vue
  15. 2 2
      LiangZhiYUMao/pages/settings/id-verification.vue
  16. 3 22
      LiangZhiYUMao/pages/today-recommend/index.vue
  17. 3 2
      service/Essential/src/main/java/com/zhentao/controller/CourseOrderController.java
  18. 122 0
      service/Essential/src/main/java/com/zhentao/entity/Course.java
  19. 3 2
      service/Essential/src/main/java/com/zhentao/mapper/CourseOrderMapper.java
  20. 2 1
      service/Essential/src/main/java/com/zhentao/service/CourseOrderService.java
  21. 2 1
      service/Essential/src/main/java/com/zhentao/service/impl/CourseOrderServiceImpl.java
  22. 10 0
      service/admin/src/main/java/com/zhentao/controller/PointsOrderController.java
  23. 4 0
      service/admin/src/main/java/com/zhentao/mapper/AdminUserMapper.java
  24. 2 0
      service/admin/src/main/java/com/zhentao/service/UserService.java
  25. 7 2
      service/admin/src/main/java/com/zhentao/service/impl/UserServiceImpl.java
  26. 3 0
      service/admin/src/main/resources/com/zhentao/mapper/AdminUserMapper.xml
  27. 102 94
      service/dynamic/src/main/java/com/zhentao/entity/DynamicComments.java
  28. 36 6
      service/dynamic/src/main/java/com/zhentao/service/impl/CommentServiceImpl.java

Plik diff jest za duży
+ 1004 - 0
LiangZhiYUMao/package-lock.json


+ 5 - 0
LiangZhiYUMao/package.json

@@ -18,6 +18,11 @@
         "element": "^0.1.4",
         "plus": "^0.1.0",
         "tim-wx-sdk": "^2.27.6",
+        "vue": "^3.5.26",
+        "vuex": "^4.1.0",
         "ws": "^8.18.3"
+    },
+    "devDependencies": {
+        "vite": "^7.3.0"
     }
 }

+ 1 - 1
LiangZhiYUMao/pages/astrology/bazi.vue

@@ -271,7 +271,7 @@ export default {
 	data() {
 		return {
 			// 输入数据
-			birthDate: '',
+			birthDate:  uni.getStorageSync("userInfo").birthDate,
 			birthDateDisplay: '请选择出生日期',
 			timeIndex: 0,
 			timeDisplay: '子时 (23:00-01:00)',

+ 1 - 1
LiangZhiYUMao/pages/astrology/constellation.vue

@@ -195,7 +195,7 @@ export default {
 	data() {
 		return {
 			// 生日相关
-			birthday: '',
+			birthday: uni.getStorageSync("userInfo").birthDate,
 			birthdayDisplay: '请选择生日',
 			startDate: '1950-01-01',
 			endDate: '2010-12-31',

+ 1 - 1
LiangZhiYUMao/pages/astrology/zodiac.vue

@@ -144,7 +144,7 @@ export default {
 	data() {
 		return {
 			// 生肖配对相关数据
-			myZodiac: '',
+			myZodiac: uni.getStorageSync("userInfo").animal,
 			targetZodiac: '',
 			myZodiacIndex: -1,
 			targetZodiacIndex: -1,

+ 121 - 84
LiangZhiYUMao/pages/index/index.vue

@@ -2,21 +2,23 @@
 	<view class="home-page">
 		<!-- 顶部状态栏 -->
 		<view class="top-bar">
-			<view class="logo">青鸾之恋</view>
-			<view class="top-right-icons">
+			<view class="logo">青鸾之恋
+			<img v-if="showMatchmakerButton" @click="openMatchmakerPopup" src="https://api.zhongruanke.cn/minio/static-images/切换用户.png" class="back-icon" />
+			</view>
+			<!-- <view class="top-right-icons"> -->
 				<!-- 红娘工作台切换按钮 - 根据数据库查询结果动态显示 -->
 				
-				<view v-if="showMatchmakerButton"
-					class="matchmaker-btn" @click="openMatchmakerPopup">
-<!--					<text class="switch-icon">🔄</text>-->
-					<text class="switch-text">切换工作台</text>
-				</view>
+				<!-- <view v-if="showMatchmakerButton"
+				class="matchmaker-btn" @click="openMatchmakerPopup">
+					<text class="switch-icon">🔄</text>-->
+					
+				<!-- </view> -->
 				<!-- 消息通知图标 -->
-				<view class="msg-icon" @click="goToMessages">
+				<!-- <view class="msg-icon" @click="goToMessages">
 					<text class="icon">🔔</text>
 					<view v-if="unreadCount > 0" class="badge">{{ unreadCount }}</view>
-				</view>
-			</view>
+				</view> -->
+			<!-- </view> -->
 		</view>
 
 
@@ -39,16 +41,27 @@
 			</swiper>
 		</view>
 
-		<!-- 小喇叭公告栏 -->
 		<view class="notice-bar" v-if="noticeList && noticeList.length > 0">
-			<text class="notice-icon">📢</text>
-			<swiper class="notice-swiper" vertical :autoplay="true" :interval="3000" :duration="500"
-				:circular="true">
-				<swiper-item v-for="(notice, index) in noticeList" :key="index">
-					<view class="notice-text">{{ notice.content }}</view>
-				</swiper-item>
-			</swiper>
-		</view>
+  <text class="notice-icon">📢</text>
+  <!-- 使用swiper实现多公告自动切换,每个公告完整显示 -->
+  <swiper 
+    class="notice-swiper" 
+    :autoplay="true"       
+    :interval="3000"        
+    :duration="500"       
+    :circular="true"        
+    :autoplay-with-control="false"  
+    :disable-touch="true"   
+    direction="horizontal"
+    :indicator-dots="false"
+  >
+    <swiper-item v-for="(notice, index) in noticeList" :key="index">
+      <view class="notice-text-wrapper">
+        <text class="notice-text">{{ notice.content }}</text>
+      </view>
+    </swiper-item>
+  </swiper>
+</view>
 
 		<!-- 核心功能入口 -->
 		<view class="function-grid">
@@ -162,31 +175,29 @@
 		<view class="bottom-placeholder"></view>
 
 		<!-- 底部导航栏 -->
-		<view class="tabbar">
-			<view class="tabbar-item active" @click="switchTab('index')">
-				
-					<image src="https://api.zhongruanke.cn/minio/static-images/首页 (1).png" class="first-icon"/>
-				
-				<text class="tabbar-text">首页</text>
-			</view>
-			<view class="tabbar-item" @click="switchTab('plaza')">
-				<image src="https://api.zhongruanke.cn/minio/static-images/动态(1).png" class="first-icon"/>
-				<text class="tabbar-text">动态</text>
-			</view>
-			<view class="tabbar-item" @click="switchTab('recommend')">
-				<text class="tabbar-icon">👍</text>
-				<text class="tabbar-text">推荐</text>
-			</view>
-			<!-- <view class="tabbar-item" @click="switchTab('message')">
-				<text class="tabbar-icon">💬</text>
-				<text class="tabbar-text">消息</text>
-				<view v-if="unreadCount >= 1" class="tabbar-badge">{{ unreadCount }}</view>
-			</view> -->
-			<view class="tabbar-item" @click="switchTab('mine')">
-				<text class="tabbar-icon">👤</text>
-				<text class="tabbar-text">我的</text>
-			</view>
-		</view>
+				<view class="tabbar">
+					<view class="tabbar-item active" @click="switchTab('index')">
+						<text class="tabbar-icon">🏠</text>
+						<text class="tabbar-text">首页</text>
+					</view>
+					<view class="tabbar-item" @click="switchTab('plaza')">
+						<text class="tabbar-icon">💕</text>
+						<text class="tabbar-text">动态</text>
+					</view>
+					<view class="tabbar-item" @click="switchTab('recommend')">
+						<text class="tabbar-icon">👍</text>
+						<text class="tabbar-text">推荐</text>
+					</view>
+					<!-- <view class="tabbar-item" @click="switchTab('message')">
+						<text class="tabbar-icon">💬</text>
+						<text class="tabbar-text">消息</text>
+						<view v-if="unreadCount >= 1" class="tabbar-badge">{{ unreadCount }}</view>
+					</view> -->
+					<view class="tabbar-item" @click="switchTab('mine')">
+						<text class="tabbar-icon">👤</text>
+						<text class="tabbar-text">我的</text>
+					</view>
+				</view>
 
 		<!-- 红娘提示弹框 -->
 		<uni-popup ref="matchmakerPopup" type="dialog">
@@ -958,7 +969,6 @@ async loadFunctionGridData() {
 
 <style lang="scss" scoped>
 	.home-page {
-		margin-top: 50px;
 		min-height: 100vh;
 		background: #F5F5F5;
 		padding-bottom: 120rpx;
@@ -977,13 +987,19 @@ async loadFunctionGridData() {
 			font-size: 38rpx;
 			font-weight: 800;
 			color: #E91E63;
-			margin-left: 20px;
+			margin-top: 35px;
+		}
+		.back-icon{
+			width: 20px;
+			height: 20px;
 		}
 
 		.top-right-icons {
 			display: flex;
 			align-items: center;
 			gap: 20rpx;
+			margin-right: 80px;
+			margin-top: 40px;
 		}
 
 		.matchmaker-btn {
@@ -1144,36 +1160,58 @@ async loadFunctionGridData() {
 	}
 
 	/* 公告栏 */
-	.notice-bar {
-		display: flex;
-		align-items: center;
-		margin: 25rpx 35rpx;
-		padding: 24rpx 30rpx;
-		background: #FFF8E1;
-		border-radius: 8rpx;
-		border: 2rpx solid #FFD54F;
-		border-left: 6rpx solid #FFC107;
-
-		.notice-icon {
-			font-size: 34rpx;
-			margin-right: 24rpx;
-		}
-
-		.notice-swiper {
-			flex: 1;
-			height: 44rpx;
-
-			.notice-text {
-				font-size: 28rpx;
-				color: #795548;
-				line-height: 44rpx;
-				white-space: nowrap;
-				overflow: hidden;
-				text-overflow: ellipsis;
-				font-weight: 500;
-			}
-		}
-	}
+.notice-bar {
+  display: flex;
+  align-items: center;
+  margin: 25rpx 35rpx;
+  padding: 24rpx 30rpx;
+  background: #FFF8E1;
+  border-radius: 8rpx;
+  border: 2rpx solid #FFD54F;
+  border-left: 6rpx solid #FFC107;
+
+  .notice-icon {
+    font-size: 34rpx;
+    margin-right: 24rpx;
+    flex-shrink: 0; /* 固定图标不被挤压 */
+  }
+
+  .notice-swiper {
+      flex: 1;
+      height: 60rpx;
+      overflow: visible; /* 允许内容溢出 */
+      width: 100%; /* 确保swiper容器占满可用宽度 */
+
+      swiper-item {
+        width: auto !important;
+        min-width: 100%; /* 确保最短的公告也能占满容器 */
+        max-width: none; /* 允许swiper-item根据内容扩展宽度 */
+        display: flex;
+        align-items: center;
+        justify-content: flex-start;
+      }
+
+      .notice-text-wrapper {
+        width: auto;
+        min-width: 100%;
+        overflow: visible;
+      }
+
+      .notice-text {
+        font-size: 28rpx;
+        color: #795548;
+        line-height: 60rpx;
+        white-space: nowrap; /* 文字不换行 */
+        font-weight: 500;
+        display: flex;
+        align-items: center;
+        height: 100%;
+        padding-right: 30rpx; /* 添加右侧间距,避免内容紧贴边缘 */
+        width: auto;
+        min-width: 100%;
+      }
+    }
+}
 
 	/* 功能入口 */
 	.function-grid {
@@ -1689,20 +1727,20 @@ async loadFunctionGridData() {
 					padding: 15rpx 0;
 					position: relative;
 		
+				.first-icon{
+					width: 25px;
+					height: 25px;
+					margin-bottom: 5px;
+					margin-top: 5px;
+				}
 					.tabbar-icon {
 						font-size: 44rpx;
 						margin-bottom: 5rpx;
 					}
-					.first-icon{
-						width: 25px;
-						height: 25px;
-						margin-bottom: 5px;
-						margin-top: 5px;
-					}
 		
 					.tabbar-text {
 						font-size: 22rpx;
-						color: #666666;
+						color: #722F37;
 					}
 		
 					&.active {
@@ -1710,7 +1748,6 @@ async loadFunctionGridData() {
 							color: #E91E63;
 							font-weight: bold;
 						}
-						
 					}
 				}
 			}

+ 2 - 2
LiangZhiYUMao/pages/matchmakers/detail.vue

@@ -161,10 +161,10 @@
 				<text class="btn-icon">📞</text>
 				<text class="btn-text">拨打电话</text>
 			</view>
-			<view class="action-btn consult-btn" @click="consultMatchmaker">
+			<!-- <view class="action-btn consult-btn" @click="consultMatchmaker">
 				<text class="btn-icon">💬</text>
 				<text class="btn-text">在线咨询</text>
-			</view>
+			</view> -->
 		</view>
 	</view>
 </template>

+ 39 - 43
LiangZhiYUMao/pages/mine/index.vue

@@ -232,32 +232,30 @@
       </view>
     </view>
 
-    <!-- 底部导航栏 -->
-    <view class="tabbar">
-     <view class="tabbar-item active" @click="switchTab('index')">
-     	
-     		<image src="https://api.zhongruanke.cn/minio/static-images/首页 (1).png" class="first-icon"/>
-     	
-     	<text class="tabbar-text">首页</text>
-     </view>
-     <view class="tabbar-item" @click="switchTab('plaza')">
-     	<image src="https://api.zhongruanke.cn/minio/static-images/动态(1).png" class="first-icon"/>
-     	<text class="tabbar-text">动态</text>
-     </view>
-      <view class="tabbar-item" @click="switchTab('recommend')">
-        <text class="tabbar-icon">👍</text>
-        <text class="tabbar-text">推荐</text>
-      </view>
-     <!-- <view class="tabbar-item" @click="switchTab('message')">
-        <text class="tabbar-icon">💬</text>
-        <text class="tabbar-text">消息</text>
-        <view v-if="unreadCount > 0" class="tabbar-badge">{{ unreadCount }}</view>
-      </view> -->
-      <view class="tabbar-item active" @click="switchTab('mine')">
-        <text class="tabbar-icon">👤</text>
-        <text class="tabbar-text">我的</text>
-      </view>
-    </view>
+   <!-- 底部导航栏 -->
+   		<view class="tabbar">
+   			<view class="tabbar-item active" @click="switchTab('index')">
+   				<text class="tabbar-icon">🏠</text>
+   				<text class="tabbar-text">首页</text>
+   			</view>
+   			<view class="tabbar-item" @click="switchTab('plaza')">
+   				<text class="tabbar-icon">💕</text>
+   				<text class="tabbar-text">动态</text>
+   			</view>
+   			<view class="tabbar-item" @click="switchTab('recommend')">
+   				<text class="tabbar-icon">👍</text>
+   				<text class="tabbar-text">推荐</text>
+   			</view>
+   			<!-- <view class="tabbar-item" @click="switchTab('message')">
+   				<text class="tabbar-icon">💬</text>
+   				<text class="tabbar-text">消息</text>
+   				<view v-if="unreadCount >= 1" class="tabbar-badge">{{ unreadCount }}</view>
+   			</view> -->
+   			<view class="tabbar-item" @click="switchTab('mine')">
+   				<text class="tabbar-icon">👤</text>
+   				<text class="tabbar-text">我的</text>
+   			</view>
+   		</view>
   </view>
 </template>
 
@@ -874,9 +872,8 @@ export default {
       }
     },
     goVerify() {
-      uni.showToast({
-        title: '前往实名认证',
-        icon: 'none'
+      uni.navigateTo({
+        url: '/pages/settings/id-verification'
       })
     },
     goCompleteProfile() {
@@ -2130,18 +2127,17 @@ export default {
 
 /* 底部导航栏 - 优化版 */
 .tabbar {
-  position: fixed;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  display: flex;
-  background: rgba(255, 255, 255, 0.95);
-  backdrop-filter: blur(20rpx);
-  border-top: 1rpx solid rgba(255, 138, 155, 0.2);
-  padding-bottom: calc(15rpx + env(safe-area-inset-bottom));
-  padding-top: 15rpx;
-  z-index: 999;
-  box-shadow: 0 -4rpx 25rpx rgba(255, 138, 155, 0.15);
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ background-color: #FFFFFF;
+ border-top: 1rpx solid #F0F0F0;
+ padding-bottom: constant(safe-area-inset-bottom);
+ padding-bottom: env(safe-area-inset-bottom);
+ z-index: 999;
+ box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
 
   .tabbar-item {
     flex: 1;
@@ -2159,8 +2155,8 @@ export default {
       transition: all 0.3s ease;
     }
 	.first-icon{
-		width: 25px;
-		height: 25px;
+		width: 20px;
+		height: 20px;
 		margin-bottom: 5px;
 		margin-top: 5px;
 	}

+ 319 - 169
LiangZhiYUMao/pages/mine/my-activities.vue

@@ -3,99 +3,131 @@
 		<!-- 标签页切换 -->
 		<view class="tabs">
 			<view class="tab-item" :class="{ active: currentTab === 'registered' }" @click="switchTab('registered')">
-				<text class="tab-text">已报名</text>
+				<text class="tab-text">我的活动</text>
 				<view class="tab-badge" v-if="registeredCount > 0">{{ registeredCount }}</view>
 			</view>
 			<view class="tab-item" :class="{ active: currentTab === 'joined' }" @click="switchTab('joined')">
-				<text class="tab-text">已参加</text>
-			</view>
-			<view class="tab-item" :class="{ active: currentTab === 'favorite' }" @click="switchTab('favorite')">
-				<text class="tab-text">我的收藏</text>
+				<text class="tab-text">我的课程</text>
+				<view class="tab-badge" v-if="purchasedCourses.length > 0">{{ purchasedCourses.length }}</view>
 			</view>
 		</view>
 		
-		<!-- 活动列表 -->
+		<!-- 活动/课程列表 -->
 		<view class="activities-list">
-			<view class="activity-card" v-for="activity in filteredActivities" :key="activity.id" @click="goToActivityDetail(activity)">
-				<image :src="activity.coverImage" class="activity-cover" mode="aspectFill"></image>
+			<!-- 活动卡片 -->
+			<view class="activity-card" v-for="item in registeredActivities" :key="`activity-${item.id}`" @click="goToActivityDetail(item.activityId)" v-if="currentTab === 'registered'">
+				<image :src="item.coverImage" class="activity-cover" mode="aspectFill"></image>
 				<view class="activity-info">
 					<view class="activity-header">
-						<text class="activity-name">{{ activity.name }}</text>
-						<el-tag :type="getStatusType(activity.status)" size="small">{{ getStatusText(activity.status) }}</el-tag>
+						<text class="activity-name">{{ item.name }}</text>
+						<!-- 修复:改用数组+字符串拼接的合法 :class 格式 -->
+						<view class="status-tag" :class="['status-' + getStatusType(item.status)]">{{ getStatusText(item.status) }}</view>
 					</view>
 					
-					<view class="activity-meta">
+					<view class="activity-meta activity-meta--activity">
 						<view class="meta-item">
 							<text class="meta-icon">📅</text>
-							<text class="meta-text">{{ formatDate(activity.startTime) }}</text>
+							<text class="meta-text">{{ formatDate(item.startTime) }}</text>
 						</view>
-						<view class="meta-item" v-if="activity.location">
+						<view class="meta-item" v-if="item.location">
 							<text class="meta-icon">📍</text>
-							<text class="meta-text">{{ activity.location }}</text>
+							<text class="meta-text">{{ item.location }}</text>
 						</view>
 						<view class="meta-item">
 							<text class="meta-icon">👥</text>
-							<text class="meta-text">{{ activity.actualParticipants }}/{{ activity.maxParticipants }}人</text>
+							<text class="meta-text">{{ item.actualParticipants }}/{{ item.maxParticipants }}人</text>
 						</view>
 					</view>
 					
 					<view class="activity-footer">
-						<text class="activity-price">¥{{ activity.price || 0 }}</text>
+						<text class="activity-price">¥{{ item.price || 0 }}</text>
 						<view class="activity-actions">
-							<button class="action-btn" size="mini" v-if="currentTab === 'registered'" @click.stop="cancelActivity(activity)">取消报名</button>
-							<button class="action-btn primary" size="mini" @click.stop="goToActivityDetail(activity)">查看详情</button>
+							<button class="action-btn primary" size="mini" @click.stop="goToActivityDetail(item.activityId,item)">查看详情</button>
+							<!-- <button class="action-btn cancel" size="mini" @click.stop="cancelActivity(item)" v-if="item.status === 1">取消报名</button> -->
 						</view>
 					</view>
 				</view>
 			</view>
 			
-			<!-- 空状态 -->
-			<view class="empty-state" v-if="filteredActivities.length === 0">
-				<text class="empty-icon">📭</text>
-				<text class="empty-text">{{ getEmptyText() }}</text>
+			<!-- 课程卡片 -->
+			<view class="course-card" v-for="item in purchasedCourses" :key="`course-${item.id}`" @click="goToDetail(item.id,item)" v-if="currentTab === 'joined'">
+				<image :src="item.cover_image" class="course-cover" mode="aspectFill"></image>
+				<view class="course-info">
+					<view class="course-header">
+						<text class="course-name">{{ item.name }}</text>
+						<view class="course-rating" v-if="item.rating">
+							<text class="meta-icon">⭐</text>
+							<text class="rating-text">{{ item.rating }}</text>
+						</view>
+					</view>
+					
+					<view class="course-meta">
+						<view class="meta-item">
+							<text class="meta-icon">👤</text>
+							<text class="meta-text">讲师: {{ item.teacher_name || '未知讲师' }}</text>
+						</view>
+						<view class="meta-item">
+							<text class="meta-icon">⭐</text>
+							<text class="meta-text">{{item.rating}}</text>
+						</view>
+						<view class="meta-item">
+							<text class="meta-icon">👥</text>
+							<text class="meta-text">{{ item.student_count || 0 }}人已学习</text>
+						</view>
+					</view>
+					
+					<view class="course-footer">
+						<text class="course-price">¥{{ item.price || 0 }}</text>
+						<view class="course-actions">
+							<button class="action-btn primary course-btn"  @click.stop="goToDetail(item.id,item)">查看详情</button>
+						</view>
+					</view>
+				</view>
+			</view>
+			
+			<!-- 活动空状态 -->
+			<view class="empty-state" v-if="currentTab === 'registered' && registeredActivities.length === 0">
+				<text class="empty-icon">📅</text>
+				<text class="empty-text">暂无已报名的活动</text>
 				<button class="browse-btn" @click="goToActivityList">浏览活动</button>
 			</view>
+			
+			<!-- 课程空状态 -->
+			<view class="empty-state" v-if="currentTab === 'joined' && purchasedCourses.length === 0">
+				<text class="empty-icon">📚</text>
+				<text class="empty-text">暂无已购买的课程</text>
+				<button class="browse-btn" @click="goToCourseList">浏览课程</button>
+			</view>
 		</view>
 	</view>
 </template>
 
 <script>
+import api from '@/utils/api'
+
 export default {
 	data() {
 		return {
 			currentTab: 'registered',
+			// 活动相关
 			registeredActivities: [],
-			joinedActivities: [],
-			favoriteActivities: [],
-			registeredCount: 0
-		}
-	},
-	
-	computed: {
-		filteredActivities() {
-			if (this.currentTab === 'registered') {
-				return this.registeredActivities
-			} else if (this.currentTab === 'joined') {
-				return this.joinedActivities
-			} else {
-				return this.favoriteActivities
-			}
+			registeredCount: 0,
+			// 课程相关
+			purchasedCourses: [], // 已购买的课程
 		}
 	},
 	
 	onLoad() {
-		this.loadMyActivities()
+		this.loadMyData()
 	},
 	
 	methods: {
-		// 加载我的活动
-		async loadMyActivities() {
+		// 加载我的活动和课程数据
+		async loadMyData() {
 			try {
 				const userInfo = uni.getStorageSync('userInfo')
-			
 				
 				if (!userInfo || !userInfo.userId) {
-					console.warn('用户未登录')
 					uni.showToast({
 						title: '请先登录',
 						icon: 'none'
@@ -103,74 +135,77 @@ export default {
 					return
 				}
 				
-				const baseUrl = 'https://api.zhongruanke.cn' // 本地开发
-				// const baseUrl = 'http://115.190.125.125:8083' // 生产环境
+				// 并行加载活动和课程数据
+				await Promise.all([
+					this.loadRegisteredActivities(userInfo.userId),
+					this.loadPurchasedCourses(userInfo.userId)
+				])
 				
-				
-				
-				// 获取已报名的活动(status=1)
-				const [error1, registeredRes] = await uni.request({
-					url: `${baseUrl}/api/activity-registration/my-activities?userId=${userInfo.userId}&status=1`,
-					method: 'GET'
+			} catch (error) {
+				console.error('加载数据失败:', error)
+				uni.showToast({
+					title: '加载数据失败',
+					icon: 'none'
 				})
-				
+			}
+		},
+		
+		// 加载已报名的活动
+		async loadRegisteredActivities(userId) {
+			const baseUrl = 'https://api.zhongruanke.cn'
 			
+			try {
+				const [error, res] = await uni.request({
+					url: `${baseUrl}/api/activity-registration/my-activities?userId=${userId}&status=1`,
+					method: 'GET'
+				})
 				
-				if (error1) {
-					console.error('查询已报名活动失败:', error1)
-				}
-				
-				if (registeredRes && registeredRes.data && registeredRes.data.code === 200) {
-					
-					this.registeredActivities = (registeredRes.data.data || []).map(item => ({
+				if (res && res.data && res.data.code === 200) {
+					this.registeredActivities = (res.data.data || []).map(item => ({
 						id: item.id,
 						activityId: item.activityId,
 						name: item.activityName,
-						coverImage: item.coverImage,
+						coverImage: item.coverImage || '/static/default-activity.png',
 						startTime: item.startTime,
 						endTime: item.endTime,
 						location: item.location,
 						price: item.price,
-						actualParticipants: item.actualParticipants,
-						maxParticipants: item.maxParticipants,
+						actualParticipants: item.actualParticipants || 0,
+						maxParticipants: item.maxParticipants || 0,
 						status: this.getActivityStatusByTime(item.startTime, item.endTime)
 					}))
 					this.registeredCount = this.registeredActivities.length
 				}
-				
-				// 获取已签到的活动(status=3)
-				const [error2, joinedRes] = await uni.request({
-					url: `${baseUrl}/api/activity-registration/my-activities?userId=${userInfo.userId}&status=3`,
-					method: 'GET'
-				})
-				
-				
-				
-				if (error2) {
-					console.error('查询已参加活动失败:', error2)
-				}
-				
-				if (joinedRes && joinedRes.data && joinedRes.data.code === 200) {
-					this.joinedActivities = (joinedRes.data.data || []).map(item => ({
-						id: item.id,
-						activityId: item.activityId,
-						name: item.activityName,
-						coverImage: item.coverImage,
-						startTime: item.startTime,
-						endTime: item.endTime,
-						location: item.location,
-						price: item.price,
-						actualParticipants: item.actualParticipants,
-						maxParticipants: item.maxParticipants,
-						status: 3
+			} catch (error) {
+				console.error('加载活动数据失败:', error)
+			}
+		},
+		
+		// 加载已购买的课程
+		async loadPurchasedCourses(userId) {
+			try {
+				const coursesRes = await api.courseOrder.getPurchasedCourses(userId)
+				console.log(coursesRes)
+				this.purchasedCourses = coursesRes
+				if (coursesRes.code === 200) {
+					this.purchasedCourses = (coursesRes.data || []).map(course => ({
+						id: course.id,
+						courseId: course.id,
+						name: course.courseName,
+						coverImage: course.coverImage || '/static/default-course.png',
+						teacher: course.teacher,
+						price: course.price,
+						studentsCount: course.studentsCount || 0,
+						rating: course.rating || 4.5,
+						chaptersCount: course.chaptersCount || 0, // 课程章节数
+						lessonsCount: course.lessonsCount || 0,   // 课程课时数
+						learnProgress: course.learnProgress || 0,  // 学习进度
+						lastLearnTime: course.lastLearnTime        // 最后学习时间
 					}))
+					console.log(this.purchasedCourses)
 				}
-				
-				// 收藏功能暂时为空
-				this.favoriteActivities = []
-				
 			} catch (error) {
-				console.error('加载我的活动失败:', error)
+				console.error('加载课程数据失败:', error)
 			}
 		},
 		
@@ -187,9 +222,26 @@ export default {
 			return 3 // 已结束
 		},
 		
+		// 获取活动状态类型(用于样式)
+		getStatusType(status) {
+			const types = { 1: 'pending', 2: 'ongoing', 3: 'ended' }
+			return types[status] || 'pending'
+		},
+		
+		// 获取活动状态文本
+		getStatusText(status) {
+			const texts = { 1: '未开始', 2: '进行中', 3: '已结束' }
+			return texts[status] || '未知'
+		},
+		
 		// 切换标签页
 		switchTab(tab) {
 			this.currentTab = tab
+			// 切换时滚动到顶部
+			uni.pageScrollTo({
+				scrollTop: 0,
+				duration: 300
+			})
 		},
 		
 		// 格式化日期
@@ -207,63 +259,29 @@ export default {
 			}
 		},
 		
-		// 获取状态类型
-		getStatusType(status) {
-			const types = { 1: 'info', 2: 'success', 3: 'default' }
-			return types[status] || 'info'
-		},
-		
-		// 获取状态文本
-		getStatusText(status) {
-			const texts = { 1: '未开始', 2: '进行中', 3: '已结束' }
-			return texts[status] || '未知'
-		},
-		
-		// 获取空状态文本
-		getEmptyText() {
-			const texts = {
-				registered: '暂无报名的活动',
-				joined: '暂无参加过的活动',
-				favorite: '暂无收藏的活动'
-			}
-			return texts[this.currentTab] || '暂无数据'
-		},
-		
-		// 取消报名
+		// 取消活动报名
 		async cancelActivity(activity) {
 			uni.showModal({
-				title: '提示',
-				content: '确定要取消报名吗?',
+				title: '取消报名',
+				content: '确定要取消本次活动报名吗?',
 				success: async (res) => {
 					if (res.confirm) {
 						try {
 							const userInfo = uni.getStorageSync('userInfo')
 							const baseUrl = 'https://api.zhongruanke.cn'
 							
-							
-							
 							const [error, result] = await uni.request({
 								url: `${baseUrl}/api/activity-registration/cancel?id=${activity.id}&userId=${userInfo.userId}`,
 								method: 'PUT'
 							})
 							
-							
-							
-							if (error) {
-								console.error('取消报名请求失败:', error)
-								uni.showToast({
-									title: '网络错误,请重试',
-									icon: 'none'
-								})
-								return
-							}
-							
 							if (result && result.data && result.data.code === 200) {
 								uni.showToast({
-									title: '取消报名',
+									title: '取消报名成功',
 									icon: 'success'
 								})
-								this.loadMyActivities()
+								// 重新加载活动数据
+								this.loadRegisteredActivities(userInfo.userId)
 							} else {
 								uni.showToast({
 									title: (result && result.data && result.data.msg) || '取消失败',
@@ -271,9 +289,8 @@ export default {
 								})
 							}
 						} catch (error) {
-							console.error('取消报名失败:', error)
 							uni.showToast({
-								title: '取消失败',
+								title: '取消失败,请重试',
 								icon: 'none'
 							})
 						}
@@ -283,9 +300,27 @@ export default {
 		},
 		
 		// 跳转到活动详情
-		goToActivityDetail(activity) {
+		goToActivityDetail(itemId,item) {
+			uni.navigateTo({
+				url: `/pages/activities/detail?id=${item.activityId}`
+			})
+		},
+		
+		// 跳转到详情
+		goToDetail(id,item) {
+
 			uni.navigateTo({
-				url: `/pages/activities/detail?id=${activity.activityId}`
+				url: `/pages/courses/detail?id=${item.id}`,
+				success: () => {
+				
+				},
+				fail: (err) => {
+					
+					uni.showToast({
+						title: '跳转失败',
+						icon: 'none'
+					})
+				}
 			})
 		},
 		
@@ -296,9 +331,11 @@ export default {
 			})
 		},
 		
-		// 返回上一页
-		goBack() {
-			uni.navigateBack()
+		// 跳转到课程列表
+		goToCourseList() {
+			uni.navigateTo({
+				url: '/pages/courses/list'
+			})
 		}
 	}
 }
@@ -307,14 +344,15 @@ export default {
 <style scoped>
 .my-activities-page {
 	min-height: 100vh;
-	background: #F5F5F5;
+	background: #FFF9F9;
 }
 
-/* 标签页 */
+/* 标签页样式 */
 .tabs {
 	display: flex;
 	background: white;
 	padding: 0 30rpx;
+	border-bottom: 1rpx solid #f0f0f0;
 }
 
 .tab-item {
@@ -325,13 +363,13 @@ export default {
 }
 
 .tab-text {
-	font-size: 28rpx;
+	font-size: 30rpx;
 	color: #666;
 }
 
 .tab-item.active .tab-text {
 	color: #E91E63;
-	font-weight: bold;
+	font-weight: 600;
 }
 
 .tab-item.active::after {
@@ -340,7 +378,7 @@ export default {
 	bottom: 0;
 	left: 50%;
 	transform: translateX(-50%);
-	width: 60rpx;
+	width: 80rpx;
 	height: 6rpx;
 	background: #E91E63;
 	border-radius: 3rpx;
@@ -359,48 +397,78 @@ export default {
 	text-align: center;
 }
 
-/* 活动列表 */
+/* 列表容器 */
 .activities-list {
+	display: grid;
+	grid-template-columns: 1fr 1fr;
+	gap: 20rpx;
 	padding: 20rpx;
 }
 
+/* 活动卡片样式 */
 .activity-card {
+	position: relative;
 	background: white;
-	border-radius: 16rpx;
-	margin-bottom: 20rpx;
+	border-radius: 20rpx;
 	overflow: hidden;
-	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
+	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
 }
 
 .activity-cover {
 	width: 100%;
-	height: 200rpx;
+	height: 240rpx;
 	background: #F5F5F5;
 }
 
 .activity-info {
-	padding: 24rpx;
+	padding: 20rpx;
 }
 
 .activity-header {
 	display: flex;
 	justify-content: space-between;
-	align-items: center;
-	margin-bottom: 16rpx;
+	align-items: flex-start;
+	margin-bottom: 15rpx;
 }
 
 .activity-name {
-	font-size: 32rpx;
-	font-weight: bold;
+	font-size: 28rpx;
+	font-weight: 600;
 	color: #333;
 	flex: 1;
-	margin-right: 16rpx;
+	margin-right: 10rpx;
+	display: -webkit-box;
+	-webkit-box-orient: vertical;
+	-webkit-line-clamp: 2;
+	overflow: hidden;
+	line-height: 1.4;
+}
+
+/* 活动状态标签 */
+.status-tag {
+	font-size: 20rpx;
+	padding: 4rpx 12rpx;
+	border-radius: 12rpx;
+	color: white;
+}
+
+.status-pending {
+	background-color: #409EFF; /* 未开始-蓝色 */
+}
+
+.status-ongoing {
+	background-color: #67C23A; /* 进行中-绿色 */
+}
+
+.status-ended {
+	background-color: #909399; /* 已结束-灰色 */
 }
 
-.activity-meta {
+/* 活动元信息 */
+.activity-meta--activity {
 	display: flex;
 	flex-direction: column;
-	gap: 12rpx;
+	gap: 10rpx;
 	margin-bottom: 20rpx;
 }
 
@@ -417,43 +485,126 @@ export default {
 .meta-text {
 	font-size: 24rpx;
 	color: #666;
+	line-height: 1.4;
 }
 
 .activity-footer {
 	display: flex;
 	justify-content: space-between;
 	align-items: center;
-	padding-top: 16rpx;
-	border-top: 1rpx solid #F0F0F0;
 }
 
 .activity-price {
-	font-size: 32rpx;
-	font-weight: bold;
 	color: #E91E63;
+	font-weight: 600;
+	font-size: 32rpx;
+}
+
+/* 课程卡片样式 */
+.course-card {
+	position: relative;
+	background: white;
+	border-radius: 20rpx;
+	overflow: hidden;
+	box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
+}
+
+.course-cover {
+	width: 100%;
+	height: 240rpx;
+	background: #F5F5F5;
+}
+
+.course-info {
+	padding: 20rpx;
+}
+
+.course-header {
+	display: flex;
+	justify-content: space-between;
+	align-items: flex-start;
+	margin-bottom: 15rpx;
 }
 
+.course-name {
+	font-size: 28rpx;
+	font-weight: 600;
+	color: #333;
+	flex: 1;
+	margin-right: 10rpx;
+	display: -webkit-box;
+	-webkit-box-orient: vertical;
+	-webkit-line-clamp: 2;
+	overflow: hidden;
+	line-height: 1.4;
+}
+
+.course-rating {
+	display: flex;
+	align-items: center;
+	gap: 4rpx;
+	background-color: #FFF8E1;
+	padding: 4rpx 10rpx;
+	border-radius: 10rpx;
+}
+
+.rating-text {
+	font-size: 20rpx;
+	color: #FF9800;
+}
+
+/* 课程元信息 */
+.course-meta {
+	display: flex;
+	flex-direction: column;
+	gap: 10rpx;
+	margin-bottom: 20rpx;
+}
+
+.course-footer {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+}
+
+.course-price {
+	color: #9C27B0;
+	font-weight: 600;
+	font-size: 32rpx;
+}
+
+/* 按钮样式 */
 .activity-actions {
 	display: flex;
-	gap: 16rpx;
+	gap: 10rpx;
 }
 
 .action-btn {
-	padding: 8rpx 24rpx;
-	background: #F5F5F5;
-	color: #666;
+	padding: 8rpx 16rpx;
 	border-radius: 20rpx;
 	font-size: 24rpx;
 	border: none;
 }
 
 .action-btn.primary {
-	background: #E91E63;
+	background-color: #E91E63;
 	color: white;
 }
 
-/* 空状态 */
+.action-btn.cancel {
+	background-color: #F5F5F5;
+	color: #666;
+}
+
+.course-btn {
+	background-color: #9C27B0 !important;
+	width: 140rpx;
+	text-align: center;
+}
+
+/* 空状态样式 */
 .empty-state {
+	grid-column: 1 / -1;
 	display: flex;
 	flex-direction: column;
 	align-items: center;
@@ -480,5 +631,4 @@ export default {
 	font-size: 28rpx;
 	border: none;
 }
-</style>
-
+</style>

+ 71 - 1
LiangZhiYUMao/pages/part-time-matchmaker/index.vue

@@ -60,7 +60,18 @@
 
 				<view class="form-group">
 					<text class="form-label">手机号:</text>
-					<input class="form-input" v-model="formData.phone" type="number" placeholder="请输入您的手机号" />
+					<input 
+						class="form-input" 
+						v-model="formData.phone" 
+						type="number" 
+						placeholder="请输入您的手机号"
+						maxlength="11"  
+						@input="checkPhoneFormat"  
+						@blur="validatePhone"     
+						:class="{ 'input-error': phoneError }"  
+					/>
+					<!-- 错误提示文本 -->
+					<text class="error-text" v-if="phoneError">{{ phoneErrorText }}</text>
 				</view>
 
 				<view class="form-group">
@@ -162,6 +173,8 @@
 	export default {
 		data() {
 			return {
+					// 新增手机号校验字段
+						phoneError: false,
 				formData: {
 					name: '',
 					phone: '',
@@ -176,6 +189,7 @@
 					endTime: '',
 					introduction: ''
 				},
+					phoneErrorText: '',
 				// 省市区数据
 				provinceList: [],
 				cityList: [],
@@ -206,6 +220,48 @@
 			this.checkApplyStatus()
 		},
 		methods: {
+				// 实时校验手机号格式(输入时触发)
+				checkPhoneFormat() {
+					const phone = this.formData.phone.trim();
+					this.phoneError = false;
+					this.phoneErrorText = '';
+					
+					// 非空时才校验
+					if (phone) {
+						// 长度校验
+						if (phone.length < 11) {
+							this.phoneError = true;
+							this.phoneErrorText = `手机号不足11位(当前${phone.length}位)`;
+						} else if (phone.length > 11) {
+							this.phoneError = true;
+							this.phoneErrorText = '手机号超过11位';
+						} else {
+							// 格式校验(11位数字,以1开头)
+							const phoneReg = /^1[3-9]\d{9}$/;
+							if (!phoneReg.test(phone)) {
+								this.phoneError = true;
+								this.phoneErrorText = '手机号格式错误(请输入11位有效手机号)';
+							}
+						}
+					}
+				},
+				
+				// 失焦时最终校验
+				validatePhone() {
+					const phone = this.formData.phone.trim();
+					if (!phone) {
+						this.phoneError = false;
+						this.phoneErrorText = '';
+						return;
+					}
+					
+					// 最终格式校验
+					const phoneReg = /^1[3-9]\d{9}$/;
+					if (!phoneReg.test(phone)) {
+						this.phoneError = true;
+						this.phoneErrorText = '请输入正确的11位手机号';
+					}
+				},
 			// 返回上一页
 			goBack() {
 				uni.navigateBack()
@@ -805,6 +861,19 @@
 		.form-group {
 			margin-bottom: 30rpx;
 			box-sizing: border-box;
+			.input-error {
+						border-color: #ff4757 !important;
+						background-color: #fff5f5 !important;
+						color: #ff4757;
+					}
+					
+					.error-text {
+						display: block;
+						font-size: 22rpx;
+						color: #ff4757;
+						margin-top: 8rpx;
+						padding-left: 5rpx;
+					}
 			
 			.form-label {
 				display: block;
@@ -828,6 +897,7 @@
 					border-color: #FF8A9B;
 					background: #FFFFFF;
 				}
+				
 			}
 			
 			.picker-input {

Plik diff jest za duży
+ 348 - 330
LiangZhiYUMao/pages/plaza/detail.vue


+ 88 - 3
LiangZhiYUMao/pages/profile/index.vue

@@ -43,12 +43,23 @@
 					<text v-else class="value">{{ profile.realName || '未设置' }}</text>
 				</view>
 				
-				<view class="info-item" :class="{ editing: isEditing }" @click="startEdit">
+				<!-- 身份证号输入项改造 -->
+				<view class="info-item" :class="{ editing: isEditing, error: idCardError }" @click="startEdit">
 					<text class="label">身份证号</text>
-					<input v-if="isEditing" class="input" v-model="profile.idCard" placeholder="请输入身份证号" />
+					<input 
+						v-if="isEditing" 
+						class="input" 
+						v-model="profile.idCard" 
+						placeholder="请输入18位身份证号"
+						maxlength="18"
+						@input="checkIdCardLength"
+						@blur="checkIdCardValid"
+					/>
 					<text v-else class="value">{{ formatIdCard(profile.idCard) }}</text>
 					<text class="verify-status verified" v-if="profile.idCard && profile.isRealNameVerified">✓已认证</text>
+					<text class="verify-status error" v-else-if="isEditing && idCardError">{{ idCardErrorText }}</text>
 					<text class="verify-status unverified" v-else-if="!profile.idCard">未认证</text>
+					<text class="verify-status warning" v-else-if="profile.idCard && profile.idCard.length !== 18">长度有误</text>
 				</view>
 				
 				<view class="info-item" :class="{ editing: isEditing }" @click="startEdit">
@@ -393,7 +404,9 @@
 				cityList: [],
 				areaList: [],
 				multiAreaData: [[], [], []], // 三级联动数据
-				multiAreaIndex: [0, 0, 0] // 三级联动索引
+				multiAreaIndex: [0, 0, 0], // 三级联动索引
+				idCardError: false,
+				idCardErrorText: ''
 			}
 		},
 		computed: {
@@ -425,6 +438,60 @@
 		}
 	},
 		methods: {
+			// 实时校验身份证长度
+				checkIdCardLength() {
+					const idCard = this.profile.idCard.trim();
+					this.idCardError = false;
+					this.idCardErrorText = '';
+					
+					if (idCard) {
+						if (idCard.length !== 18 && idCard.length !== 15) {
+							this.idCardError = true;
+							this.idCardErrorText = idCard.length < 18 ? '身份证号不足18位' : '身份证号超过18位';
+						}
+					}
+				},
+				// 失焦校验身份证格式
+					checkIdCardValid() {
+						const idCard = this.profile.idCard.trim();
+						if (!idCard) return;
+						
+						// 长度校验
+						if (idCard.length !== 18 && idCard.length !== 15) {
+							this.idCardError = true;
+							this.idCardErrorText = '请输入18位有效身份证号';
+							return;
+						}
+						
+						// 格式正则校验(支持18位含X/x,15位纯数字)
+						const reg = /(^\d{18}$)|(^\d{17}(\d|X|x)$)|(^\d{15}$)/;
+						if (!reg.test(idCard)) {
+							this.idCardError = true;
+							this.idCardErrorText = '身份证号格式有误';
+						}
+					},
+					// 保存时校验身份证
+			validateIdCard() {
+				const idCard = this.profile.idCard.trim();
+				if (!idCard) return true; // 非必填
+							
+							// 长度校验
+				if (idCard.length !== 18 && idCard.length !== 15) {
+								uni.showToast({ title: '请输入18位有效身份证号', icon: 'none' });
+								return false;
+							}
+							
+							// 格式校验
+							const reg = /(^\d{18}$)|(^\d{17}(\d|X|x)$)|(^\d{15}$)/;
+							if (!reg.test(idCard)) {
+								uni.showToast({ title: '身份证号格式有误', icon: 'none' });
+								return false;
+							}
+							
+							
+							
+							return true;
+						},
 			// 选择并上传头像
 			chooseAvatar() {
 				uni.showActionSheet({
@@ -1227,6 +1294,9 @@
 			
 			// 保存资料
 			saveProfile(needGoBack = false) {
+					if (!this.validateIdCard()) {
+							return;
+						}
 				uni.showLoading({
 					title: '保存中...'
 				})
@@ -1972,5 +2042,20 @@
 			}
 		}
 	}
+	// 补充身份证错误样式
+	.info-item {
+		&.error {
+			.input {
+				border-color: #ff4757;
+				color: #ff4757;
+			}
+		}
+		
+		.verify-status {
+			&.warning {
+				color: #ffa502;
+			}
+		}
+	}
 </style>
 

+ 33 - 26
LiangZhiYUMao/pages/recommend/index.vue

@@ -224,28 +224,29 @@
 			</view>
 		</scroll-view>
 		<!-- 底部导航栏 -->
-		<view class="tabbar">
-			<view class="tabbar-item" @click="switchTab('index')">
-				<text class="tabbar-icon">🏠</text>
-				<text class="tabbar-text">首页</text>
-			</view>
-			<view class="tabbar-item" @click="switchTab('plaza')">
-				<text class="tabbar-icon">💕</text>
-				<text class="tabbar-text">动态</text>
-			</view>
-			<view class="tabbar-item active" @click="switchTab('recommend')">
-				<text class="tabbar-icon">👍</text>
-				<text class="tabbar-text">推荐</text>
-			</view>
-			<!-- <view class="tabbar-item" @click="switchTab('message')">
-				<text class="tabbar-icon">💬</text>
-				<text class="tabbar-text">消息</text>
-			</view> -->
-			<view class="tabbar-item" @click="switchTab('mine')">
-				<text class="tabbar-icon">👤</text>
-				<text class="tabbar-text">我的</text>
-			</view>
-		</view>
+				<view class="tabbar">
+					<view class="tabbar-item active" @click="switchTab('index')">
+						<text class="tabbar-icon">🏠</text>
+						<text class="tabbar-text">首页</text>
+					</view>
+					<view class="tabbar-item" @click="switchTab('plaza')">
+						<text class="tabbar-icon">💕</text>
+						<text class="tabbar-text">动态</text>
+					</view>
+					<view class="tabbar-item" @click="switchTab('recommend')">
+						<text class="tabbar-icon">👍</text>
+						<text class="tabbar-text">推荐</text>
+					</view>
+					<!-- <view class="tabbar-item" @click="switchTab('message')">
+						<text class="tabbar-icon">💬</text>
+						<text class="tabbar-text">消息</text>
+						<view v-if="unreadCount >= 1" class="tabbar-badge">{{ unreadCount }}</view>
+					</view> -->
+					<view class="tabbar-item" @click="switchTab('mine')">
+						<text class="tabbar-icon">👤</text>
+						<text class="tabbar-text">我的</text>
+					</view>
+				</view>
 	</view>
 </template>
 
@@ -1040,7 +1041,6 @@ export default {
 		}
 		
 		&.active {
-			background: #FFE5F1;
 			
 			.tab-icon {
 				color: #E91E63;
@@ -1281,6 +1281,13 @@ export default {
 						font-size: 44rpx;
 						margin-bottom: 5rpx;
 					}
+					
+					.first-icon{
+						width: 25px;
+						height: 25px;
+						margin-bottom: 5px;
+						margin-top: 5px;
+					}
 
 					.tabbar-text {
 						font-size: 22rpx;
@@ -1322,10 +1329,10 @@ export default {
 	}
 	
 	.tabbar-item.active {
-		background: #FFE5F1;
+		
 		
 		.tabbar-text { 
-			color: #E91E63; 
+			color: #E91E63;
 			font-weight: 700;
 		}
 	}
@@ -1335,7 +1342,7 @@ export default {
 		background: #FFFFFF; 
 		border-top-left-radius: 16rpx; 
 		border-top-right-radius: 16rpx; 
-		padding-bottom: env(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom); 
 		border-top: 2rpx solid #E0E0E0;
 	}
 	

+ 13 - 13
LiangZhiYUMao/pages/recommend/user-detail.vue

@@ -102,8 +102,9 @@
 						v-for="(item, index) in dynamicList" 
 						:key="item.dynamicId || index" 
 						class="dynamic-item"
-						@click="viewDynamicDetail(item)"
+						@click="viewDynamicDetail(item, index)"
 					>
+					
 						<!-- 动态内容 -->
 						<view class="dynamic-content" v-if="item.content">
 							<text class="dynamic-text">{{ item.content }}</text>
@@ -300,18 +301,17 @@ export default {
 		},
 		
 		// 查看动态详情
-		viewDynamicDetail(item) {
-			if (!item || !item.dynamicId) {
-				console.error('动态项无效:', item)
-				uni.showToast({
-					title: '动态信息无效',
-					icon: 'none'
-				})
-				return
-			}
-			uni.navigateTo({
-				url: `/pages/plaza/detail?dynamicId=${item.dynamicId}`
-			})
+		viewDynamicDetail(item,index) {
+			  if (!this.dynamicList[index] || !this.dynamicList[index].dynamicId) {
+			    uni.showToast({
+			      title: '动态信息无效',
+			      icon: 'none'
+			    })
+			    return
+			  }
+			  uni.navigateTo({
+			    url: `/pages/plaza/detail?dynamicId=${this.dynamicList[index].dynamicId}`
+			  })
 		},
 		
 		// 查看动态评论

+ 2 - 2
LiangZhiYUMao/pages/settings/id-verification.vue

@@ -56,7 +56,7 @@
 		
 		<!-- 提交按钮 -->
 		<view class="submit-container">
-			<button class="submit-btn" :disabled="!canSubmit" @click="handleSubmit">
+			<button class="submit-btn" :disabled="canSubmit" @click="handleSubmit">
 				{{ submitting ? '提交中...' : '提交认证' }}
 			</button>
 		</view>
@@ -158,7 +158,7 @@ export default {
 		
 		// 提交认证
 		async handleSubmit() {
-			if (!this.canSubmit) return
+			if (this.canSubmit) return
 			
 			// 验证身份证格式
 			if (!this.validateIdCard(this.formData.idCard)) {

+ 3 - 22
LiangZhiYUMao/pages/today-recommend/index.vue

@@ -72,10 +72,10 @@
 							<text class="action-icon">❤️</text>
 							<text class="action-text">喜欢</text>
 						</view>
-						<view class="action-btn chat-btn" @click="handleChat(user)">
+						<!-- <view class="action-btn chat-btn" @click="handleChat(user)">
 							<text class="action-icon">💬</text>
 							<text class="action-text">打招呼</text>
-						</view>
+						</view> -->
 					</view>
 				</view>
 			</view>
@@ -319,26 +319,7 @@
 				setTimeout(() => {
 					this.recommendUsers.splice(index, 1)
 
-					// 检查是否匹配成功(模拟)
-					const isMatch = Math.random() > 0.7 // 30%几率匹配成功
-					if (isMatch) {
-						setTimeout(() => {
-							uni.showModal({
-								title: '🎉 匹配成功!',
-								content: `恭喜您与${user.nickname}互相喜欢,现在可以开始聊天了!`,
-								showCancel: false,
-								confirmText: '开始聊天',
-								success: (modalRes) => {
-									if (modalRes.confirm) {
-										// 跳转到聊天页面
-										uni.navigateTo({
-											url: `/pages/message/chat?targetUserId=${user.userId || user.id}&targetUserName=${encodeURIComponent(user.nickname)}&targetUserAvatar=${encodeURIComponent(user.avatar || '')}`
-										})
-									}
-								}
-							})
-						}, 1000)
-					}
+					
 				}, 800)
 			},
 

+ 3 - 2
service/Essential/src/main/java/com/zhentao/controller/CourseOrderController.java

@@ -3,6 +3,7 @@ package com.zhentao.controller;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.zhentao.common.Result;
 import com.zhentao.dto.CoursePurchaseRequest;
+import com.zhentao.entity.Course;
 import com.zhentao.service.CourseOrderService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -80,9 +81,9 @@ public class CourseOrderController {
      * 获取用户已购买的课程ID列表
      */
     @GetMapping("/purchased")
-    public Result<List<Integer>> getPurchasedCourses(@RequestParam Long userId) {
+    public Result<List<Course>> getPurchasedCourses(@RequestParam Long userId) {
         try {
-            List<Integer> courseIds = courseOrderService.getPurchasedCourseIds(userId);
+            List<Course> courseIds = courseOrderService.getPurchasedCourseIds(userId);
             return Result.success(courseIds);
         } catch (Exception e) {
             log.error("获取已购买课程失败", e);

+ 122 - 0
service/Essential/src/main/java/com/zhentao/entity/Course.java

@@ -0,0 +1,122 @@
+package com.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 课程实体类
+ */
+@Data
+@TableName("courses")
+public class Course implements Serializable {
+    
+    private static final long serialVersionUID = 1L;
+    
+    /**
+     * 课程ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    @JsonProperty("id")  // 确保JSON序列化包含id字段
+    private Integer id;
+    
+    /**
+     * 课程分类名称(如:婚姻沟通技巧、情感修复等)
+     */
+    @TableField("category_name")
+    @JsonProperty("category_name")
+    private String categoryName;
+    
+    /**
+     * 课程名称
+     */
+    private String name;
+    
+    /**
+     * 讲师姓名
+     */
+    @TableField("instructor")
+    @JsonProperty("teacher_name")  // 前端使用 teacher_name
+    private String instructor;
+    
+    /**
+     * 课程描述
+     */
+    private String description;
+    
+    /**
+     * 课程详细内容
+     */
+    private String content;
+    
+    /**
+     * 课程价格
+     */
+    private Double price;
+    
+    /**
+     * 课程时长(分钟)
+     */
+    private Integer duration;
+    
+    /**
+     * 课程评分(如:4.9)
+     */
+    private Float rating;
+    
+    /**
+     * 已报名人数
+     */
+    @TableField("participants")
+    @JsonProperty("student_count")  // 前端使用 student_count
+    private Integer participants;
+    
+    /**
+     * 课程封面图URL
+     */
+    @TableField("cover_image")
+    @JsonProperty("cover_image")  // 前端使用 cover_image
+    private String coverImage;
+    
+    /**
+     * 是否推荐(1:推荐,0:不推荐)
+     */
+    private Integer isRecommended;
+    
+    /**
+     * 状态:0-禁用,1-启用
+     */
+    private Integer status;
+    
+    /**
+     * 课程创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createdTime;
+    
+    /**
+     * 课程更新时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updatedTime;
+    
+    // ==================== 手动添加的 getter/setter(临时方案) ====================
+    // 注意:理想情况下这些方法应该由 @Data 注解自动生成
+    // 但如果 Lombok 注解处理器未正确配置,需要手动添加
+    
+    public Integer getStatus() {
+        return status;
+    }
+    
+    public void setStatus(Integer status) {
+        this.status = status;
+    }
+}
+

+ 3 - 2
service/Essential/src/main/java/com/zhentao/mapper/CourseOrderMapper.java

@@ -1,6 +1,7 @@
 package com.zhentao.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zhentao.entity.Course;
 import com.zhentao.entity.CourseOrder;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
@@ -23,8 +24,8 @@ public interface CourseOrderMapper extends BaseMapper<CourseOrder> {
     /**
      * 查询用户已购买的课程ID列表
      */
-    @Select("SELECT course_id FROM course_orders WHERE user_id = #{userId} AND status = 1")
-    List<Integer> selectPurchasedCourseIds(@Param("userId") Long userId);
+    @Select("SELECT cs.* FROM course_orders c left join courses cs on c.course_id = cs.id WHERE c.user_id = #{userId} AND c.status = 1")
+    List<Course> selectPurchasedCourseIds(@Param("userId") Long userId);
     
     /**
      * 检查用户是否已购买某课程

+ 2 - 1
service/Essential/src/main/java/com/zhentao/service/CourseOrderService.java

@@ -1,6 +1,7 @@
 package com.zhentao.service;
 
 import com.github.binarywang.wxpay.exception.WxPayException;
+import com.zhentao.entity.Course;
 
 import java.util.List;
 import java.util.Map;
@@ -44,5 +45,5 @@ public interface CourseOrderService {
      * @param userId 用户ID
      * @return 课程ID列表
      */
-    List<Integer> getPurchasedCourseIds(Long userId);
+    List<Course> getPurchasedCourseIds(Long userId);
 }

+ 2 - 1
service/Essential/src/main/java/com/zhentao/service/impl/CourseOrderServiceImpl.java

@@ -9,6 +9,7 @@ import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.service.WxPayService;
 import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
 import com.github.binarywang.wxpay.util.SignUtils;
+import com.zhentao.entity.Course;
 import com.zhentao.entity.CourseOrder;
 import com.zhentao.entity.Wx;
 import com.zhentao.pojo.Users;
@@ -122,7 +123,7 @@ public class CourseOrderServiceImpl implements CourseOrderService {
     }
 
     @Override
-    public List<Integer> getPurchasedCourseIds(Long userId) {
+    public List<Course> getPurchasedCourseIds(Long userId) {
         return courseOrderMapper.selectPurchasedCourseIds(userId);
     }
 

+ 10 - 0
service/admin/src/main/java/com/zhentao/controller/PointsOrderController.java

@@ -4,9 +4,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.zhentao.common.Result;
 import com.zhentao.entity.PointsOrder;
+import com.zhentao.entity.PointsProduct;
 import com.zhentao.mapper.PointsOrderMapper;
 import com.zhentao.service.PointsOrderService;
+import com.zhentao.service.PointsProductService;
 import com.zhentao.service.SystemMessagesService;
+import com.zhentao.service.UserService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -30,6 +33,10 @@ public class PointsOrderController {
     
     @Autowired
     private SystemMessagesService systemMessagesService;
+    @Autowired
+    private PointsProductService productService;
+    @Autowired
+    private UserService userService;
     
     /**
      * 积分订单列表(分页查询)
@@ -126,6 +133,9 @@ public class PointsOrderController {
                 if (reviewFail == null || reviewFail.trim().isEmpty()) {
                     return Result.error("审核不通过时必须填写审核失败说明");
                 }
+                PointsProduct byId = productService.getById(order.getProductId());
+                Boolean boo = userService.updatePoints(byId.getPointsPrice(), order.getMakerId());
+
                 order.setReviewFail(reviewFail.trim());
             } else {
                 // 审核通过时清空失败说明

+ 4 - 0
service/admin/src/main/java/com/zhentao/mapper/AdminUserMapper.java

@@ -2,6 +2,8 @@ package com.zhentao.mapper;
 
 import com.zhentao.entity.AdminUser;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
 /**
 * @author 联想
@@ -9,8 +11,10 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 * @createDate 2025-12-11 16:08:57
 * @Entity com.zhentao.entity.AdminUser
 */
+@Mapper
 public interface AdminUserMapper extends BaseMapper<AdminUser> {
 
+    Boolean updatePoints(@Param("pointsPrice") Integer pointsPrice,@Param("id") Long id);
 }
 
 

+ 2 - 0
service/admin/src/main/java/com/zhentao/service/UserService.java

@@ -60,5 +60,7 @@ public interface UserService {
      * 检查用户是否是超级管理员
      */
     boolean isSuperAdmin(Integer userId);
+
+    Boolean updatePoints(Integer pointsPrice, Long id);
 }
 

+ 7 - 2
service/admin/src/main/java/com/zhentao/service/impl/UserServiceImpl.java

@@ -20,7 +20,6 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.UUID;
-import java.util.stream.Collectors;
 
 /**
  * 用户服务实现
@@ -197,7 +196,13 @@ public class UserServiceImpl implements UserService {
     public boolean isSuperAdmin(Integer userId) {
         return hasRole(userId, "SUPER_ADMIN");
     }
-    
+
+    @Override
+    public Boolean updatePoints(Integer pointsPrice, Long id) {
+        return adminUserMapper.updatePoints(pointsPrice,id);
+
+    }
+
     /**
      * 生成随机盐
      */

+ 3 - 0
service/admin/src/main/resources/com/zhentao/mapper/AdminUserMapper.xml

@@ -22,4 +22,7 @@
         id,username,password,salt,real_name,phone,
         email,status,create_time,update_time,last_login_time
     </sql>
+    <update id="updatePoints">
+        update matchmakers set points = points + #{pointsPrice} where matchmaker_id = #{id}
+    </update>
 </mapper>

+ 102 - 94
service/dynamic/src/main/java/com/zhentao/entity/DynamicComments.java

@@ -1,94 +1,102 @@
-package com.zhentao.entity;
-
-import com.baomidou.mybatisplus.annotation.*;
-import lombok.Data;
-import java.time.LocalDateTime;
-
-/**
- * 动态评论实体类
- */
-@Data
-@TableName("dynamic_comments")
-public class DynamicComments {
-    /**
-     * 评论ID
-     */
-    @TableId(value = "comment_id", type = IdType.AUTO)
-    private Integer commentId;
-
-    /**
-     * 动态ID
-     */
-    private Integer dynamicId;
-
-    /**
-     * 用户ID
-     */
-    private Integer userId;
-
-    /**
-     * 父评论ID,0表示顶级评论
-     */
-    private Integer parentCommentId;
-
-    /**
-     * 评论内容
-     */
-    private String content;
-
-    /**
-     * 评论图片(JSON数组字符串)
-     */
-    private String imageUrls;
-
-    /**
-     * 0-待审核 1-审核通过 2-审核拒绝
-     */
-    private Integer auditStatus;
-
-    /**
-     * 审核备注
-     */
-    private String auditRemark;
-
-    /**
-     * 评论点赞数
-     */
-    private Integer likeCount;
-
-    /**
-     * 回复数
-     */
-    private Integer replyCount;
-
-    /**
-     * 是否网络评论
-     */
-    private Boolean isNetworkComment;
-
-    /**
-     * 0-删除 1-正常
-     */
-    @TableLogic
-    private Integer status;
-
-    /**
-     * 创建时间
-     */
-    @TableField(fill = FieldFill.INSERT)
-    private LocalDateTime createdAt;
-
-    /**
-     * 更新时间
-     */
-    @TableField(fill = FieldFill.INSERT_UPDATE)
-    private LocalDateTime updatedAt;
-
-    /**
-     * 用户信息(非数据库字段,用于关联查询)
-     */
-    @TableField(exist = false)
-    private Users user;
-}
-
-
+package com.zhentao.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 动态评论实体类
+ */
+@Data
+@TableName("dynamic_comments")
+public class DynamicComments {
+    /**
+     * 评论ID
+     */
+    @TableId(value = "comment_id", type = IdType.AUTO)
+    private Integer commentId;
+
+    /**
+     * 动态ID
+     */
+    private Integer dynamicId;
+
+    /**
+     * 用户ID
+     */
+    private Integer userId;
+
+    /**
+     * 父评论ID,0表示顶级评论
+     */
+    private Integer parentCommentId;
+
+    /**
+     * 评论内容
+     */
+    private String content;
+
+    /**
+     * 评论图片(JSON数组字符串)
+     */
+    private String imageUrls;
+
+    /**
+     * 0-待审核 1-审核通过 2-审核拒绝
+     */
+    private Integer auditStatus;
+
+    /**
+     * 审核备注
+     */
+    private String auditRemark;
+
+    /**
+     * 评论点赞数
+     */
+    private Integer likeCount;
+
+    /**
+     * 回复数
+     */
+    private Integer replyCount;
+
+    /**
+     * 是否网络评论
+     */
+    private Boolean isNetworkComment;
+
+    /**
+     * 0-删除 1-正常
+     */
+    @TableLogic
+    private Integer status;
+
+    /**
+     * 创建时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createdAt;
+
+    /**
+     * 更新时间
+     */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updatedAt;
+
+    /**
+     * 用户信息(非数据库字段,用于关联查询)
+     */
+    @TableField(exist = false)
+    private Users user;
+    
+    /**
+     * 子回复列表(非数据库字段,用于前端展示)
+     */
+    @TableField(exist = false)
+    private List<DynamicComments> replies;
+}
+
+
+

+ 36 - 6
service/dynamic/src/main/java/com/zhentao/service/impl/CommentServiceImpl.java

@@ -2,6 +2,8 @@ package com.zhentao.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import com.zhentao.common.PageResult;
 import com.zhentao.entity.CommentLikes;
 import com.zhentao.entity.DynamicComments;
@@ -15,11 +17,8 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDateTime;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.core.JsonProcessingException;
+import java.util.*;
+import java.util.stream.Collectors;
 
 @Service
 public class CommentServiceImpl implements CommentService {
@@ -73,12 +72,41 @@ public class CommentServiceImpl implements CommentService {
 
     @Override
     public PageResult<DynamicComments> listComments(Integer dynamicId, Integer pageNum, Integer pageSize) {
+        // 1. 首先查询所有顶级评论(parentCommentId = 0)
         Page<DynamicComments> page = new Page<>(pageNum, pageSize);
         Page<DynamicComments> pg = commentsMapper.selectPage(page, new QueryWrapper<DynamicComments>()
                 .eq("dynamic_id", dynamicId)
+                .eq("parent_comment_id", 0) // 只查询顶级评论
                 .eq("status", 1)
                 .orderByDesc("created_at"));
         
+        List<DynamicComments> topLevelComments = pg.getRecords();
+        
+        if (!topLevelComments.isEmpty()) {
+            // 2. 查询所有子回复(parentCommentId > 0)
+            List<Integer> topCommentIds = topLevelComments.stream()
+                    .map(DynamicComments::getCommentId)
+                    .collect(Collectors.toList());
+            
+            List<DynamicComments> allReplies = commentsMapper.selectList(new QueryWrapper<DynamicComments>()
+                    .eq("dynamic_id", dynamicId)
+                    .in("parent_comment_id", topCommentIds) // 只查询当前页顶级评论的子回复
+                    .eq("status", 1)
+                    .orderByAsc("created_at")); // 子回复按时间正序排列
+            
+            // 3. 将子回复按照父评论ID分组
+            Map<Integer, List<DynamicComments>> repliesByParentId = allReplies.stream()
+                    .collect(Collectors.groupingBy(DynamicComments::getParentCommentId));
+            
+            // 4. 将每个父评论对应的子回复列表设置到父评论的replies字段中
+            for (DynamicComments comment : topLevelComments) {
+                List<DynamicComments> replies = repliesByParentId.get(comment.getCommentId());
+                if (replies != null && !replies.isEmpty()) {
+                    comment.setReplies(replies);
+                }
+            }
+        }
+        
         // 同步更新动态的评论数(统计实际评论总数,确保数据一致性)
         // 统计所有评论(包括回复)
         Long actualCommentCount = commentsMapper.selectCount(new QueryWrapper<DynamicComments>()
@@ -93,7 +121,7 @@ public class CommentServiceImpl implements CommentService {
                             .set("comment_count", actualCommentCount.intValue()));
         }
         
-        return new PageResult<>(pg.getRecords(), pg.getTotal(), pg.getCurrent(), pg.getSize());
+        return new PageResult<>(topLevelComments, pg.getTotal(), pg.getCurrent(), pg.getSize());
     }
 
     @Override
@@ -136,3 +164,5 @@ public class CommentServiceImpl implements CommentService {
 }
 
 
+
+

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików