fix: 解决Im 图片相关问题

This commit is contained in:
wangdongbo 2026-01-23 11:00:00 +08:00
parent 46ce29f46e
commit 4af491e6ac
5 changed files with 129 additions and 276 deletions

View File

@ -1,4 +1,5 @@
MP_API_BASE_URL=http://localhost:8080
MP_IMAGE_URL=https://patient.youcan365.com
MP_CACHE_PREFIX=development
MP_WX_APP_ID=wx93af55767423938e
MP_CORP_ID=wwe3fb2faa52cf9dfb

View File

@ -53,7 +53,7 @@
</template>
<script setup>
import { computed, ref, onMounted, onUnmounted, nextTick } from 'vue';
import { computed, ref, onMounted, onUnmounted, nextTick } from "vue";
import {
chooseImage,
takePhoto as takePhotoUtil,
@ -64,19 +64,19 @@ import {
sendCustomMessage as sendCustomMessageUtil,
sendMessage as sendMessageUtil,
checkRecordingDuration,
validateBeforeSend
} from '@/utils/chat-utils.js';
validateBeforeSend,
} from "@/utils/chat-utils.js";
// Props
const props = defineProps({
timChatManager: { type: Object, required: true },
patientInfo: { type: Object, default: () => ({}) },
chatRoomBusiness: { type: Object, default: () => ({}) },
formatTime: { type: Function, required: true }
formatTime: { type: Function, required: true },
});
// Emits
const emit = defineEmits(['messageSent', 'scrollToBottom']);
const emit = defineEmits(["messageSent", "scrollToBottom"]);
//
const inputText = ref("");
@ -85,9 +85,12 @@ const showMorePanel = ref(false);
const isRecording = ref(false);
const recordingText = ref("录音中...");
const cloudCustomData = computed(() => {
const arr = [props.chatRoomBusiness.businessType, props.chatRoomBusiness.businessId];
return arr.filter(Boolean).join('|');
})
const arr = [
props.chatRoomBusiness.businessType,
props.chatRoomBusiness.businessId,
];
return arr.filter(Boolean).join("|");
});
// +
const recordingDuration = ref(0);
@ -112,10 +115,12 @@ const initRecorderManager = () => {
}
//
if (!checkRecordingDuration(res, () => {
if (
!checkRecordingDuration(res, () => {
isRecording.value = false;
recordingText.value = "录音中...";
})) {
})
) {
return;
}
@ -141,19 +146,19 @@ const initRecorderManager = () => {
const sendTextMessage = async () => {
if (!inputText.value.trim()) return;
await sendMessage('text', inputText.value);
await sendMessage("text", inputText.value);
inputText.value = "";
};
//
const sendImageMessage = async (imageFile) => {
console.log('chat-input sendImageMessage 被调用,参数:', imageFile);
await sendMessage('image', imageFile);
console.log("chat-input sendImageMessage 被调用,参数:", imageFile);
await sendMessage("image", imageFile);
};
//
const sendVoiceMessage = async (voiceFile, duration) => {
await sendMessage('voice', { file: voiceFile, duration });
await sendMessage("voice", { file: voiceFile, duration });
};
//
@ -166,7 +171,7 @@ const sendMessage = async (messageType, data) => {
() => {
showMorePanel.value = false;
//
emit('messageSent');
emit("messageSent");
},
cloudCustomData.value
);
@ -180,7 +185,7 @@ const sendCustomMessage = async (messageData) => {
() => validateBeforeSend(false, false, props.timChatManager),
() => {
showMorePanel.value = false;
emit('messageSent');
emit("messageSent");
}
);
};
@ -200,17 +205,22 @@ const toggleMorePanel = () => {
const showImagePicker = () => {
chooseImage(
(file) => {
console.log('选择图片成功,文件对象:', file);
console.log("选择图片成功,文件对象:", file);
//
sendImageMessage(file);
},
(err) => {
console.error('选择图片失败:', err);
if (!err.errMsg?.includes('permission') && !err.errMsg?.includes('auth') && !err.errMsg?.includes('拒绝') && !err.errMsg?.includes('未授权')) {
console.error("选择图片失败:", err);
if (
!err.errMsg?.includes("permission") &&
!err.errMsg?.includes("auth") &&
!err.errMsg?.includes("拒绝") &&
!err.errMsg?.includes("未授权")
) {
uni.showToast({
title: '选择图片失败,请重试',
icon: 'none',
duration: 2000
title: "选择图片失败,请重试",
icon: "none",
duration: 2000,
});
}
}
@ -220,17 +230,22 @@ const showImagePicker = () => {
const takePhoto = () => {
takePhotoUtil(
(file) => {
console.log('拍照成功,文件对象:', file);
console.log("拍照成功,文件对象:", file);
//
sendImageMessage(file);
},
(err) => {
console.error('拍照失败:', err);
if (!err.errMsg?.includes('permission') && !err.errMsg?.includes('auth') && !err.errMsg?.includes('拒绝') && !err.errMsg?.includes('未授权')) {
console.error("拍照失败:", err);
if (
!err.errMsg?.includes("permission") &&
!err.errMsg?.includes("auth") &&
!err.errMsg?.includes("拒绝") &&
!err.errMsg?.includes("未授权")
) {
uni.showToast({
title: '拍照失败,请重试',
icon: 'none',
duration: 2000
title: "拍照失败,请重试",
icon: "none",
duration: 2000,
});
}
}
@ -303,12 +318,11 @@ const cancelRecord = () => {
stopRecordUtil(recorderManager);
};
//
const sendSurveyMessage = async () => {
const surveyMessage = createCustomMessage("survey", {
const surveyMessage = createCustomMessage(
"survey",
{
content: "医生发送了问卷调查",
surveyTitle: "治疗效果评估",
surveyDescription: "您好,为了帮助了解您的病情变化,请您如实填写问卷。",
@ -316,15 +330,17 @@ const sendSurveyMessage = async () => {
estimatedTime: "约3-5分钟",
reward: "积分奖励10分",
note: "问卷内容涉及您的症状变化、用药情况等,请根据实际情况填写。",
}, props.formatTime);
},
props.formatTime
);
await sendCustomMessage(surveyMessage);
};
//
const morePanelButtons = [
{ text: '照片', icon: '/static/home/photo.png', action: showImagePicker },
{ text: '拍摄', icon: '/static/home/video.png', action: takePhoto },
{ text: "照片", icon: "/static/home/photo.png", action: showImagePicker },
{ text: "拍摄", icon: "/static/home/video.png", action: takePhoto },
// { text: '', icon: '/static/home/avatar.svg', action: sendSymptomMessage },
// { text: '', icon: '/static/home/avatar.svg', action: sendPrescriptionMessage },
// { text: '', icon: '/static/home/avatar.svg', action: sendRefillMessage },
@ -332,10 +348,10 @@ const morePanelButtons = [
];
function handleInputFocus() {
console.log('handleInputFocus')
console.log("handleInputFocus");
nextTick().then(() => {
emit('scrollToBottom')
})
emit("scrollToBottom");
});
}
onMounted(() => {
@ -343,14 +359,14 @@ onMounted(() => {
initRecorderManager();
//
uni.$on('closeMorePanel', () => {
uni.$on("closeMorePanel", () => {
showMorePanel.value = false;
});
});
onUnmounted(() => {
//
uni.$off('closeMorePanel');
uni.$off("closeMorePanel");
clearDurationTimer();
});
</script>

View File

@ -3,6 +3,7 @@
<text v-if="message.type === 'TIMTextElem'" class="message-text">
{{ message.payload.text }}
</text>
<!-- 图片消息 -->
<image
v-else-if="message.type === 'TIMImageElem'"
@ -61,7 +62,7 @@
</view>
</view>
<!-- 自定义消息卡片 -->
<!-- <template v-else-if="message.type === 'TIMCustomElem'">
<view
class="card-avatar-row"
@ -102,6 +103,8 @@ const isPlaying = computed(() => {
//
const getImageStyle = (imageInfo) => {
// 使
imageInfo.width = imageInfo.width || imageInfo.Width;
imageInfo.height = imageInfo.height || imageInfo.Height;
if (!imageInfo || !imageInfo.width || !imageInfo.height) {
return {
width: "400rpx",

View File

@ -25,10 +25,10 @@ export const checkConsultationStatus = (waitingForDoctor, consultationEnded) =>
return true;
};
//
// 检查IM连接状态
export const checkIMConnection = (timChatManager) => {
if (!timChatManager.tim || !timChatManager.isLoggedIn) {
// showMessage("IM连接异常请重新进入");
return false;
}
return true;
@ -346,133 +346,31 @@ export const chooseMedia = async (options, onSuccess, onFail) => {
};
/**
* 选择图片针对 TIM SDK 优化
* 选择图片
* @param {function} onSuccess - 成功回调
* @param {function} onFail - 失败回调
*/
export const chooseImage = async (onSuccess, onFail) => {
// 检查权限
const sourceType = ['album', 'camera'];
if (sourceType.includes('album')) {
const hasPermission = await checkAlbumPermission();
if (!hasPermission) {
console.log('用户未授予相册权限');
if (onFail) {
onFail({ errMsg: '未授权相册权限' });
}
return;
}
}
// 使用 wx.chooseImage 以确保与 TIM SDK 兼容
// #ifdef MP-WEIXIN
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: sourceType,
success: function (res) {
console.log('wx.chooseImage 成功,完整返回数据:', JSON.stringify(res));
console.log('tempFilePaths:', res.tempFilePaths);
console.log('tempFiles:', res.tempFiles);
// TIM SDK 需要完整的 wx.chooseImage 返回对象,而不是单个文件
// 直接传递整个 res 对象
if (onSuccess) onSuccess(res);
},
fail: function (err) {
// 用户取消选择
if (err.errMsg && err.errMsg.includes('cancel')) {
console.log('用户取消选择');
return;
}
// 权限相关错误
if (err.errMsg && (err.errMsg.includes('permission') || err.errMsg.includes('auth') || err.errMsg.includes('拒绝'))) {
console.error('相册权限被拒绝:', err);
uni.showModal({
title: '需要相册权限',
content: '请在设置中开启相册权限后重试',
confirmText: '去设置',
success: (modalRes) => {
if (modalRes.confirm) {
uni.openSetting();
}
}
});
if (onFail) {
onFail(err);
}
return;
}
// 其他错误
console.error('选择图片失败:', err);
if (onFail) {
onFail(err);
} else {
showMessage('选择图片失败,请重试');
}
}
});
// #endif
// #ifndef MP-WEIXIN
// 非微信小程序环境,使用 uni.chooseMedia
export const chooseImage = (onSuccess, onFail) => {
chooseMedia({
count: 1,
mediaType: ['image'],
sizeType: ['original', 'compressed'],
sourceType: sourceType
sourceType: ['album', 'camera']
}, onSuccess, onFail);
// #endif
};
/**
* 拍照针对 TIM SDK 优化
* 拍照
* @param {function} onSuccess - 成功回调
* @param {function} onFail - 失败回调
*/
export const takePhoto = (onSuccess, onFail) => {
// 使用 wx.chooseImage 以确保与 TIM SDK 兼容
// #ifdef MP-WEIXIN
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['camera'],
success: function (res) {
console.log('wx.chooseImage (拍照) 成功,完整返回数据:', JSON.stringify(res));
console.log('tempFilePaths:', res.tempFilePaths);
console.log('tempFiles:', res.tempFiles);
// TIM SDK 需要完整的 wx.chooseImage 返回对象
if (onSuccess) onSuccess(res);
},
fail: function (err) {
// 用户取消
if (err.errMsg && err.errMsg.includes('cancel')) {
console.log('用户取消拍照');
return;
}
console.error('拍照失败:', err);
if (onFail) {
onFail(err);
} else {
showMessage('拍照失败,请重试');
}
}
});
// #endif
// #ifndef MP-WEIXIN
// 非微信小程序环境
chooseMedia({
count: 1,
mediaType: ['image'],
sizeType: ['original', 'compressed'],
sourceType: ['camera']
}, onSuccess, onFail);
// #endif
};
// ==================== 录音相关工具方法 ====================
@ -588,8 +486,6 @@ export const sendCustomMessage = async (messageData, timChatManager, validateBef
* @param {function} onSuccess - 成功回调
*/
export const sendMessage = async (messageType, data, timChatManager, validateBeforeSend, onSuccess, cloudCustomData) => {
console.log('chat-utils sendMessage 被调用:', { messageType, data });
if (!validateBeforeSend()) {
return;
}
@ -601,9 +497,7 @@ export const sendMessage = async (messageType, data, timChatManager, validateBef
result = await timChatManager.sendTextMessage(data, cloudCustomData);
break;
case 'image':
console.log('准备发送图片消息,数据:', data);
result = await timChatManager.sendImageMessage(data, cloudCustomData);
console.log('图片消息发送结果:', result);
break;
case 'voice':
result = await timChatManager.sendVoiceMessage(data.file, data.duration,cloudCustomData);
@ -614,7 +508,6 @@ export const sendMessage = async (messageType, data, timChatManager, validateBef
}
if (result && result.success) {
console.log('消息发送成功');
if (onSuccess) onSuccess();
} else {
console.error('发送消息失败:', result?.error);

View File

@ -2119,72 +2119,33 @@ class TimChatManager {
// 发送图片消息
async sendImageMessage(imageFile) {
console.log('sendImageMessage 被调用,参数:', imageFile);
if (!this.tim) {
this.triggerCallback('onError', 'IM未初始化')
return { success: false, error: 'IM未初始化' }
return
}
// 检查登录状态
if (!this.isLoggedIn) {
console.error('IM未登录无法发送消息');
this.triggerCallback('onError', 'IM未登录请稍后重试')
return { success: false, error: 'IM未登录' }
if (!this.conversation) {
this.triggerCallback('onError', '群聊会话不存在')
return { success: false, error: '群聊会话不存在' }
}
// 优先使用 currentConversationID如果没有则尝试从 conversation 获取
let conversationID = this.currentConversationID;
if (!conversationID && this.conversation) {
conversationID = this.conversation.conversationID;
}
if (!conversationID) {
console.error('会话ID不存在');
this.triggerCallback('onError', '会话不存在,请重新进入聊天')
return { success: false, error: '会话ID不存在' }
}
// 从 conversationID 提取 groupID
let groupID = null;
if (conversationID.startsWith('GROUP')) {
groupID = conversationID.replace('GROUP', '');
} else if (this.conversation?.groupProfile?.groupID) {
groupID = this.conversation.groupProfile.groupID;
let groupID = null
if (this.conversation.groupProfile && this.conversation.groupProfile.groupID) {
groupID = this.conversation.groupProfile.groupID
} else if (this.conversation.conversationID) {
groupID = this.conversation.conversationID.replace('GROUP', '')
}
if (!groupID) {
console.error('无法获取群聊IDconversationID:', conversationID);
this.triggerCallback('onError', '无法获取群聊ID')
return { success: false, error: '无法获取群聊ID' }
}
console.log('发送图片消息conversationID:', conversationID, 'groupID:', groupID);
// 确保使用当前会话的conversationID
const conversationID = this.conversation.conversationID || this.currentConversationID
// imageFile 现在是完整的 wx.chooseImage 返回对象
console.log('接收到的图片选择结果:', imageFile);
console.log('类型:', typeof imageFile);
console.log('keys:', imageFile ? Object.keys(imageFile) : 'null');
// 验证对象
if (!imageFile) {
console.error('图片选择结果为空');
this.triggerCallback('onError', '图片文件无效');
return { success: false, error: '图片选择结果为空' };
}
// 获取文件路径用于显示预览
let previewPath = '';
if (imageFile.tempFilePaths && imageFile.tempFilePaths.length > 0) {
previewPath = imageFile.tempFilePaths[0];
} else if (imageFile.tempFiles && imageFile.tempFiles.length > 0) {
previewPath = imageFile.tempFiles[0].tempFilePath || imageFile.tempFiles[0].path;
}
console.log('预览路径:', previewPath);
// 获取图片尺寸信息(用于本地预览)
const imageInfo = await this.getImageInfo(previewPath);
// 获取图片尺寸信息
const imageInfo = await this.getImageInfo(imageFile);
const localMessage = {
ID: `local_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
@ -2192,14 +2153,14 @@ class TimChatManager {
type: 'TIMImageElem',
payload: {
imageInfoArray: [{
url: previewPath,
url: this.getImageUrl(imageFile),
width: imageInfo.width,
height: imageInfo.height
}]
},
lastTime: Date.now(),
status: 'sending',
avatar: '/static/center/user-avatar.png',
avatar: '',
conversationID: conversationID,
from: this.currentUserID
}
@ -2211,45 +2172,22 @@ class TimChatManager {
// 触发消息接收回调让UI立即显示
this.triggerCallback('onMessageReceived', localMessage)
console.log('准备创建 TIM 图片消息groupID:', groupID, 'imageFile:', imageFile);
try {
// 创建图片消息 - 直接传递 wx.chooseImage 的完整返回对象
const message = this.tim.createImageMessage({
to: groupID,
conversationType: TIM.TYPES.CONV_GROUP,
payload: {
file: imageFile // 传递完整的 wx.chooseImage 返回对象
}
payload: { file: imageFile }
})
console.log('TIM 图片消息已创建:', message);
console.log('开始发送图片消息...');
const sendResult = await this.tim.sendMessage(message);
console.log('图片消息发送成功:', sendResult);
try {
await this.tim.sendMessage(message)
localMessage.status = 'success'
return { success: true, message: localMessage }
} catch (error) {
console.error('图片消息发送失败:', error)
console.error('错误详情:', {
message: error.message,
stack: error.stack,
imageFile: imageFile
});
localMessage.status = 'failed'
// 如果是因为未登录导致的失败,尝试重连
if (error.message && (error.message.includes('not login') || error.message.includes('sdk not ready'))) {
console.log('检测到未登录错误,尝试重连...');
this.isLoggedIn = false;
this.ensureIMConnection();
}
return { success: false, error }
}
}
// 发送语音消息
async sendVoiceMessage(voiceFile, duration) {
if (!this.tim) {
@ -2497,40 +2435,42 @@ class TimChatManager {
}
getImageUrl(imageFile) {
// 支持 tempFilePath 或 path
if (imageFile?.tempFilePath) {
return imageFile.tempFilePath;
// 处理 tempFiles 数组格式
if (imageFile?.tempFiles?.length > 0) {
return imageFile.tempFiles[0].tempFilePath
}
if (imageFile?.path) {
return imageFile.path;
// 处理单个文件对象
if (imageFile?.tempFilePath) {
return imageFile.tempFilePath
}
// 处理字符串路径
if (typeof imageFile === 'string') {
return imageFile;
return imageFile
}
console.warn('无法获取图片URL使用默认图片:', imageFile);
return '/static/home/photo.png';
return '/static/home/photo.png'
}
// 获取图片尺寸信息
getImageInfo(imagePath) {
getImageInfo(imageFile) {
return new Promise((resolve) => {
// 如果传入的是对象,尝试提取路径
if (typeof imagePath === 'object') {
if (imagePath?.tempFilePath) {
imagePath = imagePath.tempFilePath;
} else if (imagePath?.path) {
imagePath = imagePath.path;
} else {
console.warn('无法从对象中获取图片路径,使用默认尺寸:', imagePath);
resolve({ width: 400, height: 300 });
return;
}
}
let imagePath = '';
// 如果不是字符串,使用默认尺寸
if (typeof imagePath !== 'string' || !imagePath) {
console.warn('图片路径无效,使用默认尺寸:', imagePath);
// 获取图片路径 - 处理多种格式
if (imageFile?.tempFilePaths?.length > 0) {
// wx.chooseImage 返回的对象
imagePath = imageFile.tempFilePaths[0];
} else if (imageFile?.tempFiles?.length > 0) {
// 从 tempFiles 中提取路径
const tempFile = imageFile.tempFiles[0];
imagePath = tempFile.path || tempFile.tempFilePath;
} else if (imageFile?.tempFilePath) {
imagePath = imageFile.tempFilePath;
} else if (typeof imageFile === 'string') {
imagePath = imageFile;
} else {
console.warn('无法获取图片路径,使用默认尺寸:', imageFile);
// 默认尺寸
resolve({ width: 400, height: 300 });
return;
}