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

Merge branch 'refs/heads/test' into lisijia

李思佳 пре 3 месеци
родитељ
комит
f32846d9cd
29 измењених фајлова са 920 додато и 712 уклоњено
  1. 2 0
      LiangZhiYUMao/main.js
  2. 1 1
      LiangZhiYUMao/package-lock.json
  3. 89 3
      LiangZhiYUMao/pages/activities/detail.vue
  4. 167 141
      LiangZhiYUMao/pages/index/index.vue
  5. 16 5
      LiangZhiYUMao/pages/message/chat.vue
  6. 133 15
      LiangZhiYUMao/pages/message/index.vue
  7. 57 40
      LiangZhiYUMao/pages/mine/index.vue
  8. 184 443
      LiangZhiYUMao/pages/page3/page3.vue
  9. 56 34
      LiangZhiYUMao/pages/plaza/index.vue
  10. 80 13
      LiangZhiYUMao/pages/recommend/index.vue
  11. 43 8
      LiangZhiYUMao/pages/success-case/detail.vue
  12. BIN
      LiangZhiYUMao/static/login-bg.png
  13. BIN
      LiangZhiYUMao/static/logo.png
  14. BIN
      LiangZhiYUMao/static/logo111.png
  15. BIN
      LiangZhiYUMao/static/romance.zip
  16. BIN
      LiangZhiYUMao/static/wechat-icon.png
  17. 26 0
      LiangZhiYUMao/store/index.js
  18. 8 2
      LiangZhiYUMao/utils/api.js
  19. 6 0
      package-lock.json
  20. 1 1
      service/homePage/src/main/java/com/zhentao/controller/ActivityController.java
  21. 2 1
      service/homePage/src/main/java/com/zhentao/service/ActivityService.java
  22. 9 1
      service/homePage/src/main/java/com/zhentao/service/impl/ActivityServiceImpl.java
  23. BIN
      service/homePage/target/classes/com/zhentao/controller/ActivityController.class
  24. BIN
      service/homePage/target/classes/com/zhentao/service/ActivityService.class
  25. BIN
      service/homePage/target/classes/com/zhentao/service/impl/ActivityServiceImpl.class
  26. 7 4
      service/websocket/src/main/java/com/zhentao/controller/ChatFriendController.java
  27. 2 0
      service/websocket/src/main/java/com/zhentao/service/ChatFriendService.java
  28. 20 0
      service/websocket/src/main/java/com/zhentao/service/impl/ChatFriendServiceImpl.java
  29. 11 0
      service/websocket/src/main/java/com/zhentao/vo/GetblockVo.java

+ 2 - 0
LiangZhiYUMao/main.js

@@ -1,11 +1,13 @@
 import Vue from 'vue'
 import App from './App'
+import store from './store'
 
 Vue.config.productionTip = false
 
 App.mpType = 'app'
 
 const app = new Vue({
+	store,
 	...App
 })
 app.$mount()

+ 1 - 1
LiangZhiYUMao/package-lock.json

@@ -395,7 +395,7 @@
         },
         "node_modules/tim-wx-sdk": {
             "version": "2.27.6",
-            "resolved": "https://registry.npmjs.org/tim-wx-sdk/-/tim-wx-sdk-2.27.6.tgz",
+
             "integrity": "sha512-zB+eRdmigdhEDeqrXC0bLJonUQZzS5uKNPLFtrje503WAnmuxVQjq/n4Zle4FYHG4FiKHKhsrVd0aCYXABlFEg==",
             "license": "ISC"
         }

+ 89 - 3
LiangZhiYUMao/pages/activities/detail.vue

@@ -11,7 +11,8 @@
 
 		<!-- 活动封面 -->
 		<view class="activity-cover">
-			<image :src="activity.coverImage" class="cover-image" mode="aspectFill"></image>
+			<image :src="activity.coverImage" class="cover-image" mode="aspectFill" 
+				@error="handleImageError" @load="handleImageLoad"></image>
 			<view class="cover-mask">
 				<view class="activity-tag" v-if="activity.isHot">🔥 热门活动</view>
 			</view>
@@ -25,7 +26,7 @@
 				<view class="info-item">
 					<text class="info-icon">⏰</text>
 					<text class="info-label">活动时间:</text>
-					<text class="info-value">{{ formatTime(activity.startTime) }}</text>
+					<text class="info-value">{{ formatActivityTime(activity.startTime, activity.endTime) }}</text>
 				</view>
 				<view class="info-item">
 					<text class="info-icon">📍</text>
@@ -106,7 +107,20 @@
 				try {
 					const data = await api.activity.getDetail(this.activityId)
 					if (data) {
-						this.activity = data
+						// 处理字段映射,确保前端使用的字段名统一
+						this.activity = {
+							...data,
+							coverImage: this.validateImageUrl(data.coverImage || data.cover_image || data.imageUrl || data.image_url),
+							startTime: data.startTime || data.start_time || data.startDate || data.start_date || '',
+							endTime: data.endTime || data.end_time || data.endDate || data.end_date || '',
+							location: data.location || data.address || data.venue || '待定',
+							maxParticipants: data.maxParticipants || data.max_participants || null,
+							actualParticipants: data.actualParticipants || data.actual_participants || 0,
+							price: data.price || data.fee || 0,
+							description: data.description || data.desc || '暂无介绍',
+							notes: data.notes || data.notice || '请准时参加活动'
+						}
+						console.log('活动详情加载成功:', this.activity)
 					}
 				} catch (error) {
 					console.error('加载活动详情失败:', error)
@@ -117,9 +131,70 @@
 				}
 			},
 
+			// 验证并修正图片URL
+			validateImageUrl(url) {
+				if (!url || url === 'null' || url === 'undefined') {
+					return DEFAULT_IMAGES.activity || '/static/default-activity.jpg'
+				}
+
+				let cleanedUrl = String(url).trim()
+				const httpIndex = cleanedUrl.indexOf('http')
+				if (httpIndex > 0) {
+					cleanedUrl = cleanedUrl.slice(httpIndex)
+				}
+
+				if (!cleanedUrl) {
+					return DEFAULT_IMAGES.activity || '/static/default-activity.jpg'
+				}
+				
+				// 如果是相对路径,添加协议和域名
+				if (cleanedUrl.startsWith('/')) {
+					return `http://115.190.125.125:9000${cleanedUrl}`
+				}
+				
+				// 如果已经是完整URL,直接返回
+				if (cleanedUrl.startsWith('http://') || cleanedUrl.startsWith('https://')) {
+					return cleanedUrl
+				}
+				
+				// 其他情况返回默认图片
+				return DEFAULT_IMAGES.activity || '/static/default-activity.jpg'
+			},
+
 			// 格式化时间
 			formatTime,
 
+			// 格式化活动时间(显示开始和结束时间)
+			formatActivityTime(startTime, endTime) {
+				if (!startTime) return '时间待定'
+				
+				try {
+					const start = new Date(startTime)
+					const startMonth = start.getMonth() + 1
+					const startDay = start.getDate()
+					const startHour = start.getHours().toString().padStart(2, '0')
+					const startMinute = start.getMinutes().toString().padStart(2, '0')
+					
+					let timeStr = `${startMonth}月${startDay}日 ${startHour}:${startMinute}`
+					
+					// 如果有结束时间,添加结束时间
+					if (endTime) {
+						const end = new Date(endTime)
+						const endMonth = end.getMonth() + 1
+						const endDay = end.getDate()
+						const endHour = end.getHours().toString().padStart(2, '0')
+						const endMinute = end.getMinutes().toString().padStart(2, '0')
+						
+						timeStr += ` - ${endMonth}月${endDay}日 ${endHour}:${endMinute}`
+					}
+					
+					return timeStr
+				} catch (error) {
+					console.error('时间格式化失败:', error)
+					return '时间待定'
+				}
+			},
+
 			// 处理报名
 			handleRegister() {
 				// 获取当前用户ID
@@ -182,6 +257,17 @@
 			// 返回
 			goBack() {
 				uni.navigateBack()
+			},
+
+			// 图片加载错误处理
+			handleImageError(e) {
+				console.warn('活动封面图片加载失败:', e)
+				this.activity.coverImage = DEFAULT_IMAGES.activity || '/static/default-activity.jpg'
+			},
+
+			// 图片加载成功
+			handleImageLoad(e) {
+				console.log('活动封面图片加载成功')
 			}
 		}
 	}

+ 167 - 141
LiangZhiYUMao/pages/index/index.vue

@@ -18,12 +18,12 @@
 		</view> -->
 
 		<!-- 轮播图 -->
-		<view class="banner-section">
+		<view class="banner-section" v-if="bannerList && bannerList.length > 0">
 			<swiper class="banner-swiper" :indicator-dots="true" :autoplay="true" :interval="4000" :duration="1000"
 				:circular="true" indicator-color="rgba(255, 255, 255, 0.5)" indicator-active-color="#E91E63">
 				<swiper-item v-for="(banner, index) in bannerList" :key="index">
 					<view class="banner-item" @click="handleBannerClick(banner)">
-						<image :src="banner.cover_image" class="banner-image" mode="aspectFill"
+						<image :src="banner.cover_image || banner.coverImage" class="banner-image" mode="aspectFill"
 							:data-section="'banner'" :data-index="index"
 							@error="handleImageError" @load="handleImageLoad"></image>
 						<view class="banner-mask">
@@ -37,7 +37,7 @@
 		</view>
 
 		<!-- 小喇叭公告栏 -->
-		<view class="notice-bar">
+		<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">
@@ -67,15 +67,15 @@
 				<view class="case-card" v-for="(caseItem, index) in successCases.slice(0, 2)" :key="index" 
 					@click="handleSuccessCaseClick(caseItem)">
 					<view class="case-image-wrapper">
-						<image :src="caseItem.imageUrl || caseItem.image_url" class="case-image" mode="aspectFill"
+						<image :src="caseItem.imageUrl" class="case-image" mode="aspectFill"
 							:data-section="'successCase'" :data-index="index"
 							@error="handleImageError" @load="handleImageLoad"></image>
 					</view>
 					<view class="case-content">
-						<view class="couple-names">{{ caseItem.maleUserNickname || caseItem.male_user_nickname }}&{{ caseItem.femaleUserNickname || caseItem.female_user_nickname }}</view>
+						<view class="couple-names">{{ caseItem.maleUserNickname || '先生' }}&{{ caseItem.femaleUserNickname || '女士' }}</view>
 						<view class="case-quote">{{ caseItem.quote || '我们通过平台找到了彼此❤️' }}</view>
 						<view class="case-meta">
-							<text class="marriage-date">相识于{{ formatMarriageDate(caseItem.marriageDate || caseItem.marriage_date) }}</text>
+							<text class="marriage-date">相识于{{ formatMarriageDate(caseItem.marriageDate) }}</text>
 						</view>
 					</view>
 				</view>
@@ -102,7 +102,7 @@
 							</view>
 							<view class="activity-info-static">
 								<view class="activity-name-static">{{ activity.name }}</view>
-								<view class="activity-time-static">{{ formatActivityTime(activity.startTime) }}</view>
+								<view class="activity-time-static">{{ formatActivityTime(activity.startTime, activity.endTime) }}</view>
 							</view>
 						</view>
 						<!-- 第二组活动(用于无缝滚动) -->
@@ -116,7 +116,7 @@
 							</view>
 							<view class="activity-info-static">
 								<view class="activity-name-static">{{ activity.name }}</view>
-								<view class="activity-time-static">{{ formatActivityTime(activity.startTime) }}</view>
+								<view class="activity-time-static">{{ formatActivityTime(activity.startTime, activity.endTime) }}</view>
 							</view>
 						</view>
 					</view>
@@ -217,7 +217,7 @@
 </template>
 
 <script>
-	import api from '@/utils/api.js'
+	import api, { request } from '@/utils/api.js'
     import { formatTime, formatCountdown, isLoggedIn, goToLogin } from '@/utils/util.js'
 	import { DEFAULT_IMAGES, ACTIVITY_TYPES } from '@/config/index.js'
 	
@@ -230,34 +230,34 @@
 					userId: null
 				},
 				matchCount: 3,
-				unreadCount: 0,
+			
 
 				// 轮播图数据
 				bannerList: [
-					// {
-					// 	id: 1,
-					// 	name: '七夕单身派对',
-					// 	subtitle: '仅剩5席',
-					// 	cover_image: DEFAULT_IMAGES.banner,
-					// 	type: 'activity',
-					// 	targetId: 1
-					// },
-					// {
-					// 	id: 2,
-					// 	name: '情感沟通课',
-					// 	subtitle: '限时5折',
-					// 	cover_image: DEFAULT_IMAGES.banner,
-					// 	type: 'course',
-					// 	targetId: 1
-					// },
-					// {
-					// 	id: 3,
-					// 	name: '他们通过我们结婚了!',
-					// 	subtitle: '查看更多成功案例',
-					// 	cover_image: DEFAULT_IMAGES.banner,
-					// 	type: 'case',
-					// 	targetId: 1
-					// }
+					{
+						id: 1,
+						name: '七夕单身派对',
+						subtitle: '仅剩5席',
+						cover_image: DEFAULT_IMAGES.banner,
+						type: 'activity',
+						targetId: 1
+					},
+					{
+						id: 2,
+						name: '情感沟通课',
+						subtitle: '限时5折',
+						cover_image: DEFAULT_IMAGES.banner,
+						type: 'course',
+						targetId: 1
+					},
+					{
+						id: 3,
+						name: '他们通过我们结婚了!',
+						subtitle: '查看更多成功案例',
+						cover_image: DEFAULT_IMAGES.banner,
+						type: 'case',
+						targetId: 1
+					}
 				],
 
 				// 公告数据
@@ -271,10 +271,14 @@
 			functionList: [
 				{ id: 1, name: '星命测算', icon: '💖', path: '/pages/astrology/index', bgColor: '#FF6B9D', iconColor: '#FFFFFF', gradient: 'linear-gradient(135deg, #FF6B9D 0%, #FF8EAB 100%)' },
 				{ id: 2, name: '红娘列表', icon: '👤', path: '/pages/matchmakers/list', bgColor: '#6BC5F8', iconColor: '#FFFFFF', gradient: 'linear-gradient(135deg, #6BC5F8 0%, #87CEEB 100%)' },
+<<<<<<< HEAD
 				// { id: 3, name: '加入红娘', icon: '💕', path: '/pages/part-time-matchmaker/index', bgColor: '#9B7EDE', iconColor: '#FFFFFF', gradient: 'linear-gradient(135deg, #9B7EDE 0%, #B19CD9 100%)' },
+=======
+				{ id: 3, name: '加入红娘', icon: '💕', path: '/pages/part-time-matchmaker/index', bgColor: '#9B7EDE', iconColor: '#FFFFFF', gradient: 'linear-gradient(135deg, #9B7EDE 0%, #B19CD9 100%)' },
+>>>>>>> yh
 				{ id: 4, name: '精品课程', icon: '📚', path: '/pages/courses/list', bgColor: '#FF8C42', iconColor: '#FFFFFF', gradient: 'linear-gradient(135deg, #FF8C42 0%, #FFA366 100%)' },
 				{ id: 5, name: '今日缘分', icon: '💝', path: '/pages/recommend/index', bgColor: '#FF69B4', iconColor: '#FFFFFF', gradient: 'linear-gradient(135deg, #FF69B4 0%, #FF8CC8 100%)', needLogin: true },
-				// { id: 6, name: '专属定制', icon: '🎁', path: '/pages/customize/index', bgColor: '#FFA500', iconColor: '#FFFFFF', gradient: 'linear-gradient(135deg, #FFA500 0%, #FFB84D 100%)' }
+				{ id: 6, name: '专属定制', icon: '🎁', path: '/pages/customize/index', bgColor: '#FFA500', iconColor: '#FFFFFF', gradient: 'linear-gradient(135deg, #FFA500 0%, #FFB84D 100%)' }
 			],
 
 				// 热门活动
@@ -352,7 +356,12 @@
 				imageErrorCount: 0
 			}
 		},
-
+			computed: {
+			    // 从Vuex获取全局未读数
+			    unreadCount() {
+			      return this.$store.getters.getTotalUnread || 0;
+			    }
+			  },
 		onLoad() {
 			this.loadUserInfo()
 			this.loadBannerData()
@@ -366,7 +375,10 @@
 		onUnload() {
 			// 清理资源
 		},
-
+onShow() {
+  // 如果需要主动刷新未读数,可以在这里触发
+  this.$store.dispatch('updateTotalUnread', this.unreadCount);
+},
 		methods: {
 			// 加载用户信息
 			loadUserInfo() {
@@ -418,12 +430,13 @@
 			async loadTodayRecommend() {
 				try {
 					// 调用 API 获取推荐用户
-					const data = await api.recommend.getTodayUsers()
+					const data = await api.recommend.getTodayRecommend()
 					if (data && data.length > 0) {
 					    this.todayRecommendUsers = data
 					}
 				} catch (error) {
 					console.error('获取推荐用户失败:', error)
+					// 使用默认数据
 				}
 			},
 
@@ -445,9 +458,9 @@
 			// 加载热门活动
 			async loadHotActivities() {
 				try {
-					// 调用 API 获取热门活动
+					// 调用 API 获取活动列表(获取所有状态为1的活动,而不是只获取热门活动
 					const data = await api.activity.getList({
-						type: 'hot',
+						status: 1,
 						limit: 10
 					})
 					if (data && data.length > 0) {
@@ -458,6 +471,7 @@
 							coverImage: this.ensureRomanticCover(rawCover, index),
 							name: activity.name || activity.title || '',
 							startTime: activity.startTime || activity.start_time || activity.startDate || '',
+							endTime: activity.endTime || activity.end_time || activity.endDate || '',
 							status: activity.status || 0
 						}})
 					} else {
@@ -533,16 +547,30 @@
 				}
 			},
 
-			// 格式化活动时间
-			formatActivityTime(timeStr) {
-				if (!timeStr) return '时间待定'
+			// 格式化活动时间(支持开始和结束时间)
+			formatActivityTime(startTime, endTime) {
+				if (!startTime) return '时间待定'
 				try {
-					const date = new Date(timeStr)
-					const month = date.getMonth() + 1
-					const day = date.getDate()
-					const hour = date.getHours()
-					const minute = date.getMinutes()
-					return `${month}月${day}日 ${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`
+					const start = new Date(startTime)
+					const startMonth = start.getMonth() + 1
+					const startDay = start.getDate()
+					const startHour = start.getHours().toString().padStart(2, '0')
+					const startMinute = start.getMinutes().toString().padStart(2, '0')
+					
+					let timeStr = `${startMonth}月${startDay}日 ${startHour}:${startMinute}`
+					
+					// 如果有结束时间,添加结束时间
+					if (endTime) {
+						const end = new Date(endTime)
+						const endMonth = end.getMonth() + 1
+						const endDay = end.getDate()
+						const endHour = end.getHours().toString().padStart(2, '0')
+						const endMinute = end.getMinutes().toString().padStart(2, '0')
+						
+						timeStr += ` - ${endMonth}月${endDay}日 ${endHour}:${endMinute}`
+					}
+					
+					return timeStr
 				} catch (error) {
 					return '时间待定'
 				}
@@ -559,34 +587,38 @@
 					if (data && data.length > 0) {
 						this.successCases = data.map(caseItem => ({
 							...caseItem,
-							image_url: this.validateImageUrl(caseItem.image_url || caseItem.imageUrl || DEFAULT_IMAGES.couple)
+							maleUserNickname: caseItem.maleUserNickname || caseItem.male_user_nickname || '先生',
+							femaleUserNickname: caseItem.femaleUserNickname || caseItem.female_user_nickname || '女士',
+							imageUrl: this.validateImageUrl(caseItem.imageUrl || caseItem.image_url || DEFAULT_IMAGES.couple),
+							quote: caseItem.quote || '我们通过平台找到了彼此❤️',
+							marriageDate: caseItem.marriageDate || caseItem.marriage_date || ''
 						}))
 					} else {
 						// 使用默认数据
 						this.successCases = [
 							{
-								case_no: 'CASE001',
-								male_user_nickname: '张先生',
-								female_user_nickname: '李女士',
-								image_url: DEFAULT_IMAGES.couple,
+								caseNo: 'CASE001',
+								maleUserNickname: '张先生',
+								femaleUserNickname: '李女士',
+								imageUrl: DEFAULT_IMAGES.couple,
 								quote: '从第一次咖啡约会到领证,只用了90天',
-								marriage_date: '2024-08-20'
+								marriageDate: '2024-08-20'
 							},
 							{
-								case_no: 'CASE002',
-								male_user_nickname: '王先生',
-								female_user_nickname: '赵女士',
-								image_url: DEFAULT_IMAGES.couple,
+								caseNo: 'CASE002',
+								maleUserNickname: '王先生',
+								femaleUserNickname: '赵女士',
+								imageUrl: DEFAULT_IMAGES.couple,
 								quote: '感谢红娘的专业服务,让我们相遇相知',
-								marriage_date: '2024-09-15'
+								marriageDate: '2024-09-15'
 							},
 							{
-								case_no: 'CASE003',
-								male_user_nickname: '刘先生',
-								female_user_nickname: '陈女士',
-								image_url: DEFAULT_IMAGES.couple,
+								caseNo: 'CASE003',
+								maleUserNickname: '刘先生',
+								femaleUserNickname: '陈女士',
+								imageUrl: DEFAULT_IMAGES.couple,
 								quote: '缘分天注定,感恩平台让我们走到一起',
-								marriage_date: '2024-10-01'
+								marriageDate: '2024-10-01'
 							}
 						]
 					}
@@ -595,20 +627,20 @@
 					// 使用默认数据
 					this.successCases = [
 						{
-							case_no: 'CASE001',
-							male_user_nickname: '张先生',
-							female_user_nickname: '李女士',
-							image_url: DEFAULT_IMAGES.couple,
+							caseNo: 'CASE001',
+							maleUserNickname: '张先生',
+							femaleUserNickname: '李女士',
+							imageUrl: DEFAULT_IMAGES.couple,
 							quote: '从第一次咖啡约会到领证,只用了90天',
-							marriage_date: '2024-08-20'
+							marriageDate: '2024-08-20'
 						},
 						{
-							case_no: 'CASE002',
-							male_user_nickname: '王先生',
-							female_user_nickname: '赵女士',
-							image_url: DEFAULT_IMAGES.couple,
+							caseNo: 'CASE002',
+							maleUserNickname: '王先生',
+							femaleUserNickname: '赵女士',
+							imageUrl: DEFAULT_IMAGES.couple,
 							quote: '感谢红娘的专业服务,让我们相遇相知',
-							marriage_date: '2024-09-15'
+							marriageDate: '2024-09-15'
 						}
 					]
 				}
@@ -658,7 +690,7 @@
 
 			// 处理成功案例点击
 			handleSuccessCaseClick(caseItem) {
-				const caseNo = caseItem.case_no || caseItem.caseNo
+				const caseNo = caseItem.caseNo || caseItem.case_no
 				uni.navigateTo({
 					url: `/pages/success-case/detail?caseNo=${caseNo}`
 				})
@@ -693,12 +725,13 @@
 				switch (section) {
 					case 'banner':
 						setFallback(this.bannerList, index, 'cover_image', fallbackMap.banner)
+						setFallback(this.bannerList, index, 'coverImage', fallbackMap.banner)
 						break
 					case 'activity':
 						setFallback(this.hotActivities, index, 'coverImage', this.getRomanticImage(index))
 						break
 					case 'successCase':
-						setFallback(this.successCases, index, 'image_url', fallbackMap.successCase, ['imageUrl'])
+						setFallback(this.successCases, index, 'imageUrl', fallbackMap.successCase)
 						break
 					case 'todayRecommend':
 						setFallback(this.todayRecommendUsers, index, 'avatar', fallbackMap.todayRecommend)
@@ -1067,9 +1100,9 @@
 	/* 功能入口 */
 	.function-grid {
 		display: grid;
-		grid-template-columns: repeat(4, 1fr);
+		grid-template-columns: repeat(3, 1fr);
 		gap: 24rpx;
-		margin: 10rpx 1rpx;
+		margin: 25rpx 35rpx;
 		padding: 30rpx;
 		background: #FFFFFF;
 		border-radius: 16rpx;
@@ -1526,70 +1559,46 @@
 
 	/* 底部导航栏 */
 	.tabbar {
-		position: fixed;
-		bottom: 0;
-		left: 0;
-		right: 0;
-		display: flex;
-		background: #FFFFFF;
-		border-top: 2rpx solid #E0E0E0;
-		padding-bottom: constant(safe-area-inset-bottom);
-		padding-bottom: env(safe-area-inset-bottom);
-		z-index: 999;
-
-		.tabbar-item {
-			flex: 1;
-			display: flex;
-			flex-direction: column;
-			align-items: center;
-			justify-content: center;
-			padding: 18rpx 0;
-			position: relative;
-			transition: all 0.2s ease;
-			
-			&:active {
-				background: #F5F5F5;
-			}
-
-			.tabbar-icon {
-				font-size: 46rpx;
-				margin-bottom: 8rpx;
-				transition: all 0.2s ease;
-			}
-
-			.tabbar-text {
-				font-size: 22rpx;
-				color: #666666;
-				font-weight: 500;
-				transition: all 0.2s ease;
-			}
-
-			.tabbar-badge {
-				position: absolute;
-				top: 8rpx;
-				right: 25%;
-				min-width: 36rpx;
-				height: 36rpx;
-				line-height: 36rpx;
-				padding: 0 8rpx;
-				background: #FF6B6B;
-				color: #FFFFFF;
-				font-size: 20rpx;
-				font-weight: bold;
-				border-radius: 18rpx;
-				text-align: center;
-			}
-
-			&.active {
-				background: #FFE5F1;
-				
-				.tabbar-text {
-					color: #E91E63;
-					font-weight: 700;
+				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;
+					display: flex;
+					flex-direction: column;
+					align-items: center;
+					justify-content: center;
+					padding: 15rpx 0;
+					position: relative;
+		
+					.tabbar-icon {
+						font-size: 44rpx;
+						margin-bottom: 5rpx;
+					}
+		
+					.tabbar-text {
+						font-size: 22rpx;
+						color: #666666;
+					}
+		
+					&.active {
+						.tabbar-text {
+							color: #E91E63;
+							font-weight: bold;
+						}
+					}
 				}
 			}
-		}
-	}
+
 
 	/* 成功案例 */
 	.success-case-section {
@@ -1665,5 +1674,22 @@
 			transform: translateX(-50%);
 		}
 	}
+	
+	.tabbar-badge {
+	      position: absolute;
+	      top: 8rpx;
+	      right: 50%;
+	      margin-right: -40rpx;
+	      min-width: 32rpx;
+	      height: 32rpx;
+	      line-height: 32rpx;
+	      padding: 0 6rpx;
+	      background-color: #FA5151;
+	      border-radius: 16rpx;
+	      font-size: 20rpx;
+	      color: #FFFFFF;
+	      text-align: center;
+	    }
+
 </style>
 

+ 16 - 5
LiangZhiYUMao/pages/message/chat.vue

@@ -1163,9 +1163,8 @@ export default {
 		          data: {
 		            userId: this.userId,
 		            targetUserId: this.targetUserId,
-					targetUserName: this.targetUserName,
-					targetUserAvatar: this.targetUserAvatar
-					
+		            targetUserName: this.targetUserName,
+		            targetUserAvatar: this.targetUserAvatar
 		          },
 		          header: {
 		            'Content-Type': 'application/json',
@@ -1180,8 +1179,20 @@ export default {
 		        if (res.data && res.data.code === 200) {
 		          uni.showToast({ title: '拉黑成功', icon: 'success' });
 		          this.closeBlockConfirm();
-		          // 拉黑成功后返回上一页
-		          setTimeout(() => this.goBack(), 1500);
+		          
+		          // 触发拉黑列表更新事件,通知消息页面实时刷新
+		          uni.$emit('blacklistUpdated');
+		          
+		          // 拉黑成功后立即返回消息列表页面(优化体验)
+		          setTimeout(() => {
+		            uni.navigateBack({
+		              delta: 1,
+		              success: () => {
+		                // 返回成功后再次触发一次刷新,确保万无一失
+		                uni.$emit('blacklistUpdated');
+		              }
+		            });
+		          }, 1500);
 		        } else {
 		          throw new Error(res.data?.message || '拉黑失败');
 		        }

+ 133 - 15
LiangZhiYUMao/pages/message/index.vue

@@ -199,6 +199,8 @@ import TIM from 'tim-wx-sdk';
 export default {
   data() {
     return {
+	  blacklistUsers: [], // 拉黑用户ID列表
+	  blacklistLoaded: false, // 拉黑列表是否加载完成
       userId: null,
       conversations: [],
       loading: false,
@@ -328,6 +330,7 @@ export default {
     
     // 监听刷新事件(从聊天页面返回时触发)
     uni.$on('refreshConversations', this.refreshConversations);
+	uni.$on('blacklistUpdated', this.handleBlacklistUpdate);
   },
   
   onShow() {
@@ -385,9 +388,37 @@ export default {
     
     // 移除事件监听
     uni.$off('refreshConversations', this.refreshConversations);
+	// 移除拉黑状态变更监听
+	  uni.$off('blacklistUpdated', this.handleBlacklistUpdate);
   },
   
   methods: {
+	  
+	  /**
+	     * 处理拉黑列表更新事件
+	     */
+	    async handleBlacklistUpdate() {
+	      console.log('🔄 检测到拉黑列表更新,开始刷新会话列表');
+	      
+	      // 重置拉黑列表加载状态
+	      this.blacklistLoaded = false;
+	      
+	      // 重新加载拉黑列表
+	      await this.loadBlacklist();
+	      
+	      // 重新加载系统消息未读
+	      await this.loadSystemUnread();
+	      
+	      // 重新加载会话列表(会自动过滤拉黑用户)
+	      this.loadConversations();
+	      
+	      // 显示提示
+	      uni.showToast({
+	        title: '已更新会话列表',
+	        icon: 'success',
+	        duration: 1500
+	      });
+	    },
     /**
      * 初始化腾讯云IM
      */
@@ -495,7 +526,6 @@ export default {
         throw error;
       }
     },
-    
     /**
      * 加载会话列表(从腾讯云IM)
      */
@@ -505,6 +535,11 @@ export default {
         return;
       }
       
+      // 确保先加载拉黑列表
+      if (!this.blacklistLoaded) {
+        await this.loadBlacklist();
+      }
+      
       // 验证登录状态
       if (!timManager.isLogin || !timManager.userId) {
         console.error('❌ TIM 未登录或用户ID为空');
@@ -546,7 +581,7 @@ export default {
         console.log('📋 从TIM获取到会话数:', conversationList.length);
         
         // 转换为UI需要的格式
-        this.conversations = conversationList.map(conv => {
+        let formattedConversations = conversationList.map(conv => {
           const formatted = this.formatConversation(conv);
           // 验证会话是否属于当前用户
           if (formatted.userId !== this.userId) {
@@ -556,27 +591,47 @@ export default {
         });
         
         // 过滤掉不属于当前用户的会话(双重保险)
-        this.conversations = this.conversations.filter(conv => conv.userId === this.userId);
+        formattedConversations = formattedConversations.filter(conv => conv.userId === this.userId);
+        
+        // 先计算所有会话的未读数(包括拉黑用户)
+        const allUnreadCount = formattedConversations.reduce((total, conv) => total + conv.unreadCount, 0);
+        
+        // 过滤掉拉黑用户的会话 🌟
+        formattedConversations = formattedConversations.filter(conv => {
+          return !this.blacklistUsers.includes(conv.targetUserId.toString());
+        });
+        
+        this.conversations = formattedConversations;
         
         // 批量获取用户头像信息
         await this.loadUserAvatars();
         
-        // 计算总未读数
-        this.totalUnread = this.conversations.reduce((total, conv) => total + conv.unreadCount, 0);
+        // 计算总未读数(包括所有会话的未读 + 系统消息未读)
+        this.totalUnread = allUnreadCount + this.systemUnread;
+        
+        // 同步到Vuex
+        this.$store.dispatch('updateTotalUnread', this.totalUnread);
         
         console.log('✅ 会话列表加载成功');
         console.log('   - 当前用户ID:', this.userId);
         console.log('   - 会话数量:', this.conversations.length);
+        console.log('   - 拉黑用户数:', this.blacklistUsers.length);
+        console.log('   - 会话未读数:', allUnreadCount);
+        console.log('   - 系统消息未读数:', this.systemUnread);
         console.log('   - 总未读数:', this.totalUnread);
         
         // 输出前3个会话的详细信息(调试用)
         if (this.conversations.length > 0) {
           console.log('📋 会话预览(前3个):');
           this.conversations.slice(0, 3).forEach((conv, idx) => {
-            console.log(`   ${idx + 1}. ${conv.targetUserName} (ID:${conv.targetUserId}) 头像: ${conv.targetUserAvatar}`);
+            console.log(`   ${idx + 1}. ${conv.targetUserName} (ID:${conv.targetUserId}) 头像: ${conv.targetUserAvatar}, 未读: ${conv.unreadCount}`);
           });
         }
         
+        if (this.blacklistUsers.length > 0) {
+          console.log('🚫 已过滤拉黑用户:', this.blacklistUsers);
+        }
+        
       } catch (e) {
         console.error('❌ 加载会话列表失败:', e);
         console.error('❌ 错误详情:', e.message || e);
@@ -591,6 +646,41 @@ export default {
       }
     },
     
+    /**
+     * 获取拉黑用户列表
+     */
+    async loadBlacklist() {
+      try {
+        console.log('🔄 加载拉黑用户列表,用户ID:', this.userId);
+        
+        const res = await uni.request({
+          url: 'http://localhost:8083/api/chatfriend/getBlacklist', // 替换为你的后端接口
+          method: 'GET',
+          data: {
+            userId: this.userId
+          }
+        });
+        
+        if (res[1].statusCode === 200 && res[1].data.code === 200) {
+          // 假设返回格式: {data: [{targetUserId: 'xxx'}, ...]}
+          const blacklistData = res[1].data.data || [];
+          this.blacklistUsers = blacklistData.map(item => {
+            // 确保用户ID为字符串格式,避免类型不一致问题
+            return String(item.targetUserId || item.userId || item.id);
+          });
+          
+          console.log('✅ 拉黑列表加载成功,共', this.blacklistUsers.length, '个拉黑用户:', this.blacklistUsers);
+        } else {
+          console.warn('⚠️ 拉黑列表接口返回异常:', res[1].data);
+        }
+      } catch (error) {
+        console.error('❌ 加载拉黑列表失败:', error);
+        // 加载失败不影响会话列表显示,只记录错误
+      } finally {
+        this.blacklistLoaded = true; // 标记为已加载,避免重复请求
+      }
+    },
+    
     /**
      * 批量获取用户头像信息
      */
@@ -741,6 +831,9 @@ export default {
       // TIM SDK 一次性返回所有会话,不需要分页加载
     },
 
+    /**
+     * 加载系统消息未读
+     */
     async loadSystemUnread() {
       try {
         const userId = this.userId;
@@ -753,6 +846,7 @@ export default {
           const r = await api.message.getSystemUnreadCount(userId);
           this.systemUnread = r.data || 0;
         }
+        
         // 预览最新一条
         const api2 = require('@/utils/api.js').default;
         const listRes = await api2.message.getSystemList(userId, 1, 1);
@@ -761,7 +855,17 @@ export default {
           this.systemLatestTime = list[0].createdAt || list[0].created_at;
           this.systemLatestPreview = list[0].title || '系统通知';
         }
-      } catch (e) { console.log('加载系统未读失败', e); }
+        
+        // 更新总未读数
+        const allUnreadCount = this.conversations.reduce((total, conv) => total + conv.unreadCount, 0);
+        this.totalUnread = allUnreadCount + this.systemUnread;
+        
+        // 同步到Vuex
+        this.$store.dispatch('updateTotalUnread', this.totalUnread);
+        
+      } catch (e) { 
+        console.log('加载系统未读失败', e); 
+      }
     },
 
     openSystemMessages() {
@@ -773,6 +877,9 @@ export default {
      */
     refreshConversations() {
       if (this.timInitialized) {
+        // 先刷新系统消息未读
+        this.loadSystemUnread();
+        // 再刷新会话列表
         this.loadConversations();
       }
     },
@@ -817,8 +924,12 @@ export default {
         // 更新本地显示
         this.selectedConversation.unreadCount = 0;
         
-        // 重新计算总未读数
-        this.totalUnread = this.conversations.reduce((total, conv) => total + conv.unreadCount, 0);
+        // 重新计算总未读数(包含所有会话+系统消息)
+        const allUnreadCount = this.conversations.reduce((total, conv) => total + conv.unreadCount, 0);
+        this.totalUnread = allUnreadCount + this.systemUnread;
+        
+        // 同步到Vuex
+        this.$store.dispatch('updateTotalUnread', this.totalUnread);
         
         uni.showToast({
           title: '已标记为已读',
@@ -854,8 +965,12 @@ export default {
                 this.conversations.splice(index, 1);
               }
               
-              // 重新计算总未读数
-              this.totalUnread = this.conversations.reduce((total, conv) => total + conv.unreadCount, 0);
+              // 重新计算总未读数(包含所有会话+系统消息)
+              const allUnreadCount = this.conversations.reduce((total, conv) => total + conv.unreadCount, 0);
+              this.totalUnread = allUnreadCount + this.systemUnread;
+              
+              // 同步到Vuex
+              this.$store.dispatch('updateTotalUnread', this.totalUnread);
               
               uni.showToast({
                 title: '删除成功',
@@ -992,8 +1107,12 @@ export default {
           this.conversations.splice(index, 1);
         }
         
-        // 重新计算总未读数
-        this.totalUnread = this.conversations.reduce((total, c) => total + c.unreadCount, 0);
+        // 重新计算总未读数(包含所有会话+系统消息)
+        const allUnreadCount = this.conversations.reduce((total, c) => total + c.unreadCount, 0);
+        this.totalUnread = allUnreadCount + this.systemUnread;
+        
+        // 同步到Vuex
+        this.$store.dispatch('updateTotalUnread', this.totalUnread);
         
         uni.showToast({
           title: '删除成功',
@@ -1008,7 +1127,6 @@ export default {
       }
     },
     
-    
     /**
      * 格式化最后一条消息
      */
@@ -1526,4 +1644,4 @@ export default {
     }
   }
 }
-</style>
+</style>

+ 57 - 40
LiangZhiYUMao/pages/mine/index.vue

@@ -219,6 +219,7 @@
 			<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>
@@ -277,6 +278,11 @@
 				checkinRewards: []
 			}
 		},
+		computed: {
+		  unreadCount() {
+		    return this.$store.getters.getTotalUnread || 0;
+		  }
+		},
 	onLoad() {
 		console.log('=== 我的页面加载开始 ===')
 		
@@ -1797,50 +1803,61 @@
 	
 	/* 底部导航栏 - 扁平化 */
 	.tabbar {
-		position: fixed;
-		bottom: 0;
-		left: 0;
-		right: 0;
-		display: flex;
-		background-color: #FFFFFF;
-		border-top: 2rpx solid #E0E0E0;
-		padding-bottom: constant(safe-area-inset-bottom);
-		padding-bottom: env(safe-area-inset-bottom);
-		z-index: 999;
-
-		.tabbar-item {
-			flex: 1;
+			position: fixed;
+			bottom: 0;
+			left: 0;
+			right: 0;
 			display: flex;
-			flex-direction: column;
-			align-items: center;
-			justify-content: center;
-			padding: 18rpx 0;
-			transition: all 0.2s ease;
-
-			&:active {
-				background: #F5F5F5;
-			}
-
-			.tabbar-icon {
-				font-size: 46rpx;
-				margin-bottom: 8rpx;
-			}
-
-			.tabbar-text {
-				font-size: 22rpx;
-				color: #666666;
-				font-weight: 500;
-			}
-
-			&.active {
-				background: #FFE5F1;
-				
+			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;
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				justify-content: center;
+				padding: 15rpx 0;
+				position: relative;
+	
+				.tabbar-icon {
+					font-size: 44rpx;
+					margin-bottom: 5rpx;
+				}
+	
 				.tabbar-text {
-					color: #E91E63;
-					font-weight: 700;
+					font-size: 22rpx;
+					color: #666666;
+				}
+	
+				&.active {
+					.tabbar-text {
+						color: #E91E63;
+						font-weight: bold;
+					}
 				}
 			}
 		}
-	}
+	
+	
+	.tabbar-badge {
+	      position: absolute;
+	      top: 8rpx;
+	      right: 50%;
+	      margin-right: -40rpx;
+	      min-width: 32rpx;
+	      height: 32rpx;
+	      line-height: 32rpx;
+	      padding: 0 6rpx;
+	      background-color: #FA5151;
+	      border-radius: 16rpx;
+	      font-size: 20rpx;
+	      color: #FFFFFF;
+	      text-align: center;
+	    }
 </style>
 

+ 184 - 443
LiangZhiYUMao/pages/page3/page3.vue

@@ -1,91 +1,25 @@
 <template>
 	<view class="content">
+		<!-- 背景图片 -->
+		<image class="bg-image" src="/static/login-bg.png" mode="aspectFill"></image>
+		
+		
 		<!-- 顶部标题区域 -->
 		<view class="header-section">
-			<view class="app-logo">🪶🪶</view>
-			<view class="app-name">两只羽毛</view>
+			<!-- <view class="app-logo">🪶🪶</view> -->
+			<image class="app-logo" src="/static/logo.png" mode="widthFix"></image>
+			<!-- <view class="app-name">青鸾之恋</view> -->
 			<view class="app-slogan">真诚相遇 · 携手一生</view>
 		</view>
 		
 		<!-- 登录框 -->
-		<!-- <view class="login-box"> -->
-			<!-- 切换标签 -->
-			<!-- <view class="login-tabs"> -->
-				<!-- <view class="tab-item" :class="{active: loginMode === 'phone'}" @click="loginMode = 'phone'">
-					<text class="tab-icon">📱</text>
-					<text>验证码登录</text>
-				</view>
-				<view class="tab-item" :class="{active: loginMode === 'password'}" @click="loginMode = 'password'">
-					<text class="tab-icon">🔐</text>
-					<text>密码登录</text>
-				</view> -->
-			<!-- </view> -->
-			
-			<!-- 手机号验证码登录 -->
-			<view v-if="loginMode === 'phone'">
-				<!-- <view class="login-title">
-					<text class="title-icon">💌</text>
-					<text>快速登录</text>
-				</view>
-				<view class="form-group">
-					<input class="input" type="number" maxlength="11" v-model="phoneForm.phone" placeholder="请输入手机号" />
-				</view>
-				<view class="form-group code-group">
-					<input class="input code-input" type="number" maxlength="6" v-model="phoneForm.code" placeholder="请输入验证码" />
-					<view class="code-btn" :class="{disabled: countdown > 0}" @click="sendCode">
-						{{ countdown > 0 ? `${countdown}秒` : '发送验证码' }}
-					</view>
-				</view>
-				<button class="login-btn" @click="handlePhoneLogin">登录</button> -->
-				
-				<!-- 微信一键登录 -->
-				<view class="divider">
-					<view class="line"></view>
-					<text class="text">其他方式登录</text>
-					<view class="line"></view>
-				</view>
-				
-			<button class="wechat-login-btn" @click="login_zheshow">
-				<text class="wechat-icon">💬</text>
-				<text>微信一键登录</text>
-			</button>
-			</view>
-			
-			<!-- 账号密码登录 -->
-			<view v-if="loginMode === 'password'">
-				<!-- <view class="login-title">
-					<text class="title-icon">💝</text>
-					<text>账号登录</text>
-				</view>
-				<view class="form-group">
-					<input class="input" v-model="passwordForm.username" placeholder="请输入账号或手机号" />
-				</view>
-				<view class="form-group password-group">
-					<input class="input" :type="showPassword ? 'text' : 'password'" v-model="passwordForm.password" placeholder="请输入密码" />
-					<view class="eye-icon" @click="showPassword = !showPassword">
-						{{ showPassword ? '👁️' : '👁️‍🗨️' }}
-					</view>
-				</view>
-				<view class="remember-row">
-					<view class="remember-check" @click="rememberMe = !rememberMe">
-						<image :src="rememberMe ? '/static/4checked.png' : '/static/4unchecked.png'" class="checkbox-icon"></image>
-						<text>记住密码</text>
-					</view>
-				</view>
-				<button class="login-btn" @click="handlePasswordLogin">登录</button> -->
-				
-				<!-- 微信一键登录 -->
-				<!-- <view class="divider">
-					<view class="line"></view>
-					<text class="text">其他方式登录</text>
-					<view class="line"></view>
-				</view> -->
-				
+		<view class="login-box">
+			<!-- 微信一键登录 -->
 			<button class="wechat-login-btn" @click="login_zheshow">
-				<text class="wechat-icon">💬</text>
+				<image class="wechat-icon" src="/static/wechat-icon.png" mode="aspectFit"></image>
 				<text>微信一键登录</text>
 			</button>
-			<!-- </view> -->
+		
 		</view>
 		
 		<!-- 底部提示 -->
@@ -109,45 +43,20 @@
         data() {
             return {
                 zheshow: false, // 控制弹窗显示隐藏
-                loginMode: 'phone', // 登录模式:phone-验证码登录,password-密码登录
-                redirectUrl: '', // 登录成功后跳转地址(可选)
-                
-                // 手机号验证码登录
-                phoneForm: {
-                    phone: '',
-                    code: ''
-                },
-                countdown: 0, // 倒计时
-                countdownTimer: null,
-                
-                // 账号密码登录
-                passwordForm: {
-                    username: '',
-                    password: ''
-                },
-                showPassword: false, // 是否显示密码
-                rememberMe: false // 记住密码
+                redirectUrl: '' // 登录成功后跳转地址(可选)
             }
         },
         
         onLoad(options) {
-            // 加载记住的密码
-            this.loadRememberedPassword();
             // 处理跳转参数
             this.redirectUrl = options && options.redirect ? decodeURIComponent(options.redirect) : ''
         },
 		
-		onUnload() {
-			// 清除倒计时
-			if (this.countdownTimer) {
-				clearInterval(this.countdownTimer);
-			}
-		},
-		
 		methods: {
 			login_zheshow(){
 				this.zheshow = !this.zheshow
 			},
+<<<<<<< HEAD
 		loset(Logon_Credentials){
 			console.log('=== 开始微信登录流程 ===')
 			console.log('用户输入信息:', Logon_Credentials)
@@ -222,155 +131,97 @@
 							icon: 'none',
 							duration: 2000
 						})
-					}
-				},
-				fail: (err) => {
-					console.error('获取微信code失败:', err)
-					uni.showToast({ title: '微信登录失败', icon: 'none' })
-				}
-			})
-		},
-			
-			// 发送验证码
-			async sendCode() {
-				if (this.countdown > 0) return;
-				
-				// 验证手机号
-				if (!this.phoneForm.phone) {
-					uni.showToast({ title: '请输入手机号', icon: 'none' });
-					return;
-				}
-				
-				if (!/^1[3-9]\d{9}$/.test(this.phoneForm.phone)) {
-					uni.showToast({ title: '请输入正确的手机号', icon: 'none' });
-					return;
-				}
-				
-				try {
-					uni.showLoading({ title: '发送中...' })
-					await api.auth.sendCode(this.phoneForm.phone)
-					uni.showToast({ title: '验证码已发送', icon: 'success' });
-					// 开始倒计时
-					this.countdown = 60;
-					this.countdownTimer = setInterval(() => {
-						this.countdown--;
-						if (this.countdown <= 0) {
-							clearInterval(this.countdownTimer);
+=======
+			loset(Logon_Credentials){
+				console.log(Logon_Credentials,'登录信息')
+				// 获取微信登录code
+				uni.login({
+					provider: 'weixin',
+					success: async (loginRes) => {
+						uni.showLoading({ title: '登录中...' })
+						try {
+							// 1. 微信授权登录
+							const wechatLoginResult = await api.auth.wechatLogin(loginRes.code)
+							const token = wechatLoginResult?.token || wechatLoginResult?.data?.token
+							const user = wechatLoginResult?.user || wechatLoginResult?.data?.user
+							
+							if (!token || !user) {
+								throw new Error('微信登录返回数据异常')
+							}
+							
+							console.log('✅ 微信登录成功,用户ID:', user.userId)
+							
+							// 2. 获取手机号
+							const phoneCode = (Logon_Credentials && Logon_Credentials.code && String(Logon_Credentials.code).trim()) ? String(Logon_Credentials.code).trim() : 'mock_dev_code'
+							console.log('📱 使用phoneCode:', phoneCode)
+							
+							try {
+								const phoneResult = await api.auth.wechatPhone(phoneCode)
+								const phone = phoneResult?.phone || phoneResult?.data?.phone
+								
+								if (phone) {
+									console.log('✅ 成功获取手机号')
+									user.phone = phone
+								} else {
+									console.warn('⚠️ 手机号为空,使用默认值(开发环境)')
+									// 开发环境:如果后端返回了mock手机号,也可能在user对象中
+									user.phone = user.phone || '13800138000'
+								}
+							} catch (phoneError) {
+								console.error('❌ 获取手机号接口失败:', phoneError)
+								// 开发环境容错:继续登录,使用默认手机号
+								user.phone = user.phone || '13800138000'
+								console.warn('⚠️ 使用默认手机号继续登录(开发环境)')
+							}
+							
+							// 3. 更新用户信息(昵称和头像)
+							user.nickname = Logon_Credentials.nickname || user.nickname
+							user.avatar = Logon_Credentials.active || user.avatar
+							
+							console.log('💾 保存用户信息:', { userId: user.userId, phone: user.phone, nickname: user.nickname })
+							
+							// 4. 如果用户输入了昵称或头像,更新到后端
+							if (Logon_Credentials.nickname || Logon_Credentials.active) {
+								try {
+									await api.user.updateInfo({
+										userId: user.userId,
+										nickname: user.nickname,
+										avatarUrl: user.avatar
+									})
+									console.log('✅ 用户信息已更新到后端')
+								} catch (updateError) {
+									console.warn('⚠️ 更新用户信息失败:', updateError)
+									// 不影响登录流程,继续
+								}
+							}
+							
+							// 5. 保存登录信息
+							userAuth.saveLoginInfo(token, user)
+							
+							uni.hideLoading()
+							uni.showToast({ title: '登录成功', icon: 'success' })
+							
+							// 5. 跳转首页
+							setTimeout(() => {
+								const target = this.redirectUrl || '/pages/index/index'
+								uni.reLaunch({ url: target })
+							}, 500)
+						} catch (error) {
+							console.error('❌ 微信登录失败:', error)
+							uni.hideLoading()
+							uni.showToast({ 
+								title: error?.message || '登录失败,请重试', 
+								icon: 'none',
+								duration: 2000
+							})
 						}
-					}, 1000);
-				} catch (err) {
-					console.error('发送验证码失败:', err)
-					uni.showToast({ title: err?.message || '发送失败', icon: 'none' })
-				} finally {
-					uni.hideLoading()
-				}
-			},
-			
-			// 手机号验证码登录
-			async handlePhoneLogin() {
-				if (!this.phoneForm.phone) {
-					uni.showToast({ title: '请输入手机号', icon: 'none' });
-					return;
-				}
-				
-				if (!this.phoneForm.code) {
-					uni.showToast({ title: '请输入验证码', icon: 'none' });
-					return;
-				}
-				
-				uni.showLoading({ title: '登录中...' });
-				try {
-					const result = await api.auth.smsLogin(this.phoneForm.phone, this.phoneForm.code)
-					const token = result?.token || result?.data?.token
-					const user = result?.user || result?.data?.user
-					if (!token || !user) {
-						throw new Error('登录返回数据异常')
+					},
+					fail: (err) => {
+						console.error('获取微信code失败:', err)
+						uni.showToast({ title: '微信登录失败', icon: 'none' })
+>>>>>>> yh
 					}
-					// 保存登录信息
-					userAuth.saveLoginInfo(token, user)
-					uni.showToast({ title: '登录成功', icon: 'success' })
-					// 跳转首页
-					const target = this.redirectUrl || '/pages/index/index'
-					uni.reLaunch({ url: target })
-					// 清空表单
-					this.phoneForm = { phone: '', code: '' }
-				} catch (error) {
-					console.error('验证码登录失败:', error)
-					uni.showToast({ title: error?.message || '登录失败', icon: 'none' })
-				} finally {
-					uni.hideLoading()
-				}
-			},
-			
-            // 账号密码登录
-            async handlePasswordLogin() {
-                if (!this.passwordForm.username) {
-                    uni.showToast({ title: '请输入账号', icon: 'none' });
-                    return;
-                }
-                
-                if (!this.passwordForm.password) {
-                    uni.showToast({ title: '请输入密码', icon: 'none' });
-                    return;
-                }
-                
-                // 调用后端API进行登录
-                uni.showLoading({ title: '登录中...' });
-                try {
-                    const result = await api.auth.loginByPassword(this.passwordForm.username, this.passwordForm.password)
-                    // 兼容不同返回结构
-                    const token = result?.token || result?.data?.token
-                    const user = result?.user || result?.data?.user
-                    if (!token || !user) {
-                        throw new Error('登录返回数据异常')
-                    }
-
-                    // 使用工具类保存登录信息 - 确保userId正确处理
-                    console.log('登录成功,用户信息:', user)
-                    console.log('用户ID类型和值:', typeof user.userId, user.userId)
-                    
-                    // 确保userId是数字类型
-                    if (user.userId) {
-                        user.userId = parseInt(user.userId)
-                        console.log('处理后的用户ID:', user.userId)
-                    }
-                    
-                    userAuth.saveLoginInfo(token, user)
-
-                    // 是否记住密码
-                    if (this.rememberMe) {
-                        uni.setStorageSync('rememberedAccount', {
-                            username: this.passwordForm.username,
-                            password: this.passwordForm.password
-                        })
-                    } else {
-                        uni.removeStorageSync('rememberedAccount')
-                    }
-
-                    uni.showToast({ title: '登录成功', icon: 'success' })
-
-                    // 跳转页面(统一使用 reLaunch 进入首页或指定页面)
-                    const target = this.redirectUrl || '/pages/index/index'
-                    uni.reLaunch({ url: target })
-
-                    // 清空表单
-                    this.passwordForm = { username: '', password: '' }
-                } catch (error) {
-                    console.error('登录失败:', error)
-                    uni.showToast({ title: error?.message || '登录失败', icon: 'none' })
-                } finally {
-                    uni.hideLoading()
-                }
-            },
-			
-			// 加载记住的密码
-			loadRememberedPassword() {
-				const remembered = uni.getStorageSync('rememberedAccount');
-				if (remembered) {
-					this.passwordForm.username = remembered.username;
-					this.passwordForm.password = remembered.password;
-					this.rememberMe = true;
-				}
+				})
 			}
 		}
 	}
@@ -379,9 +230,19 @@
 <style lang="scss" scoped>
 .content {
 	padding: 40rpx;
-	background: linear-gradient(135deg, #ffeef8 0%, #fff5f7 50%, #ffe9f0 100%);
 	min-height: 100vh;
 	position: relative;
+	overflow: hidden;
+	
+	.bg-image {
+		position: fixed;
+		top: 0;
+		left: 0;
+		width: 100%;
+		height: 100%;
+		z-index: 0;
+		pointer-events: none;
+	}
 	
 	// 添加装饰性元素
 	&::before {
@@ -417,11 +278,13 @@
 .header-section {
 	text-align: center;
 	margin-bottom: 60rpx;
+	position: relative;
+	z-index: 1;
 	
 	.app-logo {
 		font-size: 120rpx;
 		margin-bottom: 20rpx;
-		animation: heartbeat 2s infinite;
+		// animation: heartbeat 1s infinite;
 	}
 	
 	.app-name {
@@ -435,211 +298,87 @@
 	
 	.app-slogan {
 		font-size: 26rpx;
-		color: #ff6b9d;
+		color: #333333;
 		opacity: 0.8;
 	}
 }
 
 .login-box {
-	background: #FFFFFF;
+	background: rgba(211, 127, 140, 0);
 	border-radius: 30rpx;
-	padding: 50rpx 40rpx;
+	padding: 80rpx 40rpx;
 	margin-bottom: 30rpx;
 	box-shadow: 0 8rpx 24rpx rgba(255, 107, 157, 0.15);
 	border: 2rpx solid rgba(255, 107, 157, 0.1);
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	position: relative;
+	z-index: 1;
 	
-	.login-tabs {
-		display: flex;
-		background: linear-gradient(135deg, #fff0f6 0%, #ffe9f0 100%);
-		border-radius: 50rpx;
-		padding: 6rpx;
-		margin-bottom: 40rpx;
-		
-		.tab-item {
-			flex: 1;
-			height: 70rpx;
-			display: flex;
-			align-items: center;
-			justify-content: center;
-			font-size: 26rpx;
-			color: #ff6b9d;
-			border-radius: 50rpx;
-			transition: all 0.3s;
-			
-			.tab-icon {
-				font-size: 32rpx;
-				margin-right: 8rpx;
-			}
-			
-			&.active {
-				background: linear-gradient(135deg, #ff6b9d 0%, #ff8fab 100%);
-				color: #FFFFFF;
-				font-weight: bold;
-				box-shadow: 0 4rpx 12rpx rgba(255, 107, 157, 0.3);
-			}
-		}
-	}
-	
-	.login-title {
-		font-size: 34rpx;
-		font-weight: bold;
-		color: #ff6b9d;
-		margin-bottom: 40rpx;
-		text-align: center;
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		
-		.title-icon {
-			font-size: 40rpx;
-			margin-right: 10rpx;
-		}
-	}
-	
-	.form-group {
-		margin-bottom: 25rpx;
-		position: relative;
-		
-		.input {
-			width: 100%;
-			height: 90rpx;
-			background: #fff5f9;
-			border-radius: 50rpx;
-			padding: 0 30rpx;
-			font-size: 28rpx;
-			border: 2rpx solid #ffe0ed;
-			box-sizing: border-box;
-			transition: all 0.3s;
-			
-			&:focus {
-				background: #FFFFFF;
-				border-color: #ff8fab;
-			}
-		}
-		
-		&.code-group {
-			display: flex;
-			align-items: center;
-			
-			.code-input {
-				flex: 1;
-				margin-right: 20rpx;
-			}
-			
-			.code-btn {
-				width: 180rpx;
-				height: 80rpx;
-				background: linear-gradient(135deg, #ff6b9d 0%, #ff8fab 100%);
-				color: #FFFFFF;
-				border-radius: 50rpx;
-				display: flex;
-				align-items: center;
-				justify-content: center;
-				font-size: 24rpx;
-				box-shadow: 0 4rpx 12rpx rgba(255, 107, 157, 0.3);
-				
-				&.disabled {
-					background: #e8e8e8;
-					color: #999999;
-					box-shadow: none;
-				}
-			}
-		}
-		
-		&.password-group {
-			display: flex;
-			align-items: center;
-			
-			.input {
-				flex: 1;
-				padding-right: 80rpx;
-			}
-			
-			.eye-icon {
-				position: absolute;
-				right: 20rpx;
-				font-size: 40rpx;
-				padding: 10rpx;
-			}
-		}
-	}
-	
-	.remember-row {
-		margin-bottom: 25rpx;
+	// 	.wechat-login-btn {
+	// 	width: 100%;
+	// 	height: 90rpx;
+	// 		background: #FFFFFF;
+	// 		color: #ff6b9d;
+	// 	border-radius: 50rpx;
+	// 	font-size: 30rpx;
+	// 	font-weight: bold;
+	// 	display: flex;
+	// 	align-items: center;
+	// 	justify-content: center;
+	// 		border: 2rpx solid #ff6b9d;
+	// 		box-shadow: 0 8rpx 20rpx rgba(255, 107, 157, 0.2);
 		
-		.remember-check {
-			display: flex;
-			align-items: center;
-			font-size: 26rpx;
-			color: #666666;
-			
-			.checkbox-icon {
-				width: 32rpx;
-				height: 32rpx;
-				margin-right: 10rpx;
-			}
-		}
-	}
-	
-	.login-btn {
-		width: 100%;
-		height: 90rpx;
-		background: linear-gradient(135deg, #ff6b9d 0%, #ff8fab 100%);
-		color: #FFFFFF;
-		border-radius: 50rpx;
-		font-size: 32rpx;
-		font-weight: bold;
-		border: none;
-		box-shadow: 0 8rpx 20rpx rgba(255, 107, 157, 0.4);
-		margin-bottom: 30rpx;
+	// 	.wechat-icon {
+	// 		width: 40rpx;
+	// 		height: 40rpx;
+	// 		margin-right: 10rpx;
+	// 	}
 		
-		&:active {
-			opacity: 0.9;
-			transform: translateY(2rpx);
-		}
+	// 	&:active {
+	// 		opacity: 0.9;
+	// 		transform: translateY(2rpx);
+	// 	}
+	// }
+	.wechat-login-btn {
+	  width: 100%;
+	  height: 96rpx;
+	  // 渐变背景+光泽叠加
+	  background: linear-gradient(90deg, #9d40e9 0%, #5a35f7 100%),
+	              radial-gradient(circle at 30% 30%, rgba(255,255,255,0.15), transparent 50%);
+	  background-blend-mode: overlay;
+	  color: #ffffff;
+	  border-radius: 60rpx;
+	  font-size: 32rpx;
+	  font-weight: bold;
+	  display: flex;
+	  align-items: center;
+	  justify-content: center;
+	  border: none;
+	  // 多层光影:外层投影+内高光+内阴影
+	  box-shadow: 
+	    0 6rpx 16rpx rgba(90, 53, 247, 0.4), 
+	    0 3rpx 8rpx rgba(90, 53, 247, 0.2),
+	    inset 0 2rpx 3rpx rgba(255, 255, 255, 0.25),
+	    inset 0 -2rpx 3rpx rgba(0, 0, 0, 0.15);
+	  transition: all 0.2s ease; // 动画过渡,更自然
 	}
 	
-	.divider {
-		display: flex;
-		align-items: center;
-		margin-bottom: 30rpx;
-		
-		.line {
-			flex: 1;
-			height: 1rpx;
-			background: linear-gradient(to right, transparent, #ffcce0, transparent);
-		}
-		
-		.text {
-			margin: 0 20rpx;
-			font-size: 24rpx;
-			color: #ff6b9d;
-		}
+	.wechat-login-btn:active {
+	  transform: translateY(2rpx);
+	  box-shadow: 
+	    0 3rpx 8rpx rgba(90, 53, 247, 0.3), 
+	    0 1rpx 4rpx rgba(90, 53, 247, 0.2),
+	    inset 0 1rpx 2rpx rgba(255, 255, 255, 0.15),
+	    inset 0 -2rpx 3rpx rgba(0, 0, 0, 0.2);
 	}
 	
-	.wechat-login-btn {
-		width: 100%;
-		height: 90rpx;
-		background: linear-gradient(135deg, #ff6b9d 0%, #ff8fab 100%);
-		color: #FFFFFF;
-		border-radius: 50rpx;
-		font-size: 30rpx;
-		font-weight: bold;
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		border: none;
-		box-shadow: 0 8rpx 20rpx rgba(255, 107, 157, 0.4);
-		
-		.wechat-icon {
-			font-size: 40rpx;
-			margin-right: 10rpx;
-		}
-		
-		&:active {
-			opacity: 0.9;
-			transform: translateY(2rpx);
-		}
+	.wechat-icon {
+	  width: 45rpx;
+	  height: 45rpx;
+	  margin-right: 15rpx;
+	  filter: drop-shadow(0 1rpx 2rpx rgba(0,0,0,0.1)); // 给图标加轻微阴影,更立体
 	}
 }
 
@@ -647,6 +386,8 @@
 	text-align: center;
 	margin-top: 60rpx;
 	padding: 30rpx;
+	position: relative;
+	z-index: 1;
 	
 	.tip-icon {
 		font-size: 50rpx;
@@ -657,7 +398,7 @@
 	
 	.tip-text {
 		font-size: 26rpx;
-		color: #ff6b9d;
+		color: #333333;
 		font-weight: 500;
 		letter-spacing: 2rpx;
 	}

+ 56 - 34
LiangZhiYUMao/pages/plaza/index.vue

@@ -153,6 +153,7 @@
 			<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" @click="switchTab('mine')">
 				<text class="tabbar-icon">👤</text>
@@ -182,7 +183,11 @@ export default {
 			favoritingMap: {}  // 记录正在收藏的动态ID,防止重复点击
 		}
 	},
-	
+	computed: {
+    unreadCount() {
+      return this.$store.getters.getTotalUnread || 0;
+    }
+  },
 	onLoad() {
 		this.loadDynamicList()
 		// 监听详情页更新事件,实时同步点赞/收藏状态
@@ -833,44 +838,45 @@ export default {
 	
 	// 底部导航栏
 	.tabbar {
-		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;
+			position: fixed;
+			bottom: 0;
+			left: 0;
+			right: 0;
 			display: flex;
-			flex-direction: column;
-			align-items: center;
-			justify-content: center;
-			padding: 15rpx 0;
-
-			.tabbar-icon {
-				font-size: 44rpx;
-				margin-bottom: 5rpx;
-			}
-
-			.tabbar-text {
-				font-size: 22rpx;
-				color: #666666;
-			}
-
-			&.active {
+			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;
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				justify-content: center;
+				padding: 15rpx 0;
+				position: relative;
+	
+				.tabbar-icon {
+					font-size: 44rpx;
+					margin-bottom: 5rpx;
+				}
+	
 				.tabbar-text {
-					color: #E91E63;
-					font-weight: bold;
+					font-size: 22rpx;
+					color: #666666;
+				}
+	
+				&.active {
+					.tabbar-text {
+						color: #E91E63;
+						font-weight: bold;
+					}
 				}
 			}
 		}
-	}
 }
 
 // 动画
@@ -900,4 +906,20 @@ export default {
 		transform: translateY(0);
 	}
 }
+
+.tabbar-badge {
+      position: absolute;
+      top: 8rpx;
+      right: 50%;
+      margin-right: -40rpx;
+      min-width: 32rpx;
+      height: 32rpx;
+      line-height: 32rpx;
+      padding: 0 6rpx;
+      background-color: #FA5151;
+      border-radius: 16rpx;
+      font-size: 20rpx;
+      color: #FFFFFF;
+      text-align: center;
+    }
 </style>

+ 80 - 13
LiangZhiYUMao/pages/recommend/index.vue

@@ -127,6 +127,7 @@
 			<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" @click="switchTab('mine')">
 				<text class="tabbar-icon">👤</text>
@@ -166,6 +167,12 @@ export default {
       provinceList: [], cityList: [], areaList: [], currentProvinceName: '', currentCityName: '', currentAreaName: ''
     }
 	},
+	computed: {
+	    unreadCount() {
+	      return this.$store.getters.getTotalUnread || 0;
+	    }
+	  },
+	
 	onLoad() { this.refresh() },
 	methods: {
 		// 切换选项卡
@@ -657,7 +664,21 @@ export default {
 			opacity: 0.8;
 		}
 	}
-	
+	.tabbar-badge {
+	  position: absolute;
+	  top: 8rpx;
+	  right: 25%;
+	  min-width: 36rpx;
+	  height: 36rpx;
+	  line-height: 36rpx;
+	  padding: 0 8rpx;
+	  background: #FF6B6B;
+	  color: #FFFFFF;
+	  font-size: 20rpx;
+	  font-weight: bold;
+	  border-radius: 18rpx;
+	  text-align: center;
+	}
 	.act-btn.ghost{ 
 		background: #F5F5F5; 
 		color: #666;
@@ -671,18 +692,46 @@ export default {
 	}
 
 	/* 底部导航栏 - 扁平化 */
-	.tabbar { 
-		position: fixed; 
-		bottom: 0; 
-		left: 0; 
-		right: 0; 
-		display: flex; 
-		background: #FFFFFF; 
-		border-top: 2rpx solid #E0E0E0; 
-		padding-bottom: constant(safe-area-inset-bottom); 
-		padding-bottom: env(safe-area-inset-bottom); 
-		z-index: 999;
-	}
+	.tabbar {
+				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;
+					display: flex;
+					flex-direction: column;
+					align-items: center;
+					justify-content: center;
+					padding: 15rpx 0;
+					position: relative;
+		
+					.tabbar-icon {
+						font-size: 44rpx;
+						margin-bottom: 5rpx;
+					}
+		
+					.tabbar-text {
+						font-size: 22rpx;
+						color: #666666;
+					}
+		
+					&.active {
+						.tabbar-text {
+							color: #E91E63;
+							font-weight: bold;
+						}
+					}
+				}
+			}
 	
 	.tabbar-item { 
 		flex: 1; 
@@ -850,5 +899,23 @@ export default {
 		color: #FFFFFF;
 		border: none;
 	}
+	
+	
+	.tabbar-badge {
+	      position: absolute;
+	      top: 8rpx;
+	      right: 50%;
+	      margin-right: -40rpx;
+	      min-width: 32rpx;
+	      height: 32rpx;
+	      line-height: 32rpx;
+	      padding: 0 6rpx;
+	      background-color: #FA5151;
+	      border-radius: 16rpx;
+	      font-size: 20rpx;
+	      color: #FFFFFF;
+	      text-align: center;
+	    }
+
 </style>
 

+ 43 - 8
LiangZhiYUMao/pages/success-case/detail.vue

@@ -99,7 +99,12 @@
 			if (options.caseNo) {
 				this.caseNo = options.caseNo
 				this.loadCaseDetail()
-				this.loadTimeline()
+			} else {
+				console.warn('未提供案例编号')
+				uni.showToast({
+					title: '参数错误',
+					icon: 'none'
+				})
 			}
 		},
 
@@ -109,11 +114,30 @@
 				try {
 					const data = await api.successCase.getDetail(this.caseNo)
 					if (data) {
-						this.caseDetail = data
-						// 如果有照片数据
-						if (data.images) {
-							this.images = data.images
+						// 处理字段映射,支持驼峰和下划线命名
+						this.caseDetail = {
+							...data,
+							maleUserNickname: data.maleUserNickname || data.male_user_nickname || '先生',
+							femaleUserNickname: data.femaleUserNickname || data.female_user_nickname || '女士',
+							imageUrl: data.imageUrl || data.image_url || DEFAULT_IMAGES.couple,
+							quote: data.quote || '我们的故事',
+							story: data.story || '这是一段美好的爱情故事...',
+							marriageDate: data.marriageDate || data.marriage_date || '',
+							matchmakerName: data.matchmakerName || data.matchmaker_name || ''
+						}
+						console.log('案例详情加载成功:', this.caseDetail)
+						
+						// 如果有照片数据,处理字段映射
+						if (data.images && data.images.length > 0) {
+							this.images = data.images.map(img => ({
+								...img,
+								imageUrl: img.imageUrl || img.image_url || DEFAULT_IMAGES.couple
+							}))
+							console.log('照片加载成功:', this.images)
 						}
+						
+						// 案例详情加载成功后,再加载时间线
+						this.loadTimeline()
 					}
 				} catch (error) {
 					console.error('加载案例详情失败:', error)
@@ -128,11 +152,22 @@
 			async loadTimeline() {
 				try {
 					const data = await api.successCase.getTimeline(this.caseNo)
-					if (data) {
-						this.timeline = data
+					if (data && data.length > 0) {
+						// 处理时间线数据的字段映射
+						this.timeline = data.map(item => ({
+							...item,
+							eventDate: item.eventDate || item.event_date || '',
+							eventDescription: item.eventDescription || item.event_description || ''
+						}))
+						console.log('时间线加载成功:', this.timeline)
+					} else {
+						console.log('该案例暂无时间线数据')
+						this.timeline = []
 					}
 				} catch (error) {
-					console.error('加载时间线失败:', error)
+					console.warn('加载时间线失败:', error)
+					// 时间线加载失败不影响主要内容展示,只记录日志
+					this.timeline = []
 				}
 			},
 

BIN
LiangZhiYUMao/static/login-bg.png


BIN
LiangZhiYUMao/static/logo.png


BIN
LiangZhiYUMao/static/logo111.png


BIN
LiangZhiYUMao/static/romance.zip


BIN
LiangZhiYUMao/static/wechat-icon.png


+ 26 - 0
LiangZhiYUMao/store/index.js

@@ -0,0 +1,26 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+  state: {
+    totalUnread: 0 // 全局未读数(存储所有页面需要共享的值)
+  },
+  mutations: {
+    // 更新未读数
+    SET_TOTAL_UNREAD(state, count) {
+      state.totalUnread = count
+    }
+  },
+  actions: {
+    // 触发更新未读数的动作
+    updateTotalUnread({ commit }, count) {
+      commit('SET_TOTAL_UNREAD', count)
+    }
+  },
+  getters: {
+    // 获取全局未读数
+    getTotalUnread: state => state.totalUnread
+  }
+})

+ 8 - 2
LiangZhiYUMao/utils/api.js

@@ -81,11 +81,17 @@ export default {
     // 获取今日匹配数
     getMatchCount: () => request({ url: '/user/match-count' }),
     
-    // 更新用户信息
+    // 更新用户基本信息(昵称、头像等)
     updateInfo: (data) => request({ 
-      url: '/user/info', 
+      url: '/user/basic', 
       method: 'PUT', 
       data 
+    }),
+    
+    // 更新单个字段
+    updateField: (userId, fieldName, fieldValue) => request({
+      url: `/user/basic/field?userId=${userId}&fieldName=${fieldName}&fieldValue=${encodeURIComponent(fieldValue)}`,
+      method: 'PUT'
     })
   },
 

+ 6 - 0
package-lock.json

@@ -0,0 +1,6 @@
+{
+  "name": "xiangqinxiangmu",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {}
+}

+ 1 - 1
service/homePage/src/main/java/com/zhentao/controller/ActivityController.java

@@ -76,7 +76,7 @@ public class ActivityController {
                         // 忽略无法转换的类型
                     }
                 }
-                activityList = activityService.getActivityList(typeInt, status);
+                activityList = activityService.getActivityList(typeInt, status, limit);
             }
             
             return Result.success(activityList);

+ 2 - 1
service/homePage/src/main/java/com/zhentao/service/ActivityService.java

@@ -13,9 +13,10 @@ public interface ActivityService {
      * 
      * @param type 活动类型(null表示全部)
      * @param status 活动状态(null表示全部)
+     * @param limit 限制数量(可选)
      * @return 活动列表
      */
-    List<Activity> getActivityList(Integer type, Integer status);
+    List<Activity> getActivityList(Integer type, Integer status, Integer limit);
     
     /**
      * 获取活动详情

+ 9 - 1
service/homePage/src/main/java/com/zhentao/service/impl/ActivityServiceImpl.java

@@ -30,9 +30,12 @@ public class ActivityServiceImpl implements ActivityService {
      * 获取活动列表
      */
     @Override
-    public List<Activity> getActivityList(Integer type, Integer status) {
+    public List<Activity> getActivityList(Integer type, Integer status, Integer limit) {
         QueryWrapper<Activity> queryWrapper = new QueryWrapper<>();
         
+        // 只查询未删除的活动
+        queryWrapper.eq("is_deleted", 0);
+        
         // 按类型筛选
         if (type != null) {
             queryWrapper.eq("type", type);
@@ -46,6 +49,11 @@ public class ActivityServiceImpl implements ActivityService {
         // 按开始时间降序排序
         queryWrapper.orderByDesc("start_time");
         
+        // 限制数量
+        if (limit != null && limit > 0) {
+            queryWrapper.last("LIMIT " + limit);
+        }
+        
         return activityMapper.selectList(queryWrapper);
     }
     

BIN
service/homePage/target/classes/com/zhentao/controller/ActivityController.class


BIN
service/homePage/target/classes/com/zhentao/service/ActivityService.class


BIN
service/homePage/target/classes/com/zhentao/service/impl/ActivityServiceImpl.class


+ 7 - 4
service/websocket/src/main/java/com/zhentao/controller/ChatFriendController.java

@@ -4,10 +4,7 @@ import com.zhentao.dto.BlockDto;
 import com.zhentao.service.ChatFriendService;
 import com.zhentao.vo.ResultVo;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import java.util.Map;
 
@@ -21,4 +18,10 @@ public class ChatFriendController {
     public ResultVo block(@RequestBody BlockDto blockDto){
         return chatFriendService.block(blockDto);
     }
+
+    @GetMapping("/getBlacklist")
+    public ResultVo getBlacklist(@RequestParam("userId") Long userId){
+        return chatFriendService.getBlacklist(userId);
+    }
+
 }

+ 2 - 0
service/websocket/src/main/java/com/zhentao/service/ChatFriendService.java

@@ -13,4 +13,6 @@ import com.zhentao.vo.ResultVo;
 public interface ChatFriendService extends IService<ChatFriend> {
 
     ResultVo block(BlockDto blockDto);
+
+    ResultVo getBlacklist(Long userId);
 }

+ 20 - 0
service/websocket/src/main/java/com/zhentao/service/impl/ChatFriendServiceImpl.java

@@ -12,10 +12,12 @@ import com.zhentao.entity.ChatFriend;
 import com.zhentao.service.ChatConversationService;
 import com.zhentao.service.ChatFriendService;
 import com.zhentao.mapper.ChatFriendMapper;
+import com.zhentao.vo.GetblockVo;
 import com.zhentao.vo.ResultVo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
@@ -70,6 +72,24 @@ public class ChatFriendServiceImpl extends ServiceImpl<ChatFriendMapper, ChatFri
         return ResultVo.fail("拉黑失败");
     }
 
+    @Override
+    public ResultVo getBlacklist(Long userId) {
+        QueryWrapper<ChatFriend> chatFriendQueryWrapper = new QueryWrapper<>();
+        chatFriendQueryWrapper.lambda().eq(ChatFriend::getUserId,userId).eq(ChatFriend::getStatus,2);
+        List<ChatFriend> chatFriends = this.baseMapper.selectList(chatFriendQueryWrapper);
+        if (CollUtil.isEmpty(chatFriends)){
+            return ResultVo.success("success");
+        }
+        List<GetblockVo> getblockVoList = new ArrayList<>();
+        for (ChatFriend chatFriend : chatFriends) {
+            GetblockVo getblockVo = new GetblockVo();
+            getblockVo.setUserId(chatFriend.getFriendId());
+            getblockVo.setCreateTime(chatFriend.getCreateTime());
+            getblockVoList.add(getblockVo);
+        }
+        return ResultVo.success("success",getblockVoList);
+    }
+
 
     public void deleteChatConversation(BlockDto dto){
         chatConversationService.deleteChatConversation(dto);

+ 11 - 0
service/websocket/src/main/java/com/zhentao/vo/GetblockVo.java

@@ -0,0 +1,11 @@
+package com.zhentao.vo;
+
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class GetblockVo {
+    private Long userId;
+    private Date createTime;
+}