|
|
@@ -1,7 +1,15 @@
|
|
|
/**
|
|
|
* 会话消息:拉取历史、加载更多、发文字、发文件
|
|
|
*/
|
|
|
-import { getMessages, sendMessage as apiSendMessage, uploadFile, getToken, getContentType, getUserIdFromToken } from '../utils/api'
|
|
|
+import {
|
|
|
+ getMessages,
|
|
|
+ sendMessage as apiSendMessage,
|
|
|
+ uploadFile,
|
|
|
+ getToken,
|
|
|
+ getContentType,
|
|
|
+ getUserIdFromToken,
|
|
|
+ normalizeMessageContentType
|
|
|
+} from '../utils/api'
|
|
|
import { chatStore } from '../store/chat'
|
|
|
|
|
|
/**
|
|
|
@@ -17,13 +25,17 @@ function normalizeMessage(m, currentUserId) {
|
|
|
const senderId = m.sender_id ?? m.senderId
|
|
|
isMe = String(senderId) === String(currentUserId)
|
|
|
}
|
|
|
+ const rawText = m.content ?? m.text ?? ''
|
|
|
+ const urlField = m.url ?? m.file_url ?? m.content_url
|
|
|
+ const content =
|
|
|
+ urlField && /^https?:\/\//i.test(String(urlField)) ? String(urlField) : String(rawText)
|
|
|
return {
|
|
|
id: String(m.id),
|
|
|
type,
|
|
|
senderId: m.sender_id ?? m.senderId,
|
|
|
receiverId: m.receiver_id ?? m.receiverId,
|
|
|
- content: m.content ?? m.text ?? '',
|
|
|
- contentType: m.content_type ?? m.contentType ?? 'TEXT',
|
|
|
+ content,
|
|
|
+ contentType: normalizeMessageContentType(m.content_type ?? m.contentType ?? 'TEXT'),
|
|
|
title: m.title,
|
|
|
createdAt: m.created_at ?? m.createdAt,
|
|
|
isMe: !!isMe,
|
|
|
@@ -150,16 +162,96 @@ export function useMessages() {
|
|
|
uni.showToast({ title: '请先登录', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
+ const cid = String(contactId)
|
|
|
+ const apiCt = normalizeMessageContentType(contentType)
|
|
|
+ const displayTitle =
|
|
|
+ (fileName && String(fileName).trim()) ||
|
|
|
+ (apiCt === 'IMAGE' ? '图片' : apiCt === 'VIDEO' ? '视频' : '文件')
|
|
|
+ const tempId = 'temp-' + Date.now()
|
|
|
+ const tempMsg = {
|
|
|
+ id: tempId,
|
|
|
+ tempId,
|
|
|
+ senderId: null,
|
|
|
+ content: filePath,
|
|
|
+ contentType: apiCt,
|
|
|
+ title: displayTitle,
|
|
|
+ createdAt: new Date().toISOString(),
|
|
|
+ isMe: true,
|
|
|
+ status: 'sending',
|
|
|
+ localFilePath: filePath,
|
|
|
+ uploadProgress: 0
|
|
|
+ }
|
|
|
+ chatStore.appendMessage(cid, tempMsg)
|
|
|
try {
|
|
|
- const uploadResult = await uploadFile(token, filePath, fileName, onProgress)
|
|
|
+ const uploadResult = await uploadFile(token, filePath, fileName, (p) => {
|
|
|
+ if (typeof p === 'number' && p >= 0 && p <= 1) {
|
|
|
+ chatStore.updateMessage(cid, tempId, { uploadProgress: p })
|
|
|
+ }
|
|
|
+ if (typeof onProgress === 'function') onProgress(p)
|
|
|
+ })
|
|
|
const key = uploadResult.key || uploadResult.file_key
|
|
|
- const name = fileName || uploadResult.filename
|
|
|
+ const name = (fileName && String(fileName).trim()) || uploadResult.filename || displayTitle
|
|
|
if (!key) throw new Error('上传未返回 key')
|
|
|
- const res = await apiSendMessage(token, contactId, key, contentType, name)
|
|
|
- const serverMsg = normalizeMessage(res, getUserIdFromToken(token))
|
|
|
- if (serverMsg) chatStore.appendMessage(contactId, serverMsg)
|
|
|
+ const res = await apiSendMessage(token, cid, key, apiCt, name)
|
|
|
+ const raw = (res && (res.data ?? res.message ?? res)) || res
|
|
|
+ const currentUserId = getUserIdFromToken(token)
|
|
|
+ const serverMsg = normalizeMessage(typeof raw === 'object' && raw !== null ? raw : null, currentUserId)
|
|
|
+ const hasValidId = serverMsg && serverMsg.id && String(serverMsg.id) !== 'undefined'
|
|
|
+ if (hasValidId) {
|
|
|
+ const finalMsg =
|
|
|
+ serverMsg.content != null && String(serverMsg.content) !== ''
|
|
|
+ ? serverMsg
|
|
|
+ : { ...serverMsg, content: key, contentType: apiCt, title: name }
|
|
|
+ chatStore.replaceTempMessage(cid, tempId, finalMsg)
|
|
|
+ } else {
|
|
|
+ chatStore.updateMessage(cid, tempId, { status: 'sent', uploadProgress: undefined })
|
|
|
+ }
|
|
|
} catch (e) {
|
|
|
- uni.showToast({ title: e.message || '发送失败', icon: 'none' })
|
|
|
+ chatStore.updateMessage(cid, tempId, { status: 'failed', uploadProgress: undefined })
|
|
|
+ const errMsg = (e && (e.message || e.errMsg)) || '发送失败'
|
|
|
+ uni.showToast({ title: errMsg, icon: 'none', duration: 3000 })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async function retrySendFileMessage(contactId, msg) {
|
|
|
+ if (!msg || !msg.tempId || msg.status !== 'failed' || !msg.localFilePath) return
|
|
|
+ const token = getToken()
|
|
|
+ if (!token) {
|
|
|
+ uni.showToast({ title: '请先登录', icon: 'none' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const cid = String(contactId)
|
|
|
+ const tempId = msg.tempId
|
|
|
+ const apiCt = normalizeMessageContentType(msg.contentType)
|
|
|
+ const displayTitle = (msg.title && String(msg.title).trim()) || (apiCt === 'IMAGE' ? '图片' : apiCt === 'VIDEO' ? '视频' : '文件')
|
|
|
+ chatStore.updateMessage(cid, tempId, { status: 'sending', uploadProgress: 0 })
|
|
|
+ try {
|
|
|
+ const uploadResult = await uploadFile(token, msg.localFilePath, undefined, (p) => {
|
|
|
+ if (typeof p === 'number' && p >= 0 && p <= 1) {
|
|
|
+ chatStore.updateMessage(cid, tempId, { uploadProgress: p })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ const key = uploadResult.key || uploadResult.file_key
|
|
|
+ const name = uploadResult.filename || displayTitle
|
|
|
+ if (!key) throw new Error('上传未返回 key')
|
|
|
+ const res = await apiSendMessage(token, cid, key, apiCt, name)
|
|
|
+ const raw = (res && (res.data ?? res.message ?? res)) || res
|
|
|
+ const currentUserId = getUserIdFromToken(token)
|
|
|
+ const serverMsg = normalizeMessage(typeof raw === 'object' && raw !== null ? raw : null, currentUserId)
|
|
|
+ const hasValidId = serverMsg && serverMsg.id && String(serverMsg.id) !== 'undefined'
|
|
|
+ if (hasValidId) {
|
|
|
+ const finalMsg =
|
|
|
+ serverMsg.content != null && String(serverMsg.content) !== ''
|
|
|
+ ? serverMsg
|
|
|
+ : { ...serverMsg, content: key, contentType: apiCt, title: name }
|
|
|
+ chatStore.replaceTempMessage(cid, tempId, finalMsg)
|
|
|
+ } else {
|
|
|
+ chatStore.updateMessage(cid, tempId, { status: 'sent', uploadProgress: undefined })
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ chatStore.updateMessage(cid, tempId, { status: 'failed', uploadProgress: undefined })
|
|
|
+ const errMsg = (e && (e.message || e.errMsg)) || '发送失败'
|
|
|
+ uni.showToast({ title: errMsg, icon: 'none', duration: 3000 })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -224,6 +316,7 @@ export function useMessages() {
|
|
|
sendMessage,
|
|
|
retrySendMessage,
|
|
|
sendFileMessage,
|
|
|
+ retrySendFileMessage,
|
|
|
remindUserNotification,
|
|
|
hasContactLoaded: (id) => chatStore.hasContactLoaded(id),
|
|
|
getContentType
|