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,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
......