feat(消息): 实现消息详情和聊天功能的API集成
- 在点击对话时根据类型获取系统消息详情或聊天记录 - 重构MessageDetail组件以显示真实数据而非模拟数据 - 添加消息发送功能并与API集成 - 完善系统消息的显示样式和元信息
Showing
2 changed files
with
172 additions
and
77 deletions
| ... | @@ -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,45 +159,32 @@ const messages = ref([]) | ... | @@ -149,45 +159,32 @@ 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获取的真实聊天记录 |
| 154 | - messages.value = [ | 167 | + if (props.conversation.chatMessages && props.conversation.chatMessages.length > 0) { |
| 155 | - { | 168 | + // 转换API数据格式为组件需要的格式 |
| 156 | - type: 'received', | 169 | + messages.value = props.conversation.chatMessages.map(msg => ({ |
| 157 | - content: props.conversation.lastMessage || '您好,有什么可以帮助您的吗?', | 170 | + type: msg.created_by === currentUserId ? 'sent' : 'received', // 根据创建者ID判断 |
| 158 | - time: '10:30' | 171 | + content: msg.note || '', |
| 159 | - }, | 172 | + time: msg.created_time_desc || '', |
| 160 | - { | 173 | + id: msg.id |
| 161 | - type: 'sent', | 174 | + })) |
| 162 | - content: '我想咨询一下车辆的相关问题', | 175 | + } else { |
| 163 | - time: '10:32' | 176 | + // 如果没有历史消息,显示欢迎消息 |
| 164 | - }, | 177 | + messages.value = [ |
| 165 | - { | 178 | + { |
| 166 | - type: 'received', | 179 | + type: 'received', |
| 167 | - content: '好的,请问您具体想了解哪方面的信息呢?我会尽力为您解答。', | 180 | + content: '您好,有什么可以帮助您的吗?', |
| 168 | - time: '10:33' | 181 | + time: new Date().toLocaleTimeString('zh-CN', { |
| 169 | - }, | 182 | + hour: '2-digit', |
| 170 | - { | 183 | + minute: '2-digit' |
| 171 | - type: 'sent', | 184 | + }) |
| 172 | - content: '我想咨询一下车辆的相关问题', | 185 | + } |
| 173 | - time: '10:34' | 186 | + ] |
| 174 | - }, | 187 | + } |
| 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 | - ] | ||
| 191 | } | 188 | } |
| 192 | } | 189 | } |
| 193 | 190 | ||
| ... | @@ -203,46 +200,75 @@ const sendMessage = async () => { | ... | @@ -203,46 +200,75 @@ const sendMessage = async () => { |
| 203 | return | 200 | return |
| 204 | } | 201 | } |
| 205 | 202 | ||
| 206 | - const newMessage = { | 203 | + // 只有聊天类型才能发送消息 |
| 207 | - type: 'sent', | 204 | + if (props.conversation?.type !== 'chat') { |
| 208 | - content: inputMessage.value.trim(), | 205 | + Taro.showToast({ |
| 209 | - time: new Date().toLocaleTimeString('zh-CN', { | 206 | + title: '系统消息无法回复', |
| 210 | - hour: '2-digit', | 207 | + icon: 'none' |
| 211 | - minute: '2-digit' | ||
| 212 | }) | 208 | }) |
| 209 | + return | ||
| 213 | } | 210 | } |
| 214 | 211 | ||
| 215 | - messages.value.push(newMessage) | 212 | + const messageContent = inputMessage.value.trim() |
| 216 | - | 213 | + |
| 217 | - // 清空输入框 | 214 | + try { |
| 218 | - const messageContent = inputMessage.value | 215 | + // 先添加到本地显示 |
| 219 | - inputMessage.value = '' | 216 | + const newMessage = { |
| 220 | - | 217 | + type: 'sent', |
| 221 | - // 滚动到底部 | 218 | + content: messageContent, |
| 222 | - await nextTick() | ||
| 223 | - scrollToBottom() | ||
| 224 | - | ||
| 225 | - // 触发发送消息事件 | ||
| 226 | - emit('sendMessage', { | ||
| 227 | - conversation: props.conversation, | ||
| 228 | - message: messageContent | ||
| 229 | - }) | ||
| 230 | - | ||
| 231 | - // 模拟对方回复 | ||
| 232 | - setTimeout(() => { | ||
| 233 | - const replyMessage = { | ||
| 234 | - type: 'received', | ||
| 235 | - content: '收到您的消息,我们会尽快处理并回复您。', | ||
| 236 | time: new Date().toLocaleTimeString('zh-CN', { | 219 | time: new Date().toLocaleTimeString('zh-CN', { |
| 237 | hour: '2-digit', | 220 | hour: '2-digit', |
| 238 | minute: '2-digit' | 221 | minute: '2-digit' |
| 239 | }) | 222 | }) |
| 240 | } | 223 | } |
| 241 | - messages.value.push(replyMessage) | 224 | + messages.value.push(newMessage) |
| 242 | - nextTick(() => { | 225 | + |
| 243 | - scrollToBottom() | 226 | + // 清空输入框 |
| 227 | + inputMessage.value = '' | ||
| 228 | + | ||
| 229 | + // 滚动到底部 | ||
| 230 | + await nextTick() | ||
| 231 | + scrollToBottom() | ||
| 232 | + | ||
| 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 | + // 触发发送消息事件,通知父组件更新列表 | ||
| 246 | + emit('sendMessage', { | ||
| 247 | + conversation: props.conversation, | ||
| 248 | + message: messageContent | ||
| 249 | + }) | ||
| 250 | + | ||
| 251 | + Taro.showToast({ | ||
| 252 | + title: '发送成功', | ||
| 253 | + icon: 'success' | ||
| 254 | + }) | ||
| 255 | + } else { | ||
| 256 | + // 发送失败,移除本地消息 | ||
| 257 | + messages.value.pop() | ||
| 258 | + Taro.showToast({ | ||
| 259 | + title: response.msg || '发送失败', | ||
| 260 | + icon: 'error' | ||
| 261 | + }) | ||
| 262 | + } | ||
| 263 | + } catch (error) { | ||
| 264 | + console.error('发送消息失败:', error) | ||
| 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 | - selectedConversation.value = 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 { | ||
| 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 | ... | ... |
-
Please register or login to post a comment