合并merchant文件

This commit is contained in:
2026-03-20 15:43:33 +08:00
parent 29f588a2b2
commit 620ae742df
12 changed files with 3477 additions and 0 deletions

View File

@@ -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>