hookehuyr

feat(消息): 实现消息详情和聊天功能的API集成

- 在点击对话时根据类型获取系统消息详情或聊天记录
- 重构MessageDetail组件以显示真实数据而非模拟数据
- 添加消息发送功能并与API集成
- 完善系统消息的显示样式和元信息
......@@ -32,7 +32,15 @@
<!-- 系统通知样式 -->
<view v-if="conversation?.type === 'system'" class="system-content">
<view class="message-content">
<text class="text-base">{{ conversation?.lastMessage }}</text>
<view class="message-title" v-if="conversation?.title">
<text class="text-lg font-medium">{{ conversation.title }}</text>
</view>
<view class="message-body">
<text class="text-base">{{ conversation?.note || conversation?.lastMessage }}</text>
</view>
<view class="message-meta" v-if="conversation?.created_time_desc">
<text class="text-sm text-gray-500">发布时间:{{ conversation.created_time_desc }}</text>
</view>
</view>
</view>
......@@ -107,6 +115,8 @@
<script setup>
import { ref, computed, watch, nextTick } from 'vue'
import Taro from '@tarojs/taro'
import { sendChatAPI } from '@/api/chat'
import { useUserStore } from '@/stores/user'
/**
* 消息详情组件 Props
......@@ -149,46 +159,33 @@ const messages = ref([])
* 初始化聊天消息
*/
const initChatMessages = () => {
const userStore = useUserStore()
const currentUserId = userStore.userInfo?.id
if (props.conversation?.type === 'chat') {
// 模拟历史消息
// 使用从API获取的真实聊天记录
if (props.conversation.chatMessages && props.conversation.chatMessages.length > 0) {
// 转换API数据格式为组件需要的格式
messages.value = props.conversation.chatMessages.map(msg => ({
type: msg.created_by === currentUserId ? 'sent' : 'received', // 根据创建者ID判断
content: msg.note || '',
time: msg.created_time_desc || '',
id: msg.id
}))
} else {
// 如果没有历史消息,显示欢迎消息
messages.value = [
{
type: 'received',
content: props.conversation.lastMessage || '您好,有什么可以帮助您的吗?',
time: '10:30'
},
{
type: 'sent',
content: '我想咨询一下车辆的相关问题',
time: '10:32'
},
{
type: 'received',
content: '好的,请问您具体想了解哪方面的信息呢?我会尽力为您解答。',
time: '10:33'
},
{
type: 'sent',
content: '我想咨询一下车辆的相关问题',
time: '10:34'
},
{
type: 'received',
content: '好的,请问您具体想了解哪方面的信息呢?我会尽力为您解答。',
time: '10:35'
},
{
type: 'sent',
content: '我想咨询一下车辆的相关问题',
time: '10:36'
},
{
type: 'received',
content: '好的,请问您具体想了解哪方面的信息呢?我会尽力为您解答。',
time: '10:37'
},
content: '您好,有什么可以帮助您的吗?',
time: new Date().toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
})
}
]
}
}
}
/**
......@@ -203,46 +200,75 @@ const sendMessage = async () => {
return
}
// 只有聊天类型才能发送消息
if (props.conversation?.type !== 'chat') {
Taro.showToast({
title: '系统消息无法回复',
icon: 'none'
})
return
}
const messageContent = inputMessage.value.trim()
try {
// 先添加到本地显示
const newMessage = {
type: 'sent',
content: inputMessage.value.trim(),
content: messageContent,
time: new Date().toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
})
}
messages.value.push(newMessage)
// 清空输入框
const messageContent = inputMessage.value
inputMessage.value = ''
// 滚动到底部
await nextTick()
scrollToBottom()
// 触发发送消息事件
// 调用API发送消息
const response = await sendChatAPI({
conversation_id: props.conversation.id,
note: messageContent
})
if (response.code) {
// 发送成功,更新消息ID
if (response.data && response.data.id) {
newMessage.id = response.data.id
}
// 触发发送消息事件,通知父组件更新列表
emit('sendMessage', {
conversation: props.conversation,
message: messageContent
})
// 模拟对方回复
setTimeout(() => {
const replyMessage = {
type: 'received',
content: '收到您的消息,我们会尽快处理并回复您。',
time: new Date().toLocaleTimeString('zh-CN', {
hour: '2-digit',
minute: '2-digit'
Taro.showToast({
title: '发送成功',
icon: 'success'
})
} else {
// 发送失败,移除本地消息
messages.value.pop()
Taro.showToast({
title: response.msg || '发送失败',
icon: 'error'
})
}
messages.value.push(replyMessage)
nextTick(() => {
scrollToBottom()
} catch (error) {
console.error('发送消息失败:', error)
// 发送失败,移除本地消息
messages.value.pop()
Taro.showToast({
title: '发送失败',
icon: 'error'
})
}, 1000)
}
}
/**
......@@ -350,6 +376,22 @@ watch(visible, (newVisible) => {
border-radius: 16rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.message-title {
margin-bottom: 16rpx;
padding-bottom: 12rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.message-body {
line-height: 1.6;
margin-bottom: 16rpx;
}
.message-meta {
padding-top: 12rpx;
border-top: 1rpx solid #f0f0f0;
}
}
}
......
......@@ -103,7 +103,7 @@ import MessageDetail from '@/components/MessageDetail.vue'
import { $ } from '@tarojs/extend'
import Taro from '@tarojs/taro'
// 导入接口
import { getMessagesListAPI } from '@/api/chat'
import { getMessagesListAPI, getMessagesDetailAPI, getChatListAPI } from '@/api/chat'
// 默认头像
const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'
......@@ -332,14 +332,67 @@ const loadMore = async () => {
// 点击对话 - 显示消息详情弹框
const onConversationClick = (conversation) => {
const onConversationClick = async (conversation) => {
try {
loading.value = true
// 根据消息类型获取详情数据
if (conversation.type === 'system') {
// 获取系统消息详情
const response = await getMessagesDetailAPI({ id: conversation.id })
if (response.code && response.data) {
// 更新conversation数据
selectedConversation.value = {
...conversation,
...response.data,
name: '系统通知',
lastMessage: response.data.note || conversation.lastMessage,
time: response.data.created_time_desc || conversation.time
}
} else {
selectedConversation.value = conversation
}
} else if (conversation.type === 'chat') {
// 获取聊天消息列表
const response = await getChatListAPI({
conversation_id: conversation.id,
page: 0,
limit: 50
})
if (response.code && response.data) {
// 更新conversation数据,包含聊天记录和接收者信息
selectedConversation.value = {
...conversation,
chatMessages: response.data.list || [],
receiver: response.data.receiver || {},
name: response.data.receiver?.nickname || conversation.name,
avatar: response.data.receiver?.avatar || conversation.avatar
}
} else {
selectedConversation.value = conversation
}
} else {
selectedConversation.value = conversation
}
// 显示弹框
showMessageDetail.value = true
// 标记为已读
markAsRead(conversation.id)
} catch (error) {
console.error('获取消息详情失败:', error)
Taro.showToast({
title: '获取详情失败',
icon: 'error'
})
// 即使失败也显示基本信息
selectedConversation.value = conversation
showMessageDetail.value = true
markAsRead(conversation.id)
} finally {
loading.value = false
}
}
......