合并merchant文件
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<!-- 商家端 - 消息中心页面 -->
|
||||
<template>
|
||||
<view class="messages-page">
|
||||
<<<<<<< HEAD
|
||||
<view class="tabs">
|
||||
<view class="tab" :class="{ active: currentTab === 'chat' }" @click="switchTab('chat')">会话列表</view>
|
||||
<view class="tab" :class="{ active: currentTab === 'all' }" @click="switchTab('all')">全部消息</view>
|
||||
@@ -40,6 +41,48 @@
|
||||
<view v-if="!msg.is_read" class="unread-dot"></view>
|
||||
</view>
|
||||
</view>
|
||||
=======
|
||||
<view class="header">
|
||||
<text class="header-title">消息</text>
|
||||
<text class="header-subtitle">与客户的聊天记录</text>
|
||||
</view>
|
||||
|
||||
<scroll-view class="messages-list" scroll-y :refresher-enabled="true" :refresher-triggered="refreshing" @refresherrefresh="onRefresh">
|
||||
<view v-if="loading && conversations.length === 0" class="loading-container">
|
||||
<text class="loading-icon">⏳</text>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
|
||||
<view v-else-if="conversations.length === 0" class="empty-container">
|
||||
<text class="empty-icon">💬</text>
|
||||
<text class="empty-text">暂无会话</text>
|
||||
<text class="empty-hint">有客户消息时会在这里显示</text>
|
||||
</view>
|
||||
|
||||
<view v-else class="conv-list">
|
||||
<view v-for="conv in conversations" :key="conv.sessionId" class="conversation-card" @click="goToChat(conv)">
|
||||
<view class="conv-avatar-wrap">
|
||||
<image class="conv-avatar" :src="conv.avatar || '/static/images/default-avatar.png'" mode="aspectFill" />
|
||||
<view v-if="conv.unread > 0" class="online-dot"></view>
|
||||
</view>
|
||||
<view class="conv-info">
|
||||
<view class="conv-row">
|
||||
<text class="conv-name">{{ conv.name }}</text>
|
||||
<text class="conv-time">{{ conv.lastTime }}</text>
|
||||
</view>
|
||||
<view class="conv-bottom">
|
||||
<text class="conv-preview">{{ conv.lastMessage }}</text>
|
||||
<view v-if="conv.unread > 0" class="unread-badge">
|
||||
<text class="unread-num">{{ conv.unread > 99 ? '99+' : conv.unread }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="conv-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="safe-bottom"></view>
|
||||
>>>>>>> local-backup-root-cyj
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -65,6 +108,10 @@
|
||||
avatar: string
|
||||
lastMessage: string
|
||||
lastTime: string
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
lastTimeRaw: string
|
||||
>>>>>>> local-backup-root-cyj
|
||||
unread: number
|
||||
userId: string
|
||||
}
|
||||
@@ -72,8 +119,11 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
<<<<<<< HEAD
|
||||
currentTab: 'chat',
|
||||
messages: [] as MessageType[],
|
||||
=======
|
||||
>>>>>>> local-backup-root-cyj
|
||||
conversations: [] as ConversationType[],
|
||||
loading: false,
|
||||
refreshing: false,
|
||||
@@ -111,17 +161,24 @@
|
||||
const response = await query.execute()
|
||||
|
||||
if (response.error != null || !response.data) {
|
||||
<<<<<<< HEAD
|
||||
this.messages = []
|
||||
=======
|
||||
>>>>>>> local-backup-root-cyj
|
||||
this.conversations = []
|
||||
return
|
||||
}
|
||||
|
||||
const rawData = response.data as any[]
|
||||
<<<<<<< HEAD
|
||||
const messagesData: MessageType[] = []
|
||||
=======
|
||||
>>>>>>> local-backup-root-cyj
|
||||
const sessionMap = new Map<string, ConversationType>()
|
||||
|
||||
for (let i = 0; i < rawData.length; i++) {
|
||||
const item = rawData[i] as UTSJSONObject
|
||||
<<<<<<< HEAD
|
||||
const msg: MessageType = {
|
||||
id: item.getString('id') || '',
|
||||
session_id: item.getString('session_id') || '',
|
||||
@@ -145,20 +202,61 @@
|
||||
avatar: '',
|
||||
lastMessage: msg.content,
|
||||
lastTime: this.formatTime(msg.created_at),
|
||||
=======
|
||||
const isFromUser = item.getBoolean('is_from_user') || false
|
||||
const senderId = item.getString('sender_id') || ''
|
||||
const receiverId = item.getString('receiver_id') || ''
|
||||
|
||||
// is_from_user=true 表示消息来自用户,sender_id是用户ID
|
||||
// is_from_user=false 表示消息来自商家,receiver_id是用户ID
|
||||
const otherUserId = isFromUser ? senderId : receiverId
|
||||
|
||||
// 只用 otherUserId 分组,确保每个客户只有一个会话
|
||||
if (otherUserId === '' || otherUserId === this.merchantId) continue
|
||||
|
||||
const isRead = item.getBoolean('is_read') || false
|
||||
const content = item.getString('content') || ''
|
||||
const createdAt = item.getString('created_at') || ''
|
||||
const sessionId = item.getString('session_id') || otherUserId
|
||||
|
||||
if (!sessionMap.has(otherUserId)) {
|
||||
sessionMap.set(otherUserId, {
|
||||
sessionId: sessionId,
|
||||
name: '客户',
|
||||
avatar: '',
|
||||
lastMessage: content,
|
||||
lastTime: this.formatTime(createdAt),
|
||||
lastTimeRaw: createdAt,
|
||||
>>>>>>> local-backup-root-cyj
|
||||
unread: 0,
|
||||
userId: otherUserId
|
||||
})
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
const conv = sessionMap.get(sessionId)!
|
||||
conv.lastMessage = msg.content
|
||||
conv.lastTime = this.formatTime(msg.created_at)
|
||||
if (!msg.is_read && !msg.is_from_user) {
|
||||
=======
|
||||
const conv = sessionMap.get(otherUserId)!
|
||||
// 更新最后一条消息(按时间最新的)
|
||||
if (createdAt > conv.lastTimeRaw) {
|
||||
conv.lastMessage = content
|
||||
conv.lastTime = this.formatTime(createdAt)
|
||||
conv.lastTimeRaw = createdAt
|
||||
}
|
||||
// 未读消息:消息来自用户且未读
|
||||
if (!isRead && isFromUser) {
|
||||
>>>>>>> local-backup-root-cyj
|
||||
conv.unread++
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
this.messages = messagesData
|
||||
=======
|
||||
>>>>>>> local-backup-root-cyj
|
||||
this.conversations = Array.from(sessionMap.values()).sort((a, b) => b.unread - a.unread)
|
||||
|
||||
} catch (e) {
|
||||
@@ -175,15 +273,19 @@
|
||||
})
|
||||
},
|
||||
|
||||
<<<<<<< HEAD
|
||||
switchTab(tab: string) {
|
||||
this.currentTab = tab
|
||||
},
|
||||
|
||||
=======
|
||||
>>>>>>> local-backup-root-cyj
|
||||
onRefresh() {
|
||||
this.refreshing = true
|
||||
this.loadMessages()
|
||||
},
|
||||
|
||||
<<<<<<< HEAD
|
||||
viewMessage(msg: MessageType) {
|
||||
if (!msg.is_read) {
|
||||
supa.from('ml_chat_messages').update({ is_read: true }).eq('id', msg.id).execute()
|
||||
@@ -196,6 +298,8 @@
|
||||
})
|
||||
},
|
||||
|
||||
=======
|
||||
>>>>>>> local-backup-root-cyj
|
||||
formatTime(timeStr: string): string {
|
||||
if (!timeStr) return ''
|
||||
const date = new Date(timeStr)
|
||||
@@ -215,6 +319,7 @@
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<<<<<<< HEAD
|
||||
.messages-page { background-color: #f5f5f5; min-height: 100vh; }
|
||||
.tabs { display: flex; background-color: #fff; padding: 0 20rpx; position: sticky; top: 0; z-index: 10; }
|
||||
.tab { flex: 1; text-align: center; padding: 24rpx 0; font-size: 28rpx; color: #666; position: relative; }
|
||||
@@ -244,4 +349,39 @@
|
||||
.message-time { font-size: 22rpx; color: #999; }
|
||||
.message-text { font-size: 26rpx; color: #666; line-height: 1.4; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
|
||||
.unread-dot { position: absolute; top: 30rpx; right: 30rpx; width: 16rpx; height: 16rpx; background-color: #FF3B30; border-radius: 50%; }
|
||||
=======
|
||||
.messages-page { background-color: #f5f7fa; min-height: 100vh; display: flex; flex-direction: column; }
|
||||
|
||||
.header { background-color: #fff; padding-top: 60rpx; padding-bottom: 24rpx; padding-left: 30rpx; padding-right: 30rpx; border-bottom-width: 1rpx; border-bottom-style: solid; border-bottom-color: #eee; }
|
||||
.header-title { font-size: 44rpx; font-weight: bold; color: #333; display: block; margin-bottom: 8rpx; }
|
||||
.header-subtitle { font-size: 26rpx; color: #999; }
|
||||
|
||||
.messages-list { flex: 1; padding: 20rpx; }
|
||||
|
||||
.loading-container { display: flex; flex-direction: column; align-items: center; justify-content: center; padding-top: 100rpx; padding-bottom: 100rpx; }
|
||||
.loading-icon { font-size: 60rpx; margin-bottom: 20rpx; }
|
||||
.loading-text { font-size: 28rpx; color: #999; }
|
||||
|
||||
.empty-container { display: flex; flex-direction: column; align-items: center; justify-content: center; padding-top: 100rpx; padding-bottom: 100rpx; }
|
||||
.empty-icon { font-size: 100rpx; margin-bottom: 20rpx; }
|
||||
.empty-text { font-size: 32rpx; color: #666; margin-bottom: 10rpx; }
|
||||
.empty-hint { font-size: 26rpx; color: #999; }
|
||||
|
||||
.conv-list { display: flex; flex-direction: column; }
|
||||
.conversation-card { display: flex; flex-direction: row; align-items: center; background-color: #fff; border-radius: 20rpx; padding: 24rpx; margin-bottom: 16rpx; }
|
||||
.conv-avatar-wrap { position: relative; margin-right: 20rpx; }
|
||||
.conv-avatar { width: 100rpx; height: 100rpx; border-radius: 50rpx; background-color: #e0e0e0; }
|
||||
.online-dot { position: absolute; bottom: 4rpx; right: 4rpx; width: 24rpx; height: 24rpx; background-color: #4CAF50; border-radius: 12rpx; border-width: 3rpx; border-style: solid; border-color: #fff; }
|
||||
.conv-info { flex: 1; }
|
||||
.conv-row { display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin-bottom: 12rpx; }
|
||||
.conv-name { font-size: 32rpx; color: #333; font-weight: bold; }
|
||||
.conv-time { font-size: 24rpx; color: #999; }
|
||||
.conv-bottom { display: flex; flex-direction: row; justify-content: space-between; align-items: center; }
|
||||
.conv-preview { font-size: 28rpx; color: #666; flex: 1; margin-right: 16rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.unread-badge { min-width: 36rpx; height: 36rpx; background-color: #FF3B30; border-radius: 18rpx; display: flex; align-items: center; justify-content: center; padding-left: 10rpx; padding-right: 10rpx; }
|
||||
.unread-num { font-size: 22rpx; color: #fff; font-weight: bold; }
|
||||
.conv-arrow { font-size: 40rpx; color: #ccc; margin-left: 10rpx; }
|
||||
|
||||
.safe-bottom { height: 30rpx; }
|
||||
>>>>>>> local-backup-root-cyj
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user