hookehuyr

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

- 在点击对话时根据类型获取系统消息详情或聊天记录
- 重构MessageDetail组件以显示真实数据而非模拟数据
- 添加消息发送功能并与API集成
- 完善系统消息的显示样式和元信息
...@@ -32,7 +32,15 @@ ...@@ -32,7 +32,15 @@
32 <!-- 系统通知样式 --> 32 <!-- 系统通知样式 -->
33 <view v-if="conversation?.type === 'system'" class="system-content"> 33 <view v-if="conversation?.type === 'system'" class="system-content">
34 <view class="message-content"> 34 <view class="message-content">
35 - <text class="text-base">{{ conversation?.lastMessage }}</text> 35 + <view class="message-title" v-if="conversation?.title">
36 + <text class="text-lg font-medium">{{ conversation.title }}</text>
37 + </view>
38 + <view class="message-body">
39 + <text class="text-base">{{ conversation?.note || conversation?.lastMessage }}</text>
40 + </view>
41 + <view class="message-meta" v-if="conversation?.created_time_desc">
42 + <text class="text-sm text-gray-500">发布时间:{{ conversation.created_time_desc }}</text>
43 + </view>
36 </view> 44 </view>
37 </view> 45 </view>
38 46
...@@ -107,6 +115,8 @@ ...@@ -107,6 +115,8 @@
107 <script setup> 115 <script setup>
108 import { ref, computed, watch, nextTick } from 'vue' 116 import { ref, computed, watch, nextTick } from 'vue'
109 import Taro from '@tarojs/taro' 117 import Taro from '@tarojs/taro'
118 +import { sendChatAPI } from '@/api/chat'
119 +import { useUserStore } from '@/stores/user'
110 120
111 /** 121 /**
112 * 消息详情组件 Props 122 * 消息详情组件 Props
...@@ -149,46 +159,33 @@ const messages = ref([]) ...@@ -149,46 +159,33 @@ const messages = ref([])
149 * 初始化聊天消息 159 * 初始化聊天消息
150 */ 160 */
151 const initChatMessages = () => { 161 const initChatMessages = () => {
162 + const userStore = useUserStore()
163 + const currentUserId = userStore.userInfo?.id
164 +
152 if (props.conversation?.type === 'chat') { 165 if (props.conversation?.type === 'chat') {
153 - // 模拟历史消息 166 + // 使用从API获取的真实聊天记录
167 + if (props.conversation.chatMessages && props.conversation.chatMessages.length > 0) {
168 + // 转换API数据格式为组件需要的格式
169 + messages.value = props.conversation.chatMessages.map(msg => ({
170 + type: msg.created_by === currentUserId ? 'sent' : 'received', // 根据创建者ID判断
171 + content: msg.note || '',
172 + time: msg.created_time_desc || '',
173 + id: msg.id
174 + }))
175 + } else {
176 + // 如果没有历史消息,显示欢迎消息
154 messages.value = [ 177 messages.value = [
155 { 178 {
156 type: 'received', 179 type: 'received',
157 - content: props.conversation.lastMessage || '您好,有什么可以帮助您的吗?', 180 + content: '您好,有什么可以帮助您的吗?',
158 - time: '10:30' 181 + time: new Date().toLocaleTimeString('zh-CN', {
159 - }, 182 + hour: '2-digit',
160 - { 183 + minute: '2-digit'
161 - type: 'sent', 184 + })
162 - content: '我想咨询一下车辆的相关问题', 185 + }
163 - time: '10:32'
164 - },
165 - {
166 - type: 'received',
167 - content: '好的,请问您具体想了解哪方面的信息呢?我会尽力为您解答。',
168 - time: '10:33'
169 - },
170 - {
171 - type: 'sent',
172 - content: '我想咨询一下车辆的相关问题',
173 - time: '10:34'
174 - },
175 - {
176 - type: 'received',
177 - content: '好的,请问您具体想了解哪方面的信息呢?我会尽力为您解答。',
178 - time: '10:35'
179 - },
180 - {
181 - type: 'sent',
182 - content: '我想咨询一下车辆的相关问题',
183 - time: '10:36'
184 - },
185 - {
186 - type: 'received',
187 - content: '好的,请问您具体想了解哪方面的信息呢?我会尽力为您解答。',
188 - time: '10:37'
189 - },
190 ] 186 ]
191 } 187 }
188 + }
192 } 189 }
193 190
194 /** 191 /**
...@@ -203,46 +200,75 @@ const sendMessage = async () => { ...@@ -203,46 +200,75 @@ const sendMessage = async () => {
203 return 200 return
204 } 201 }
205 202
203 + // 只有聊天类型才能发送消息
204 + if (props.conversation?.type !== 'chat') {
205 + Taro.showToast({
206 + title: '系统消息无法回复',
207 + icon: 'none'
208 + })
209 + return
210 + }
211 +
212 + const messageContent = inputMessage.value.trim()
213 +
214 + try {
215 + // 先添加到本地显示
206 const newMessage = { 216 const newMessage = {
207 type: 'sent', 217 type: 'sent',
208 - content: inputMessage.value.trim(), 218 + content: messageContent,
209 time: new Date().toLocaleTimeString('zh-CN', { 219 time: new Date().toLocaleTimeString('zh-CN', {
210 hour: '2-digit', 220 hour: '2-digit',
211 minute: '2-digit' 221 minute: '2-digit'
212 }) 222 })
213 } 223 }
214 -
215 messages.value.push(newMessage) 224 messages.value.push(newMessage)
216 225
217 // 清空输入框 226 // 清空输入框
218 - const messageContent = inputMessage.value
219 inputMessage.value = '' 227 inputMessage.value = ''
220 228
221 // 滚动到底部 229 // 滚动到底部
222 await nextTick() 230 await nextTick()
223 scrollToBottom() 231 scrollToBottom()
224 232
225 - // 触发发送消息事件 233 + // 调用API发送消息
234 + const response = await sendChatAPI({
235 + conversation_id: props.conversation.id,
236 + note: messageContent
237 + })
238 +
239 + if (response.code) {
240 + // 发送成功,更新消息ID
241 + if (response.data && response.data.id) {
242 + newMessage.id = response.data.id
243 + }
244 +
245 + // 触发发送消息事件,通知父组件更新列表
226 emit('sendMessage', { 246 emit('sendMessage', {
227 conversation: props.conversation, 247 conversation: props.conversation,
228 message: messageContent 248 message: messageContent
229 }) 249 })
230 250
231 - // 模拟对方回复 251 + Taro.showToast({
232 - setTimeout(() => { 252 + title: '发送成功',
233 - const replyMessage = { 253 + icon: 'success'
234 - type: 'received', 254 + })
235 - content: '收到您的消息,我们会尽快处理并回复您。', 255 + } else {
236 - time: new Date().toLocaleTimeString('zh-CN', { 256 + // 发送失败,移除本地消息
237 - hour: '2-digit', 257 + messages.value.pop()
238 - minute: '2-digit' 258 + Taro.showToast({
259 + title: response.msg || '发送失败',
260 + icon: 'error'
239 }) 261 })
240 } 262 }
241 - messages.value.push(replyMessage) 263 + } catch (error) {
242 - nextTick(() => { 264 + console.error('发送消息失败:', error)
243 - scrollToBottom() 265 + // 发送失败,移除本地消息
266 + messages.value.pop()
267 + Taro.showToast({
268 + title: '发送失败',
269 + icon: 'error'
244 }) 270 })
245 - }, 1000) 271 + }
246 } 272 }
247 273
248 /** 274 /**
...@@ -350,6 +376,22 @@ watch(visible, (newVisible) => { ...@@ -350,6 +376,22 @@ watch(visible, (newVisible) => {
350 border-radius: 16rpx; 376 border-radius: 16rpx;
351 margin-bottom: 24rpx; 377 margin-bottom: 24rpx;
352 box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); 378 box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
379 +
380 + .message-title {
381 + margin-bottom: 16rpx;
382 + padding-bottom: 12rpx;
383 + border-bottom: 1rpx solid #f0f0f0;
384 + }
385 +
386 + .message-body {
387 + line-height: 1.6;
388 + margin-bottom: 16rpx;
389 + }
390 +
391 + .message-meta {
392 + padding-top: 12rpx;
393 + border-top: 1rpx solid #f0f0f0;
394 + }
353 } 395 }
354 } 396 }
355 397
......
...@@ -103,7 +103,7 @@ import MessageDetail from '@/components/MessageDetail.vue' ...@@ -103,7 +103,7 @@ import MessageDetail from '@/components/MessageDetail.vue'
103 import { $ } from '@tarojs/extend' 103 import { $ } from '@tarojs/extend'
104 import Taro from '@tarojs/taro' 104 import Taro from '@tarojs/taro'
105 // 导入接口 105 // 导入接口
106 -import { getMessagesListAPI } from '@/api/chat' 106 +import { getMessagesListAPI, getMessagesDetailAPI, getChatListAPI } from '@/api/chat'
107 107
108 // 默认头像 108 // 默认头像
109 const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg' 109 const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'
...@@ -332,14 +332,67 @@ const loadMore = async () => { ...@@ -332,14 +332,67 @@ const loadMore = async () => {
332 332
333 333
334 // 点击对话 - 显示消息详情弹框 334 // 点击对话 - 显示消息详情弹框
335 -const onConversationClick = (conversation) => { 335 +const onConversationClick = async (conversation) => {
336 + try {
337 + loading.value = true
338 +
339 + // 根据消息类型获取详情数据
340 + if (conversation.type === 'system') {
341 + // 获取系统消息详情
342 + const response = await getMessagesDetailAPI({ id: conversation.id })
343 + if (response.code && response.data) {
344 + // 更新conversation数据
345 + selectedConversation.value = {
346 + ...conversation,
347 + ...response.data,
348 + name: '系统通知',
349 + lastMessage: response.data.note || conversation.lastMessage,
350 + time: response.data.created_time_desc || conversation.time
351 + }
352 + } else {
336 selectedConversation.value = conversation 353 selectedConversation.value = conversation
354 + }
355 + } else if (conversation.type === 'chat') {
356 + // 获取聊天消息列表
357 + const response = await getChatListAPI({
358 + conversation_id: conversation.id,
359 + page: 0,
360 + limit: 50
361 + })
362 + if (response.code && response.data) {
363 + // 更新conversation数据,包含聊天记录和接收者信息
364 + selectedConversation.value = {
365 + ...conversation,
366 + chatMessages: response.data.list || [],
367 + receiver: response.data.receiver || {},
368 + name: response.data.receiver?.nickname || conversation.name,
369 + avatar: response.data.receiver?.avatar || conversation.avatar
370 + }
371 + } else {
372 + selectedConversation.value = conversation
373 + }
374 + } else {
375 + selectedConversation.value = conversation
376 + }
337 377
338 // 显示弹框 378 // 显示弹框
339 showMessageDetail.value = true 379 showMessageDetail.value = true
340 380
341 // 标记为已读 381 // 标记为已读
342 markAsRead(conversation.id) 382 markAsRead(conversation.id)
383 + } catch (error) {
384 + console.error('获取消息详情失败:', error)
385 + Taro.showToast({
386 + title: '获取详情失败',
387 + icon: 'error'
388 + })
389 + // 即使失败也显示基本信息
390 + selectedConversation.value = conversation
391 + showMessageDetail.value = true
392 + markAsRead(conversation.id)
393 + } finally {
394 + loading.value = false
395 + }
343 } 396 }
344 397
345 398
......