diff --git a/pages/chat-detail/chat-detail.js b/pages/chat-detail/chat-detail.js
index 1488798..6f8cb25 100644
--- a/pages/chat-detail/chat-detail.js
+++ b/pages/chat-detail/chat-detail.js
@@ -127,7 +127,10 @@ Page({
// 免费畅聊相关
freeTime: null,
- countdownText: ''
+ countdownText: '',
+
+ // 聊天模式 (ai | human)
+ chatMode: 'ai'
},
onLoad(options) {
@@ -140,6 +143,10 @@ Page({
this.messageTimer = null
this.isProcessing = false
+ // 获取当前用户ID
+ const userId = app.globalData.userId || wx.getStorageSync(config.STORAGE_KEYS.USER_ID)
+ this.setData({ userId })
+
// 获取参数
const characterId = options.id || ''
const conversationId = options.conversationId || ''
@@ -177,10 +184,20 @@ Page({
if (!this.data.loading) {
this.loadQuotaStatus()
}
+
+ // 开启消息轮询
+ this.startPolling()
+ },
+
+ onHide() {
+ // 页面隐藏时停止轮询
+ this.stopPolling()
},
onUnload() {
// 页面卸载时清理
+ this.stopPolling()
+
// 清除消息处理定时器
if (this.messageTimer) {
clearTimeout(this.messageTimer)
@@ -201,6 +218,156 @@ Page({
}
},
+ /**
+ * 开启消息轮询 (智能自适应模式)
+ * 策略:
+ * 1. 默认状态:低频轮询 (每4秒),降低性能损耗
+ * 2. 交互状态:当用户发送消息后,进入"高速模式" (每1.5秒),持续60秒,确保及时收到回复
+ */
+ startPolling() {
+ this.stopPolling()
+
+ // 如果处于高速模式,使用短间隔
+ const interval = this.isFastPolling ? 1500 : 4000
+
+ console.log(`[chat-detail] 开启消息轮询 (模式: ${this.isFastPolling ? '高速' : '省流'}, 间隔: ${interval}ms)`)
+
+ this.pollingTimer = setTimeout(() => {
+ this.checkNewMessages().finally(() => {
+ // 递归调用,确保上一次请求结束后才开始下一次计时
+ // 页面未卸载且未停止轮询时继续
+ if (this.data.conversationId) {
+ this.startPolling()
+ }
+ })
+ }, interval)
+ },
+
+ /**
+ * 触发高速轮询模式
+ * 在用户发送消息后调用
+ */
+ triggerFastPolling() {
+ console.log('[chat-detail] 激活高速轮询模式')
+ this.isFastPolling = true
+ this.startPolling() // 立即重启轮询以应用新间隔
+
+ // 清除旧的定时器
+ if (this.fastPollingTimeout) clearTimeout(this.fastPollingTimeout)
+
+ // 60秒后自动恢复普通模式
+ this.fastPollingTimeout = setTimeout(() => {
+ console.log('[chat-detail] 高速模式结束,恢复省流模式')
+ this.isFastPolling = false
+ // 不需要立即重启,下次轮询会自动使用新间隔
+ }, 60000)
+ },
+
+ /**
+ * 停止消息轮询
+ */
+ stopPolling() {
+ if (this.pollingTimer) {
+ clearTimeout(this.pollingTimer)
+ this.pollingTimer = null
+ }
+ },
+
+ /**
+ * 检查新消息
+ */
+ async checkNewMessages() {
+ // 如果正在加载中、正在发送消息或页面不可见,跳过
+ if (this.data.loading || this.data.loadingMore || this.data.isSending) return
+
+ const { characterId, messages, conversationId } = this.data
+ if (!characterId) return
+
+ try {
+ // 1. 获取最新的一页消息
+ // 添加时间戳防止缓存
+ const res = await api.chat.getChatHistoryByCharacter(characterId, {
+ limit: 20,
+ page: 1,
+ _t: Date.now()
+ })
+
+ if (res.success && res.data && res.data.length > 0) {
+ // 转换消息格式
+ const latestMessages = res.data.map(msg => this.transformMessage(msg))
+
+ // 筛选出本地不存在的新消息
+ // 重点:只筛选对方发来的消息(isMe: false),避免本地已存在的用户消息重复显示
+ // 用户自己的消息(isMe: true)由本地乐观更新处理,轮询时不重复添加
+ const newMessages = latestMessages.filter(msg => {
+ // 2. 本地必须不存在该ID
+ const exists = messages.some(m => m.id === msg.id)
+ return !exists
+ })
+
+ if (newMessages.length > 0) {
+ console.log('[chat-detail] 轮询发现新消息:', newMessages.length, '条')
+
+ // 将新消息追加到列表末尾
+ // 按时间排序确保顺序正确
+ newMessages.sort((a, b) => new Date(a.time) - new Date(b.time))
+
+ const updatedMessages = [...messages, ...newMessages]
+
+ this.setData({
+ messages: updatedMessages
+ }, () => {
+ this.scrollToBottom()
+
+ // 如果有新消息,标记会话已读
+ if (this.data.conversationId) {
+ this.markConversationAsRead(this.data.conversationId)
+ }
+
+ // 收到新消息时,如果是对方发的,也可以触发一下高速模式,以便快速接收连续回复
+ this.triggerFastPolling()
+
+ // 更新本地缓存
+ this.saveMessagesToCache(updatedMessages)
+ })
+ }
+ }
+
+ // 2. 检查会话模式状态
+ if (conversationId) {
+ // 降低频率:只有在高速模式或者每5次轮询检查一次会话状态
+ // 简单起见,这里每次轮询都检查(因为是 silent 请求,且频率不高)
+ this.checkConversationStatus(conversationId)
+ }
+
+ } catch (err) {
+ // 静默失败,不打印过多日志
+ }
+ },
+
+ /**
+ * 检查会话状态 (AI/Human 模式)
+ */
+ async checkConversationStatus(conversationId) {
+ try {
+ const res = await api.chat.getConversationDetail(conversationId)
+ if (res && res.success && res.data) {
+ const mode = res.data.currentMode || res.data.mode || 'ai'
+
+ // 如果模式发生变化,或者首次获取
+ if (mode !== this.data.chatMode) {
+ console.log('[chat-detail] 会话模式变更为:', mode)
+ this.setData({ chatMode: mode })
+
+ // 移除人工模式的显式提示,保持沉浸感
+ // 让用户感觉不到是真人在接管
+ }
+ }
+ } catch (err) {
+ // 静默失败
+ }
+ },
+
/**
* 标记会话已读
* 进入聊天详情页时调用,清除未读数
@@ -450,68 +617,101 @@ Page({
/**
* 加载聊天历史(首次加载最近20条)
+ * 优化策略:优先从本地缓存加载,实现"无感"体验,防止后端切换模式导致消息丢失
*/
async loadChatHistory() {
const { characterId, pageSize } = this.data
if (!characterId) {
- const welcomeMsg = {
- id: 'welcome',
- text: `你好!我是${this.data.character.name},很高兴认识你~`,
- isMe: false,
- time: util.formatTime(new Date(), 'HH:mm'),
- type: 'text'
- }
- this.setData({
- messages: [welcomeMsg],
- isFirstLoad: false,
- hasMore: false
- })
+ this.showWelcomeMessage()
return
}
+ // 1. 先尝试从本地缓存加载,确保秒开且不丢失历史
+ const cachedMessages = wx.getStorageSync(`chat_history_${characterId}`) || []
+ if (cachedMessages.length > 0) {
+ console.log(`[chat-detail] 从本地缓存加载了 ${cachedMessages.length} 条消息`)
+ this.setData({
+ messages: cachedMessages,
+ isFirstLoad: false,
+ hasMore: true // 假设还有更多,允许下拉
+ }, () => {
+ this.scrollToBottom()
+ })
+ }
+
try {
- console.log('[chat-detail] 开始加载聊天历史, characterId:', characterId)
+ console.log('[chat-detail] 开始从API同步聊天历史, characterId:', characterId)
// 首次只加载最近20条消息
const res = await api.chat.getChatHistoryByCharacter(characterId, {
limit: pageSize,
- page: 1
+ page: 1,
+ _t: Date.now() // 防缓存
})
- console.log('[chat-detail] API响应:', JSON.stringify(res).substring(0, 200))
-
if (res.success && res.data && res.data.length > 0) {
- console.log('[chat-detail] 收到历史消息数量:', res.data.length)
+ console.log('[chat-detail] API返回消息数量:', res.data.length)
- const messages = res.data.map(msg => this.transformMessage(msg))
+ const apiMessages = res.data.map(msg => this.transformMessage(msg))
- this.setData({
- messages,
- hasMore: res.data.length >= pageSize,
- page: 1,
- isFirstLoad: false
- })
+ // 合并策略:以API数据为准更新,但保留API未返回的本地旧数据(如果有)
+ // 简单起见,如果本地为空,直接用API;如果本地有值,做去重合并
- console.log('[chat-detail] 消息已设置, 当前数量:', this.data.messages.length)
- console.log('[chat-detail] 首次加载完成,不自动滚动到底部')
- } else {
- console.log('[chat-detail] 没有历史记录,显示欢迎消息')
- const welcomeMsg = {
- id: 'welcome',
- text: `你好!我是${this.data.character.name},很高兴认识你~`,
- isMe: false,
- time: util.formatTime(new Date(), 'HH:mm'),
- type: 'text'
+ let finalMessages = []
+ if (this.data.messages.length === 0) {
+ finalMessages = apiMessages
+ } else {
+ // 合并逻辑:将API返回的新消息合并到现有列表中
+ // 1. 创建现有消息的ID映射
+ const existingIds = new Set(this.data.messages.map(m => m.id))
+
+ // 2. 找出API返回中本地没有的消息
+ const newApiMessages = apiMessages.filter(m => !existingIds.has(m.id))
+
+ // 3. 将新消息追加进来 (注意顺序)
+ // API通常返回按时间排序好的,或者我们需要重排
+ finalMessages = [...this.data.messages, ...newApiMessages]
+
+ // 4. 按时间重新排序确保正确
+ finalMessages.sort((a, b) => new Date(a.time) - new Date(b.time))
+ }
+
+ // 只有当消息列表真的发生变化时才更新,避免闪烁
+ if (finalMessages.length !== this.data.messages.length) {
+ this.setData({
+ messages: finalMessages,
+ hasMore: res.data.length >= pageSize,
+ page: 1,
+ isFirstLoad: false
+ }, () => {
+ this.scrollToBottom()
+ // 更新缓存
+ this.saveMessagesToCache(finalMessages)
+ })
+ } else {
+ console.log('[chat-detail] 消息列表无变化,跳过更新')
+ this.setData({ isFirstLoad: false })
+ }
+
+ } else {
+ console.log('[chat-detail] API未返回历史记录')
+ if (this.data.messages.length === 0) {
+ this.showWelcomeMessage()
}
- this.setData({
- messages: [welcomeMsg],
- isFirstLoad: false,
- hasMore: false
- })
}
} catch (err) {
console.log('加载聊天历史失败:', err)
+ if (this.data.messages.length === 0) {
+ this.showWelcomeMessage()
+ }
+ }
+ },
+
+ /**
+ * 显示欢迎消息
+ */
+ showWelcomeMessage() {
const welcomeMsg = {
id: 'welcome',
text: `你好!我是${this.data.character.name},很高兴认识你~`,
@@ -524,7 +724,16 @@ Page({
isFirstLoad: false,
hasMore: false
})
- }
+ },
+
+ /**
+ * 将消息保存到本地缓存
+ */
+ saveMessagesToCache(messages) {
+ if (!this.data.characterId || !messages || messages.length === 0) return
+ // 只缓存最近100条,避免Storage爆满
+ const messagesToSave = messages.slice(-100)
+ wx.setStorageSync(`chat_history_${this.data.characterId}`, messagesToSave)
},
/**
@@ -591,10 +800,35 @@ Page({
* 转换消息格式
*/
transformMessage(msg) {
+ // 调试日志:打印原始消息结构,帮助排查 role/sender_id 问题
+ console.log('[chat-detail] transformMessage raw:', msg)
+
+ let isMe = false
+ const currentUserId = this.data.userId || app.globalData.userId || wx.getStorageSync(config.STORAGE_KEYS.USER_ID)
+
+ // 1. 优先使用 sender_id 判断 (最准确)
+ if (msg.sender_id && currentUserId) {
+ isMe = String(msg.sender_id) === String(currentUserId)
+ }
+ // 2. 其次使用 role 判断
+ else if (msg.role) {
+ // 只有明确是 user 且不是 assistant/system 时才认为是自己
+ isMe = msg.role === 'user'
+ }
+ // 3. 最后尝试 sender_type
+ else if (msg.sender_type) {
+ isMe = msg.sender_type === 'user'
+ }
+
+ // 4. 强制修正:如果 role 是 assistant 或 system,绝对不是我
+ if (msg.role === 'assistant' || msg.role === 'system') {
+ isMe = false
+ }
+
const baseMessage = {
id: msg.id,
text: msg.content,
- isMe: msg.role === 'user',
+ isMe: isMe,
time: util.formatTime(msg.created_at || msg.timestamp, 'HH:mm'),
type: msg.message_type || 'text'
}
@@ -743,6 +977,9 @@ Page({
// 只检查输入是否为空
if (!inputText.trim()) return
+
+ // 触发高速轮询模式,确保及时收到回复
+ this.triggerFastPolling()
// 检查登录
if (app.checkNeedLogin()) return
@@ -842,8 +1079,10 @@ Page({
console.log('[chat-detail] 合并处理消息:', messagesToProcess.length, '条')
console.log('[chat-detail] 合并后内容:', combinedMessage)
- // 显示AI正在输入
- this.setData({ isTyping: true })
+ // 显示AI正在输入(仅在AI模式下)
+ if (this.data.chatMode === 'ai') {
+ this.setData({ isTyping: true })
+ }
try {
// 构建对话历史(最近10条消息,只包含文字消息)
@@ -918,22 +1157,29 @@ Page({
})
}
- // 添加AI回复
- const aiMessage = {
- id: res.data.id || util.generateId(),
- text: res.data.content || res.data.message,
- isMe: false,
- time: util.formatTime(new Date(), 'HH:mm'),
- audioUrl: res.data.audio_url,
- type: 'text' // 标记为文字消息
+ // 只有当有回复内容时才添加AI消息
+ // 如果是人工模式(human),后端可能只返回成功但不返回content,或者content为空
+ const content = res.data.content || res.data.message
+ if (content) {
+ // 添加AI回复
+ const aiMessage = {
+ id: res.data.id || util.generateId(),
+ text: content,
+ isMe: false,
+ time: util.formatTime(new Date(), 'HH:mm'),
+ audioUrl: res.data.audio_url,
+ type: 'text' // 标记为文字消息
+ }
+
+ this.setData({
+ messages: [...this.data.messages, aiMessage]
+ }, () => {
+ // AI回复后滚动到底部
+ this.scrollToBottom()
+ })
+ } else {
+ console.log('[chat-detail] 收到空回复,不添加消息气泡 (可能是Human模式)')
}
-
- this.setData({
- messages: [...this.data.messages, aiMessage]
- }, () => {
- // AI回复后滚动到底部
- this.scrollToBottom()
- })
} else {
throw new Error(res.error || res.message || '发送失败')
}
diff --git a/pages/city-activities/city-activities.wxml b/pages/city-activities/city-activities.wxml
index e1ee6e9..a888e06 100644
--- a/pages/city-activities/city-activities.wxml
+++ b/pages/city-activities/city-activities.wxml
@@ -49,9 +49,9 @@
-
- {{item.venue}}
-
+
+ {{item.venue}}
+
diff --git a/pages/entertainment/entertainment.js b/pages/entertainment/entertainment.js
index d0aa799..69296a4 100644
--- a/pages/entertainment/entertainment.js
+++ b/pages/entertainment/entertainment.js
@@ -121,50 +121,32 @@ Page({
/**
* 加载功能入口图标
- * 从后台素材管理API加载 (group=entries)
+ * 从后台素材管理API加载
*/
async loadEntries() {
- // 暂时禁用API加载,使用本地配置的图标
- console.log('使用本地配置的功能入口图标')
- return;
- /*
try {
- const res = await api.pageAssets.getAssets('entries')
- console.log('功能入口 API响应:', res)
+ const res = await api.pageAssets.getEntertainmentCategories()
+ console.log('娱乐页分类图标 API响应:', res)
- if (res.success && res.data) {
- const icons = res.data
- const { categoryList } = this.data
+ if (res.success && res.data && res.data.length > 0) {
+ // 映射API数据到前端格式
+ const categoryList = res.data.map(item => ({
+ id: item.id,
+ name: item.name,
+ icon: this.processImageUrl(item.iconUrl || item.icon),
+ url: item.pagePath || item.url || '',
+ sort: item.sort || 0
+ })).sort((a, b) => a.sort - b.sort) // 按sort字段排序
- // 映射图标:搭子(id=1), 同城(id=2), 户外(id=3), 定制(id=4), 学堂(id=5), 传递(id=6)
- const idMap = {
- 1: 'entry_1', // 兴趣搭子
- 2: 'entry_2', // 同城活动
- 3: 'entry_3', // 户外郊游
- 4: 'entry_4', // 定制主题
- 5: 'entry_5', // 快乐学堂
- 6: 'entry_6' // 爱心传递
- }
-
- const updatedCategoryList = categoryList.map(item => {
- const assetKey = idMap[item.id]
- const iconUrl = icons[assetKey]
- if (iconUrl) {
- return {
- ...item,
- icon: this.processImageUrl(iconUrl)
- }
- }
- return item
- })
-
- this.setData({ categoryList: updatedCategoryList })
- console.log('已更新娱乐页功能入口图标')
+ this.setData({ categoryList })
+ console.log(`加载了 ${categoryList.length} 个娱乐页分类图标`)
+ } else {
+ console.log('娱乐页分类图标API返回为空,使用本地默认配置')
}
} catch (err) {
- console.error('加载功能入口失败', err)
+ console.error('加载功能入口图标失败', err)
+ // 失败时保持使用 data 初始化时的本地配置,无需额外操作
}
- */
},
/**
@@ -523,7 +505,13 @@ Page({
onCategoryTap(e) {
const { id, name, url } = e.currentTarget.dataset
- // 从版本配置中获取分类信息
+ // 优先跳转配置的 URL
+ if (url) {
+ wx.navigateTo({ url: url })
+ return
+ }
+
+ // 从版本配置中获取分类信息(作为兜底)
const categoryList = versionConfig.getCategoryList()
const category = categoryList.find(item => item.id === id)
diff --git a/pages/interest-partner/interest-partner.js b/pages/interest-partner/interest-partner.js
index 7309346..bbc0c93 100644
--- a/pages/interest-partner/interest-partner.js
+++ b/pages/interest-partner/interest-partner.js
@@ -57,6 +57,7 @@ Page({
async loadBanner() {
try {
// 尝试获取 interest_partner 分组的素材
+ // 后端已修复:请求 group=interest_partner 时,会自动返回 interest_partner_banners 的数据,并封装成 { banners: [] } 格式
const res = await api.pageAssets.getAssets('interest_partner')
console.log('[兴趣搭子] Banner API响应:', res)
@@ -66,12 +67,14 @@ Page({
// 如果 banners 为空,尝试读取单图字段
if (!banners || banners.length === 0) {
- const singleBanner = res.data.banner || res.data.interest_banner || res.data.top_banner
+ const singleBanner = res.data.banner || res.data.interest_banner || res.data.top_banner || (Array.isArray(res.data) ? res.data : [])
if (singleBanner) {
// 支持逗号分隔的多图字符串
if (typeof singleBanner === 'string' && singleBanner.includes(',')) {
banners = singleBanner.split(',').map(s => s.trim()).filter(s => s)
- } else {
+ } else if (Array.isArray(singleBanner)) {
+ banners = singleBanner
+ } else if (typeof singleBanner === 'string') {
banners = [singleBanner]
}
}
@@ -79,8 +82,16 @@ Page({
if (banners && banners.length > 0) {
// 处理图片URL
- const bannerList = banners.map(url => {
- if (typeof url !== 'string') return ''
+ const bannerList = banners.map(item => {
+ let url = ''
+ if (typeof item === 'string') {
+ url = item
+ } else if (item && typeof item === 'object') {
+ url = item.asset_url || item.url || item.imageUrl || ''
+ }
+
+ if (!url) return ''
+
let fullUrl = url
if (fullUrl.startsWith('/')) {
fullUrl = config.API_BASE_URL.replace('/api', '') + fullUrl
@@ -138,10 +149,21 @@ Page({
console.log('[兴趣搭子] API原始响应:', JSON.stringify(res).substring(0, 500))
- // 线上API返回格式:{ success: true, data: [...] } 或 { success: true, data: { list: [...] } }
+ // 线上API返回格式:{ success: true, data: [...] } 或 { success: true, data: { list: [...] } } 或 { success: true, data: { data: [...] } }
if (res.success && res.data) {
- // 兼容两种返回格式
- let partnerList = Array.isArray(res.data) ? res.data : (res.data.list || [])
+ // 兼容多种返回格式
+ // 1. res.data 是数组
+ // 2. res.data.data 是数组 (Laravel分页或标准包装)
+ // 3. res.data.list 是数组 (自定义列表包装)
+ let partnerList = []
+
+ if (Array.isArray(res.data)) {
+ partnerList = res.data
+ } else if (res.data.data && Array.isArray(res.data.data)) {
+ partnerList = res.data.data
+ } else if (res.data.list && Array.isArray(res.data.list)) {
+ partnerList = res.data.list
+ }
console.log('[兴趣搭子] 解析后的列表数量:', partnerList.length)
if (partnerList.length > 0) {
diff --git a/pages/interest-partner/interest-partner.wxml b/pages/interest-partner/interest-partner.wxml
index 7110fc0..e4a5b53 100644
--- a/pages/interest-partner/interest-partner.wxml
+++ b/pages/interest-partner/interest-partner.wxml
@@ -28,15 +28,6 @@
-
-
-
-
-
-
- 寻找志同道合的伙伴
- 加入感兴趣的社群,开启精彩退休生活
-
diff --git a/pages/interest-partner/interest-partner.wxss b/pages/interest-partner/interest-partner.wxss
index 0413b0c..461eaf5 100644
--- a/pages/interest-partner/interest-partner.wxss
+++ b/pages/interest-partner/interest-partner.wxss
@@ -19,7 +19,7 @@ page {
/* 顶部Banner */
.hero-banner {
margin: 32rpx 32rpx 48rpx;
- height: 240rpx;
+ height: 400rpx;
border-radius: 32rpx;
position: relative;
overflow: hidden;
diff --git a/pages/performance/performance.js b/pages/performance/performance.js
index 0c6ebed..2fafce9 100644
--- a/pages/performance/performance.js
+++ b/pages/performance/performance.js
@@ -164,13 +164,31 @@ Page({
// 优先使用API返回的中文等级名称
const userLevel = record.fromUserRoleName || record.userRoleName || roleMap[record.fromUserRole] || roleMap[record.userRole] || record.levelText || '普通用户';
+ const type = record.orderType || record.type;
+ let productName = this.getOrderTypeText(type);
+
+ // 后端已把年卡会员改成分销商等级,不显示 VIP会员
+ if (type === 'vip') {
+ productName = '';
+ }
+
+ // Determine level class
+ const rawRole = record.fromUserRole || record.userRole || record.distributorRole || '';
+ let levelClass = '';
+ if (rawRole.includes('soulmate')) levelClass = 'tag-soulmate';
+ else if (rawRole.includes('guardian')) levelClass = 'tag-guardian';
+ else if (rawRole.includes('companion')) levelClass = 'tag-companion';
+ else if (rawRole.includes('listener')) levelClass = 'tag-listener';
+ else if (rawRole.includes('partner')) levelClass = 'tag-partner';
+
return {
id: record.id,
orderNo: record.orderNo || record.order_no || record.id || '---',
userName: record.fromUserName || record.userName || '匿名用户',
userAvatar: avatar || this.data.defaultAvatar,
- productName: this.getOrderTypeText(record.orderType || record.type),
+ productName: productName,
userLevel: userLevel,
+ levelClass: levelClass,
orderAmount: record.orderAmount ? Number(record.orderAmount).toFixed(2) : (record.amount ? Number(record.amount).toFixed(2) : '0.00'),
time: fmtTime
}
diff --git a/pages/performance/performance.wxml b/pages/performance/performance.wxml
index 3b9e80b..d4e7be8 100644
--- a/pages/performance/performance.wxml
+++ b/pages/performance/performance.wxml
@@ -53,7 +53,10 @@
{{item.userName}}
- {{item.productName}} · {{item.userLevel}}
+
+ {{item.productName ? item.productName + ' · ' : ''}}
+ {{item.userLevel}}
+
时间: {{item.time}}
单号: {{item.orderNo}}
diff --git a/pages/performance/performance.wxss b/pages/performance/performance.wxss
index ba03a8c..b221069 100644
--- a/pages/performance/performance.wxss
+++ b/pages/performance/performance.wxss
@@ -191,6 +191,43 @@
color: #B06AB3;
}
+/* Tag Styles */
+.tag-badge {
+ display: inline-block;
+ font-size: 20rpx;
+ font-weight: 700;
+ padding: 2rpx 10rpx;
+ border-radius: 999rpx;
+ margin-left: 8rpx;
+ background: #E5E7EB;
+ color: #374151;
+}
+
+.tag-soulmate {
+ background: #7C3AED;
+ color: #ffffff;
+}
+
+.tag-guardian {
+ background: #3B82F6;
+ color: #ffffff;
+}
+
+.tag-companion {
+ background: #10B981;
+ color: #ffffff;
+}
+
+.tag-listener {
+ background: #F59E0B;
+ color: #ffffff;
+}
+
+.tag-partner {
+ background: #EF4444;
+ color: #ffffff;
+}
+
/* 底部提示 */
.bottom-tip {
text-align: center;
diff --git a/pages/profile/profile.js b/pages/profile/profile.js
index bf870ce..9c5cb94 100644
--- a/pages/profile/profile.js
+++ b/pages/profile/profile.js
@@ -161,7 +161,7 @@ Page({
const roleMap = {
'soulmate': { text: '心伴会员', class: 'vip-soulmate' },
'guardian': { text: '守护会员', class: 'vip-guardian' },
- 'companion': { text: '心伴会员', class: 'vip-companion' },
+ 'companion': { text: '陪伴会员', class: 'vip-companion' },
'listener': { text: '倾听会员', class: 'vip-listener' },
'partner': { text: '城市合伙人', class: 'vip-partner' }
};
diff --git a/pages/team/team.js b/pages/team/team.js
index 3e5492e..6fcf8cf 100644
--- a/pages/team/team.js
+++ b/pages/team/team.js
@@ -137,6 +137,15 @@ Page({
roleMap[x.distributorRole] || roleMap[user.distributorRole] ||
roleMap[x.role] || roleMap[user.role] ||
'普通用户';
+
+ // Determine level class
+ const rawRole = x.userRole || user.userRole || x.distributorRole || user.distributorRole || x.role || user.role || '';
+ let levelClass = '';
+ if (rawRole.includes('soulmate')) levelClass = 'tag-soulmate';
+ else if (rawRole.includes('guardian')) levelClass = 'tag-guardian';
+ else if (rawRole.includes('companion')) levelClass = 'tag-companion';
+ else if (rawRole.includes('listener')) levelClass = 'tag-listener';
+ else if (rawRole.includes('partner')) levelClass = 'tag-partner';
return {
...x,
@@ -144,6 +153,7 @@ Page({
userAvatar: avatar || this.data.defaultAvatar,
userName: name,
levelText: levelText,
+ levelClass: levelClass,
totalContribution: contribution,
boundAtText: this.formatDate(new Date(dateStr))
};
diff --git a/pages/team/team.wxml b/pages/team/team.wxml
index 436436e..0e83595 100644
--- a/pages/team/team.wxml
+++ b/pages/team/team.wxml
@@ -49,7 +49,7 @@
{{item.userName}}
- {{item.levelText}}
+ {{item.levelText}}
{{item.boundAtText}}
diff --git a/pages/team/team.wxss b/pages/team/team.wxss
index e755b47..890a4bf 100644
--- a/pages/team/team.wxss
+++ b/pages/team/team.wxss
@@ -150,14 +150,39 @@
}
.tag-badge {
- background: #B06AB3;
- color: #ffffff;
+ background: #E5E7EB;
+ color: #374151;
font-size: 20rpx;
font-weight: 700;
padding: 4rpx 12rpx;
border-radius: 999rpx;
}
+.tag-soulmate {
+ background: #7C3AED;
+ color: #ffffff;
+}
+
+.tag-guardian {
+ background: #3B82F6;
+ color: #ffffff;
+}
+
+.tag-companion {
+ background: #10B981;
+ color: #ffffff;
+}
+
+.tag-listener {
+ background: #F59E0B;
+ color: #ffffff;
+}
+
+.tag-partner {
+ background: #EF4444;
+ color: #ffffff;
+}
+
.member-meta {
font-size: 24rpx;
color: #6B7280;
diff --git a/utils/api.js b/utils/api.js
index a6700f3..bf75c22 100644
--- a/utils/api.js
+++ b/utils/api.js
@@ -402,6 +402,12 @@ const chat = {
*/
getConversations: () => request('/conversations', { silent: true }),
+ /**
+ * 获取会话详情
+ * @param {string} id - 会话ID
+ */
+ getConversationDetail: (id) => request(`/conversations/${id}`, { silent: true }),
+
/**
* 删除会话
* @param {string} conversationId - 会话ID
@@ -910,6 +916,11 @@ const pageAssets = {
*/
getEntertainmentBanners: () => request('/page-assets/entertainment-banners'),
+ /**
+ * 获取娱乐页分类图标列表
+ */
+ getEntertainmentCategories: () => request('/page-assets/entertainment-categories'),
+
/**
* 获取合作入驻页在线Banner列表
*/