Merge branch 'dev-wdb' of http://175.27.226.205:3000/huxuejian/ykt-wxapp into dev-wdb
This commit is contained in:
commit
9a251ddcb0
@ -526,6 +526,7 @@ async function sendFollowUp(todo) {
|
|||||||
content: todo.sendContent,
|
content: todo.sendContent,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
console.log("==============>fileList", todo.fileList);
|
||||||
|
|
||||||
// 2. 处理文件列表(图片、宣教文章、问卷)
|
// 2. 处理文件列表(图片、宣教文章、问卷)
|
||||||
if (Array.isArray(todo.fileList)) {
|
if (Array.isArray(todo.fileList)) {
|
||||||
@ -537,20 +538,21 @@ async function sendFollowUp(todo) {
|
|||||||
content: file.URL,
|
content: file.URL,
|
||||||
name: file.file?.name || file.name || "图片",
|
name: file.file?.name || file.name || "图片",
|
||||||
});
|
});
|
||||||
} else if (file.type === "article" && file.file?.url) {
|
} else if (file.file.type === "article" && file.file?.url) {
|
||||||
// 发送宣教文章
|
// 发送宣教文章 - 从 URL 中解析 id
|
||||||
|
const articleId = extractIdFromUrl(file.file.url);
|
||||||
messages.push({
|
messages.push({
|
||||||
type: "article",
|
type: "article",
|
||||||
content: {
|
content: {
|
||||||
_id: file.file?._id || file._id,
|
_id: articleId,
|
||||||
title: file.file?.name || file.name || "宣教文章",
|
title: file.file?.name || "宣教文章",
|
||||||
url: file.file?.url || file.URL,
|
url: file.file?.url || file.URL,
|
||||||
subtitle: file.file?.subtitle || "",
|
subtitle: file.file?.subtitle || "",
|
||||||
cover: file.file?.cover || "",
|
cover: file.file?.cover || "",
|
||||||
articleId: file.file?._id || file._id,
|
articleId: articleId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (file.type === "questionnaire" && file.file?.surveryId) {
|
} else if (file.file.type === "questionnaire" && file.file?.surveryId) {
|
||||||
// 发送问卷
|
// 发送问卷
|
||||||
messages.push({
|
messages.push({
|
||||||
type: "questionnaire",
|
type: "questionnaire",
|
||||||
@ -580,6 +582,30 @@ async function sendFollowUp(todo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 URL 中提取 id 参数
|
||||||
|
* @param {string} url - 完整的 URL
|
||||||
|
* @returns {string} 提取出的 id 值
|
||||||
|
*/
|
||||||
|
function extractIdFromUrl(url) {
|
||||||
|
if (!url) return "";
|
||||||
|
try {
|
||||||
|
// 处理格式: https://www.youcan365.com/patientDeploy/#/pages/article/index?id=267epkhd3xbklcnbf0f45gzp1769567841991&corpId=...
|
||||||
|
const urlObj = new URL(url);
|
||||||
|
const id = urlObj.searchParams.get("id");
|
||||||
|
if (id) return id;
|
||||||
|
|
||||||
|
// 备用方案:使用正则表达式提取
|
||||||
|
const match = url.match(/[?&]id=([^&]+)/);
|
||||||
|
return match ? decodeURIComponent(match[1]) : "";
|
||||||
|
} catch (error) {
|
||||||
|
console.error("解析 URL 失败:", error);
|
||||||
|
// 备用方案:使用正则表达式提取
|
||||||
|
const match = url.match(/[?&]id=([^&]+)/);
|
||||||
|
return match ? decodeURIComponent(match[1]) : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---- filter popup ----
|
// ---- filter popup ----
|
||||||
const filterPopupRef = ref(null);
|
const filterPopupRef = ref(null);
|
||||||
const state = ref(null);
|
const state = ref(null);
|
||||||
|
|||||||
@ -418,6 +418,25 @@ $primary-color: #0877F1;
|
|||||||
background: #2456c7;
|
background: #2456c7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.disabled-btn {
|
||||||
|
background: #ccc;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
border: none;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
min-width: 112rpx;
|
||||||
|
padding: 0 32rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(200, 200, 200, 0.08);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.input-area {
|
.input-area {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin: 0 8rpx;
|
margin: 0 8rpx;
|
||||||
|
|||||||
@ -57,10 +57,12 @@ const props = defineProps({
|
|||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const emit = defineEmits(["streamText", "clearInput"]);
|
const emit = defineEmits(["streamText", "clearInput", "generatingStateChange"]);
|
||||||
|
|
||||||
const typeSelectorRef = ref(null);
|
const typeSelectorRef = ref(null);
|
||||||
const progressRef = ref(null);
|
const progressRef = ref(null);
|
||||||
|
const isGenerating = ref(false);
|
||||||
|
|
||||||
const buttons = ref([
|
const buttons = ref([
|
||||||
{
|
{
|
||||||
id: "followUp",
|
id: "followUp",
|
||||||
@ -183,6 +185,8 @@ const streamTextToInput = (text) => {
|
|||||||
|
|
||||||
// 先清空输入框
|
// 先清空输入框
|
||||||
emit("clearInput");
|
emit("clearInput");
|
||||||
|
isGenerating.value = true;
|
||||||
|
emit("generatingStateChange", true);
|
||||||
|
|
||||||
let currentIndex = 0;
|
let currentIndex = 0;
|
||||||
const speed = 50; // 每个字符的延迟时间(毫秒)
|
const speed = 50; // 每个字符的延迟时间(毫秒)
|
||||||
@ -196,6 +200,8 @@ const streamTextToInput = (text) => {
|
|||||||
currentIndex++;
|
currentIndex++;
|
||||||
} else {
|
} else {
|
||||||
clearInterval(streamInterval);
|
clearInterval(streamInterval);
|
||||||
|
isGenerating.value = false;
|
||||||
|
emit("generatingStateChange", false);
|
||||||
}
|
}
|
||||||
}, speed);
|
}, speed);
|
||||||
}, 100);
|
}, 100);
|
||||||
@ -363,6 +369,11 @@ const handleRegenerateMedicalCase = (data) => {
|
|||||||
const type = { id: data.caseType };
|
const type = { id: data.caseType };
|
||||||
handleCaseTypeSelect(type);
|
handleCaseTypeSelect(type);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 暴露生成状态给父组件
|
||||||
|
defineExpose({
|
||||||
|
isGenerating,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@ -8,18 +8,21 @@
|
|||||||
<view class="input-area">
|
<view class="input-area">
|
||||||
<textarea v-if="!showVoiceInput" class="text-input" v-model="inputText" placeholder="我来说两句..."
|
<textarea v-if="!showVoiceInput" class="text-input" v-model="inputText" placeholder="我来说两句..."
|
||||||
@confirm="sendTextMessage" @focus="handleInputFocus" @input="handleInput"
|
@confirm="sendTextMessage" @focus="handleInputFocus" @input="handleInput"
|
||||||
:auto-height="true" :show-confirm-bar="false" :adjust-position="true" :cursor-spacing="60"
|
:auto-height="true" :show-confirm-bar="false" :adjust-position="true" :cursor-spacing="30"
|
||||||
/>
|
/>
|
||||||
<input v-else class="voice-input-btn" :class="{ recording: isRecording }" @touchstart="startRecord"
|
<input v-else class="voice-input-btn" :class="{ recording: isRecording }" @touchstart="startRecord"
|
||||||
@touchmove="onRecordTouchMove" @touchend="stopRecord" @touchcancel="cancelRecord" :placeholder="isRecording ? '松开发送' : '按住说话'" disabled>
|
@touchmove="onRecordTouchMove" @touchend="stopRecord" @touchcancel="cancelRecord" :placeholder="isRecording ? '松开发送' : '按住说话'" disabled>
|
||||||
</input>
|
</input>
|
||||||
</view>
|
</view>
|
||||||
<button v-if="inputText.trim()" class="send-btn" @click="sendTextMessage">
|
<button v-if="inputText.trim() && !props.isGenerating" class="send-btn" @click="sendTextMessage">
|
||||||
发送
|
发送
|
||||||
</button>
|
</button>
|
||||||
<view v-else class="plus-btn" @click="toggleMorePanel()">
|
<view v-else-if="!inputText.trim() && !props.isGenerating" class="plus-btn" @click="toggleMorePanel()">
|
||||||
<uni-icons type="plusempty" size="28" color="#666" />
|
<uni-icons type="plusempty" size="28" color="#666" />
|
||||||
</view>
|
</view>
|
||||||
|
<view v-else class="send-btn disabled-btn">
|
||||||
|
<text>生成中...</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="more-panel" v-if="showMorePanel">
|
<view class="more-panel" v-if="showMorePanel">
|
||||||
<view v-for="btn in morePanelButtons" :key="btn.text" class="more-btn" @click="btn.action">
|
<view v-for="btn in morePanelButtons" :key="btn.text" class="more-btn" @click="btn.action">
|
||||||
@ -80,6 +83,7 @@ const props = defineProps({
|
|||||||
patientId: { type: String, default: "" },
|
patientId: { type: String, default: "" },
|
||||||
corpId: { type: String, default: "" },
|
corpId: { type: String, default: "" },
|
||||||
orderStatus: { type: String, default: "" },
|
orderStatus: { type: String, default: "" },
|
||||||
|
isGenerating: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
// Emits
|
// Emits
|
||||||
|
|||||||
@ -140,12 +140,14 @@
|
|||||||
!showConsultAccept &&
|
!showConsultAccept &&
|
||||||
orderStatus === 'processing'
|
orderStatus === 'processing'
|
||||||
"
|
"
|
||||||
|
ref="aiAssistantRef"
|
||||||
:groupId="groupId"
|
:groupId="groupId"
|
||||||
:patientAccountId="chatInfo.userID || ''"
|
:patientAccountId="chatInfo.userID || ''"
|
||||||
:patientId="patientId"
|
:patientId="patientId"
|
||||||
:corpId="corpId"
|
:corpId="corpId"
|
||||||
@streamText="handleStreamText"
|
@streamText="handleStreamText"
|
||||||
@clearInput="handleClearInput"
|
@clearInput="handleClearInput"
|
||||||
|
@generatingStateChange="handleGeneratingStateChange"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 聊天输入组件 -->
|
<!-- 聊天输入组件 -->
|
||||||
@ -165,6 +167,7 @@
|
|||||||
:corpId="corpId"
|
:corpId="corpId"
|
||||||
:patientInfo="patientInfo"
|
:patientInfo="patientInfo"
|
||||||
:orderStatus="orderStatus"
|
:orderStatus="orderStatus"
|
||||||
|
:isGenerating="isGenerating"
|
||||||
@scrollToBottom="() => scrollToBottom(true)"
|
@scrollToBottom="() => scrollToBottom(true)"
|
||||||
@messageSent="() => scrollToBottom(true)"
|
@messageSent="() => scrollToBottom(true)"
|
||||||
@endConsult="handleEndConsult"
|
@endConsult="handleEndConsult"
|
||||||
@ -217,6 +220,8 @@ const { initIMAfterLogin } = useAccountStore();
|
|||||||
|
|
||||||
// 聊天输入组件引用
|
// 聊天输入组件引用
|
||||||
const chatInputRef = ref(null);
|
const chatInputRef = ref(null);
|
||||||
|
const aiAssistantRef = ref(null);
|
||||||
|
const isGenerating = ref(false);
|
||||||
|
|
||||||
const groupId = ref("");
|
const groupId = ref("");
|
||||||
const { chatMember, getGroupInfo, getUserAvatar } = useGroupChat(groupId);
|
const { chatMember, getGroupInfo, getUserAvatar } = useGroupChat(groupId);
|
||||||
@ -316,7 +321,11 @@ const fetchGroupOrderStatus = async () => {
|
|||||||
const teamName = result.data.team?.name || "群聊";
|
const teamName = result.data.team?.name || "群聊";
|
||||||
updateNavigationTitle(teamName);
|
updateNavigationTitle(teamName);
|
||||||
|
|
||||||
teamId.value = result.data.teamId || result.data.team?.teamId || result.data.team?._id || "";
|
teamId.value =
|
||||||
|
result.data.teamId ||
|
||||||
|
result.data.team?.teamId ||
|
||||||
|
result.data.team?._id ||
|
||||||
|
"";
|
||||||
|
|
||||||
// 更新患者信息
|
// 更新患者信息
|
||||||
if (result.data.patient) {
|
if (result.data.patient) {
|
||||||
@ -765,7 +774,7 @@ onShow(() => {
|
|||||||
startIMMonitoring(30000);
|
startIMMonitoring(30000);
|
||||||
|
|
||||||
// 监听回访任务发送事件
|
// 监听回访任务发送事件
|
||||||
uni.$on('send-followup-message', handleSendFollowUpMessage);
|
uni.$on("send-followup-message", handleSendFollowUpMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 处理发送回访任务消息
|
// 处理发送回访任务消息
|
||||||
@ -783,10 +792,10 @@ const handleSendFollowUpMessage = async (data) => {
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('发送回访任务消息失败:', error);
|
console.error("发送回访任务消息失败:", error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '发送失败,请重试',
|
title: "发送失败,请重试",
|
||||||
icon: 'none',
|
icon: "none",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -820,6 +829,11 @@ const handleClearInput = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理生成状态变化
|
||||||
|
const handleGeneratingStateChange = (generating) => {
|
||||||
|
isGenerating.value = generating;
|
||||||
|
};
|
||||||
|
|
||||||
// 暴露方法给常用语页面调用
|
// 暴露方法给常用语页面调用
|
||||||
defineExpose({
|
defineExpose({
|
||||||
sendCommonPhrase,
|
sendCommonPhrase,
|
||||||
@ -1016,7 +1030,7 @@ onUnmounted(() => {
|
|||||||
timChatManager.setCallback("onError", null);
|
timChatManager.setCallback("onError", null);
|
||||||
|
|
||||||
// 移除回访任务发送事件监听
|
// 移除回访任务发送事件监听
|
||||||
uni.$off('send-followup-message', handleSendFollowUpMessage);
|
uni.$off("send-followup-message", handleSendFollowUpMessage);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -164,9 +164,18 @@
|
|||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
uni.showToast({
|
// 显示重试提示
|
||||||
title: "IM连接失败,请重试",
|
uni.showModal({
|
||||||
icon: "none",
|
title: "IM连接失败",
|
||||||
|
content: "连接失败,请检查网络后重试。如果IM连接失败,请重新登陆IM再连接",
|
||||||
|
confirmText: "重新登陆",
|
||||||
|
cancelText: "取消",
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
// 重新登陆
|
||||||
|
handleReloginIM();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -178,21 +187,100 @@
|
|||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
|
|
||||||
if (!reconnected) {
|
if (!reconnected) {
|
||||||
|
// 显示重试提示
|
||||||
|
uni.showModal({
|
||||||
|
title: "IM连接失败",
|
||||||
|
content: "连接失败,请检查网络后重试。如果IM连接失败,请重新登陆IM再连接",
|
||||||
|
confirmText: "重新登陆",
|
||||||
|
cancelText: "取消",
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
// 重新登陆
|
||||||
|
handleReloginIM();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 重新登陆IM
|
||||||
|
const handleReloginIM = async () => {
|
||||||
|
try {
|
||||||
|
uni.showLoading({
|
||||||
|
title: "重新登陆中...",
|
||||||
|
});
|
||||||
|
|
||||||
|
// 清理旧的IM实例
|
||||||
|
if (globalTimChatManager) {
|
||||||
|
await globalTimChatManager.cleanupOldInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新初始化IM
|
||||||
|
const { initIMAfterLogin } = useAccountStore();
|
||||||
|
const success = await initIMAfterLogin();
|
||||||
|
uni.hideLoading();
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "IM连接成功",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
// 重新加载会话列表
|
||||||
|
await loadConversationList();
|
||||||
|
setupConversationListener();
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: "IM连接失败,请检查网络",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.hideLoading();
|
||||||
|
console.error("重新登陆IM失败:", error);
|
||||||
|
uni.showToast({
|
||||||
|
title: "重新登陆失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 加载会话列表
|
// 加载会话列表
|
||||||
const loadConversationList = async () => {
|
const loadConversationList = async () => {
|
||||||
if (loading.value) return;
|
if (loading.value) return;
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
console.log("开始加载群聊列表");
|
console.log("开始加载群聊列表");
|
||||||
if (!globalTimChatManager || !globalTimChatManager.getGroupList) {
|
|
||||||
|
// 确保 IM 已连接
|
||||||
|
if (!globalTimChatManager) {
|
||||||
throw new Error("IM管理器未初始化");
|
throw new Error("IM管理器未初始化");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查 TIM 实例是否存在
|
||||||
|
if (!globalTimChatManager.tim) {
|
||||||
|
console.warn("TIM实例不存在,尝试重新初始化IM");
|
||||||
|
const reinitialized = await initIMAfterLogin();
|
||||||
|
if (!reinitialized) {
|
||||||
|
throw new Error("IM重新初始化失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否已登录
|
||||||
|
if (!globalTimChatManager.isLoggedIn) {
|
||||||
|
console.warn("IM未登录,尝试重新连接");
|
||||||
|
const reconnected = await globalTimChatManager.ensureIMConnection();
|
||||||
|
if (!reconnected) {
|
||||||
|
throw new Error("IM重新连接失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!globalTimChatManager.getGroupList) {
|
||||||
|
throw new Error("IM管理器方法不可用");
|
||||||
|
}
|
||||||
|
|
||||||
const result = await globalTimChatManager.getGroupList();
|
const result = await globalTimChatManager.getGroupList();
|
||||||
if (result && result.success && result.groupList) {
|
if (result && result.success && result.groupList) {
|
||||||
// 合并后端群组详细信息(已包含格式化和排序)
|
// 合并后端群组详细信息(已包含格式化和排序)
|
||||||
|
|||||||
@ -108,16 +108,30 @@ export default defineStore("accountStore", () => {
|
|||||||
}
|
}
|
||||||
async function initIMAfterLogin() {
|
async function initIMAfterLogin() {
|
||||||
if (isIMInitialized.value) return true;
|
if (isIMInitialized.value) return true;
|
||||||
if (!doctorInfo.value) return;
|
if (!doctorInfo.value) {
|
||||||
|
console.error('医生信息未获取,无法初始化IM');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const userID = doctorInfo.value.userid;
|
const userID = doctorInfo.value.userid;
|
||||||
if (!userID) await getDoctorInfo();
|
if (!userID) {
|
||||||
await initGlobalTIM(userID);
|
await getDoctorInfo();
|
||||||
|
if (!doctorInfo.value?.userid) {
|
||||||
|
throw new Error('无法获取用户ID');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const success = await initGlobalTIM(userID);
|
||||||
|
if (!success) {
|
||||||
|
console.error('initGlobalTIM 返回失败');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
isIMInitialized.value = true;
|
isIMInitialized.value = true;
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('IM初始化失败:', error);
|
console.error('IM初始化失败:', error);
|
||||||
|
isIMInitialized.value = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,13 +41,13 @@ export async function sendTextMessage(content) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送图片消息
|
* 发送图片消息
|
||||||
* @param {string} imageUrl - 图片URL(字符串)
|
* @param {string|File} imageSource - 图片URL(字符串)或文件对象
|
||||||
* @param {string} imageName - 图片名称(可选)
|
* @param {string} imageName - 图片名称(可选)
|
||||||
* @returns {Promise<boolean>} 发送是否成功
|
* @returns {Promise<boolean>} 发送是否成功
|
||||||
*/
|
*/
|
||||||
export async function sendImageMessage(imageUrl, imageName = '图片') {
|
export async function sendImageMessage(imageSource, imageName = '图片') {
|
||||||
if (!imageUrl) {
|
if (!imageSource) {
|
||||||
toast('图片URL不能为空');
|
toast('图片不能为空');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,9 +57,17 @@ export async function sendImageMessage(imageUrl, imageName = '图片') {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 直接调用 tim-chat 的 sendImageMessage 方法
|
// 如果是URL字符串,需要先下载转换为文件对象
|
||||||
// tim-chat.js 中的 getImageUrl 方法可以处理 URL 字符串
|
let imageFile = imageSource;
|
||||||
const result = await globalTimChatManager.sendImageMessage(imageUrl);
|
if (typeof imageSource === 'string') {
|
||||||
|
imageFile = await downloadImageAsFile(imageSource, imageName);
|
||||||
|
if (!imageFile) {
|
||||||
|
toast('图片下载失败');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await globalTimChatManager.sendImageMessage(imageFile);
|
||||||
|
|
||||||
if (result?.success) {
|
if (result?.success) {
|
||||||
return true;
|
return true;
|
||||||
@ -74,6 +82,44 @@ export async function sendImageMessage(imageUrl, imageName = '图片') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将图片URL下载转换为文件对象(小程序环境)
|
||||||
|
* @param {string} imageUrl - 图片URL
|
||||||
|
* @param {string} fileName - 文件名
|
||||||
|
* @returns {Promise<Object|null>} 文件对象或null
|
||||||
|
*/
|
||||||
|
async function downloadImageAsFile(imageUrl, fileName = '图片') {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
uni.downloadFile({
|
||||||
|
url: imageUrl,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
// 返回包含 tempFiles 数组的对象,模拟 uni.chooseMedia 的返回格式
|
||||||
|
const fileObj = {
|
||||||
|
tempFiles: [{
|
||||||
|
tempFilePath: res.tempFilePath,
|
||||||
|
path: res.tempFilePath,
|
||||||
|
size: 0,
|
||||||
|
type: 'image'
|
||||||
|
}],
|
||||||
|
// 也保留直接的 tempFilePath 以兼容其他代码
|
||||||
|
tempFilePath: res.tempFilePath
|
||||||
|
};
|
||||||
|
console.log('图片下载成功,文件对象:', fileObj);
|
||||||
|
resolve(fileObj);
|
||||||
|
} else {
|
||||||
|
console.error('下载图片失败,状态码:', res.statusCode);
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.error('下载图片失败:', error);
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送宣教文章消息
|
* 发送宣教文章消息
|
||||||
* @param {Object} article - 文章对象 { _id, title, cover, url }
|
* @param {Object} article - 文章对象 { _id, title, cover, url }
|
||||||
|
|||||||
@ -1,234 +0,0 @@
|
|||||||
# 微信小程序分享功能使用指南
|
|
||||||
|
|
||||||
## 功能说明
|
|
||||||
|
|
||||||
提供了完整的微信小程序分享功能,包括:
|
|
||||||
- 分享给好友
|
|
||||||
- 分享到朋友圈
|
|
||||||
- 保存图片到相册
|
|
||||||
|
|
||||||
## 使用方法
|
|
||||||
|
|
||||||
### 1. 基础分享(在页面中)
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<view>
|
|
||||||
<button open-type="share">分享给好友</button>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { createShareMessage, createShareTimeline } from '@/utils/share'
|
|
||||||
|
|
||||||
// 分享给好友
|
|
||||||
function onShareAppMessage() {
|
|
||||||
return createShareMessage({
|
|
||||||
title: '分享标题',
|
|
||||||
path: '/pages/index/index?id=123',
|
|
||||||
imageUrl: 'https://example.com/share.jpg'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分享到朋友圈(需要在 app.json 中配置)
|
|
||||||
function onShareTimeline() {
|
|
||||||
return createShareTimeline({
|
|
||||||
title: '朋友圈标题',
|
|
||||||
query: 'id=123',
|
|
||||||
imageUrl: 'https://example.com/share.jpg'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导出分享方法
|
|
||||||
defineExpose({
|
|
||||||
onShareAppMessage,
|
|
||||||
onShareTimeline
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 使用分享组件
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<view>
|
|
||||||
<share-actions
|
|
||||||
@save="handleSave"
|
|
||||||
:show-save="true"
|
|
||||||
:show-share="true"
|
|
||||||
save-text="保存图片"
|
|
||||||
share-text="分享微信"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { saveImageToAlbum, createShareMessage } from '@/utils/share'
|
|
||||||
import shareActions from '@/components/share-actions.vue'
|
|
||||||
|
|
||||||
// 保存图片
|
|
||||||
async function handleSave() {
|
|
||||||
const imagePath = 'https://example.com/image.jpg'
|
|
||||||
await saveImageToAlbum(imagePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分享配置
|
|
||||||
function onShareAppMessage() {
|
|
||||||
return createShareMessage({
|
|
||||||
title: '分享标题',
|
|
||||||
path: '/pages/index/index'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
onShareAppMessage
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 保存二维码图片
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<view>
|
|
||||||
<uqrcode ref="qrcode" canvasId="qrcode" :value="qrcodeUrl" />
|
|
||||||
<button @click="saveQrcode">保存二维码</button>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { saveImageToAlbum } from '@/utils/share'
|
|
||||||
import { toast } from '@/utils/widget'
|
|
||||||
|
|
||||||
const qrcode = ref(null)
|
|
||||||
const qrcodeUrl = ref('https://example.com')
|
|
||||||
|
|
||||||
async function saveQrcode() {
|
|
||||||
try {
|
|
||||||
if (!qrcode.value) {
|
|
||||||
toast('二维码未加载完成')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取二维码临时文件路径
|
|
||||||
const tempFilePath = qrcode.value.toTempFilePath()
|
|
||||||
if (tempFilePath) {
|
|
||||||
await saveImageToAlbum(tempFilePath)
|
|
||||||
} else {
|
|
||||||
toast('获取二维码失败')
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('保存失败:', err)
|
|
||||||
toast('保存失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 动态分享内容
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
import { ref, computed } from 'vue'
|
|
||||||
import { createShareMessage } from '@/utils/share'
|
|
||||||
|
|
||||||
const currentItem = ref({
|
|
||||||
id: '123',
|
|
||||||
title: '商品标题',
|
|
||||||
image: 'https://example.com/product.jpg'
|
|
||||||
})
|
|
||||||
|
|
||||||
// 动态生成分享配置
|
|
||||||
function onShareAppMessage() {
|
|
||||||
return createShareMessage({
|
|
||||||
title: currentItem.value.title,
|
|
||||||
path: `/pages/detail/detail?id=${currentItem.value.id}`,
|
|
||||||
imageUrl: currentItem.value.image
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
onShareAppMessage
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 配置说明
|
|
||||||
|
|
||||||
### 1. 启用分享到朋友圈
|
|
||||||
|
|
||||||
在 `pages.json` 中配置页面:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"path": "pages/index/index",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "首页",
|
|
||||||
"enableShareTimeline": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 全局分享配置
|
|
||||||
|
|
||||||
在 `App.vue` 中配置全局分享:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
onShareAppMessage() {
|
|
||||||
return {
|
|
||||||
title: '默认分享标题',
|
|
||||||
path: '/pages/index/index'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onShareTimeline() {
|
|
||||||
return {
|
|
||||||
title: '默认朋友圈标题'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## API 说明
|
|
||||||
|
|
||||||
### createShareMessage(options)
|
|
||||||
|
|
||||||
创建分享给好友的配置
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `title` (string): 分享标题
|
|
||||||
- `path` (string): 分享路径
|
|
||||||
- `imageUrl` (string): 分享图片URL
|
|
||||||
|
|
||||||
**返回:** 分享配置对象
|
|
||||||
|
|
||||||
### createShareTimeline(options)
|
|
||||||
|
|
||||||
创建分享到朋友圈的配置
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `title` (string): 分享标题
|
|
||||||
- `query` (string): 分享路径参数
|
|
||||||
- `imageUrl` (string): 分享图片URL
|
|
||||||
|
|
||||||
**返回:** 分享配置对象
|
|
||||||
|
|
||||||
### saveImageToAlbum(filePath)
|
|
||||||
|
|
||||||
保存图片到相册
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `filePath` (string): 图片路径(本地临时路径或网络路径)
|
|
||||||
|
|
||||||
**返回:** Promise<boolean>
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. 分享图片建议尺寸:5:4,推荐 500x400px
|
|
||||||
2. 分享路径必须是已注册的页面路径
|
|
||||||
3. 保存图片需要用户授权相册权限
|
|
||||||
4. 分享到朋友圈需要在页面配置中启用
|
|
||||||
5. 网络图片会自动下载后保存到相册
|
|
||||||
@ -20,16 +20,16 @@ if (!TIM_CONFIG.SDKAppID || isNaN(TIM_CONFIG.SDKAppID)) {
|
|||||||
|
|
||||||
// IM连接配置常量
|
// IM连接配置常量
|
||||||
const IM_CONNECTION_CONFIG = {
|
const IM_CONNECTION_CONFIG = {
|
||||||
MAX_RECONNECT_ATTEMPTS: 10, // 最大重连次数
|
MAX_RECONNECT_ATTEMPTS: 15, // 增加最大重连次数到15次
|
||||||
RECONNECT_DELAYS: [2000, 4000, 8000, 16000, 30000], // 重连延迟(指数退避)
|
RECONNECT_DELAYS: [1000, 2000, 4000, 8000, 16000, 30000], // 重连延迟(指数退避)
|
||||||
LOGIN_COOLDOWN: 5000, // 登录冷却时间
|
LOGIN_COOLDOWN: 3000, // 降低登录冷却时间到3秒
|
||||||
SDK_READY_TIMEOUT: 15000, // SDK就绪超时时间
|
SDK_READY_TIMEOUT: 20000, // 增加SDK就绪超时时间到20秒
|
||||||
LOGIN_CHECK_INTERVAL_STABLE: 60000, // 稳定状态检查间隔
|
LOGIN_CHECK_INTERVAL_STABLE: 60000, // 稳定状态检查间隔
|
||||||
LOGIN_CHECK_INTERVAL_UNSTABLE: 15000, // 不稳定状态检查间隔
|
LOGIN_CHECK_INTERVAL_UNSTABLE: 10000, // 降低不稳定状态检查间隔到10秒
|
||||||
LOGIN_CHECK_FIRST_DELAY: 30000, // 首次检查延迟
|
LOGIN_CHECK_FIRST_DELAY: 20000, // 降低首次检查延迟到20秒
|
||||||
HEARTBEAT_INTERVAL: 60000, // 心跳间隔(毫秒)60秒
|
HEARTBEAT_INTERVAL: 60000, // 心跳间隔(毫秒)60秒
|
||||||
HEARTBEAT_MAX_FAIL: 3, // 心跳最大失败次数
|
HEARTBEAT_MAX_FAIL: 2, // 降低心跳最大失败次数到2次
|
||||||
NETWORK_RECONNECT_DELAY: 2000, // 网络恢复后延迟重连
|
NETWORK_RECONNECT_DELAY: 1000, // 降低网络恢复后延迟重连到1秒
|
||||||
MESSAGE_BATCH_COUNT: 20, // 每批消息数量
|
MESSAGE_BATCH_COUNT: 20, // 每批消息数量
|
||||||
MAX_MESSAGE_REQUESTS: 50, // 最大消息请求次数
|
MAX_MESSAGE_REQUESTS: 50, // 最大消息请求次数
|
||||||
MAX_CACHE_SIZE: 1000, // 最大缓存消息数
|
MAX_CACHE_SIZE: 1000, // 最大缓存消息数
|
||||||
@ -191,7 +191,12 @@ class TimChatManager {
|
|||||||
|
|
||||||
// 等待SDK Ready
|
// 等待SDK Ready
|
||||||
console.log('等待SDK Ready...')
|
console.log('等待SDK Ready...')
|
||||||
|
try {
|
||||||
await this.waitForSDKReady(IM_CONNECTION_CONFIG.SDK_READY_TIMEOUT)
|
await this.waitForSDKReady(IM_CONNECTION_CONFIG.SDK_READY_TIMEOUT)
|
||||||
|
} catch (timeoutError) {
|
||||||
|
// SDK Ready 超时,但不一定是致命错误,继续进行
|
||||||
|
console.warn('SDK Ready 超时,但继续进行:', timeoutError.message)
|
||||||
|
}
|
||||||
|
|
||||||
console.log('=== IM初始化完成 ===')
|
console.log('=== IM初始化完成 ===')
|
||||||
return true
|
return true
|
||||||
@ -264,7 +269,7 @@ class TimChatManager {
|
|||||||
waitForSDKReady(timeout = IM_CONNECTION_CONFIG.SDK_READY_TIMEOUT) {
|
waitForSDKReady(timeout = IM_CONNECTION_CONFIG.SDK_READY_TIMEOUT) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
const checkInterval = 1000 // 每秒检查一次
|
const checkInterval = 500 // 每500ms检查一次,更快响应
|
||||||
let checkCount = 0
|
let checkCount = 0
|
||||||
|
|
||||||
const checkSDKReady = () => {
|
const checkSDKReady = () => {
|
||||||
@ -277,8 +282,8 @@ class TimChatManager {
|
|||||||
} else if (elapsed > timeout) {
|
} else if (elapsed > timeout) {
|
||||||
const error = new Error(`等待SDK Ready超时(${timeout}ms)`)
|
const error = new Error(`等待SDK Ready超时(${timeout}ms)`)
|
||||||
console.error('✗', error.message)
|
console.error('✗', error.message)
|
||||||
// 超时不算致命错误,尝试继续
|
// 超时时拒绝而不是继续,让调用者知道出错了
|
||||||
resolve()
|
reject(error)
|
||||||
} else {
|
} else {
|
||||||
console.log(`等待SDK Ready... ${Math.floor(elapsed / 1000)}/${Math.floor(timeout / 1000)}秒`)
|
console.log(`等待SDK Ready... ${Math.floor(elapsed / 1000)}/${Math.floor(timeout / 1000)}秒`)
|
||||||
setTimeout(checkSDKReady, checkInterval)
|
setTimeout(checkSDKReady, checkInterval)
|
||||||
@ -838,16 +843,16 @@ class TimChatManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (netState === TIM.TYPES.NET_STATE_CONNECTED) {
|
if (netState === TIM.TYPES.NET_STATE_CONNECTED) {
|
||||||
console.log('✓ 网络已连接,延迟检查IM状态以确保稳定')
|
console.log('✓ 网络已连接,立即检查IM状态')
|
||||||
|
|
||||||
// 网络恢复后延迟再检查,避免网络还不稳定时立即重连
|
// 网络恢复后立即检查,不再延迟
|
||||||
const delay = IM_CONNECTION_CONFIG.NETWORK_RECONNECT_DELAY
|
const delay = IM_CONNECTION_CONFIG.NETWORK_RECONNECT_DELAY
|
||||||
this.networkReconnectTimer = setTimeout(() => {
|
this.networkReconnectTimer = setTimeout(() => {
|
||||||
if (this.tim && !this.isLoggedIn && !this.isLoggingIn) {
|
if (this.tim && !this.isLoggedIn && !this.isLoggingIn) {
|
||||||
console.log('🔄 网络已稳定,开始重连')
|
console.log('🔄 网络已恢复,开始重连')
|
||||||
this.ensureIMConnection()
|
this.ensureIMConnection()
|
||||||
} else if (this.isLoggedIn) {
|
} else if (this.isLoggedIn) {
|
||||||
console.log('✓ 网络已稳定,IM连接正常')
|
console.log('✓ 网络已恢复,IM连接正常')
|
||||||
}
|
}
|
||||||
this.networkReconnectTimer = null
|
this.networkReconnectTimer = null
|
||||||
}, delay)
|
}, delay)
|
||||||
@ -855,7 +860,7 @@ class TimChatManager {
|
|||||||
// 重置重连次数(网络恢复后给更多机会)
|
// 重置重连次数(网络恢复后给更多机会)
|
||||||
if (this.reconnectAttempts > 0) {
|
if (this.reconnectAttempts > 0) {
|
||||||
console.log(`重置重连次数(之前: ${this.reconnectAttempts})`)
|
console.log(`重置重连次数(之前: ${this.reconnectAttempts})`)
|
||||||
this.reconnectAttempts = 0
|
this.reconnectAttempts = Math.max(0, this.reconnectAttempts - 3) // 减少重连次数,给更多机会
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (netState === TIM.TYPES.NET_STATE_CONNECTING) {
|
} else if (netState === TIM.TYPES.NET_STATE_CONNECTING) {
|
||||||
@ -1025,8 +1030,31 @@ class TimChatManager {
|
|||||||
// 获取群聊列表
|
// 获取群聊列表
|
||||||
getGroupList() {
|
getGroupList() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
// 如果 TIM 实例不存在,等待初始化
|
||||||
if (!this.tim) {
|
if (!this.tim) {
|
||||||
reject(new Error('TIM实例不存在'))
|
console.log('TIM实例不存在,等待初始化...')
|
||||||
|
let waitTime = 0
|
||||||
|
const maxWaitTime = 30000 // 最多等待30秒
|
||||||
|
const checkInterval = 500 // 每500ms检查一次
|
||||||
|
let timeoutHandle = null
|
||||||
|
|
||||||
|
const checkTIMReady = () => {
|
||||||
|
if (this.tim && this.isLoggedIn) {
|
||||||
|
console.log('TIM实例已就绪,开始获取群聊列表')
|
||||||
|
if (timeoutHandle) clearTimeout(timeoutHandle)
|
||||||
|
this.getGroupListInternal().then(resolve).catch(reject)
|
||||||
|
} else if (waitTime >= maxWaitTime) {
|
||||||
|
console.error('等待TIM实例就绪超时')
|
||||||
|
if (timeoutHandle) clearTimeout(timeoutHandle)
|
||||||
|
reject(new Error('IM连接失败,请检查网络连接或重新登陆'))
|
||||||
|
} else {
|
||||||
|
waitTime += checkInterval
|
||||||
|
console.log(`等待TIM实例就绪... (${Math.floor(waitTime / 1000)}/${Math.floor(maxWaitTime / 1000)}秒)`)
|
||||||
|
timeoutHandle = setTimeout(checkTIMReady, checkInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkTIMReady()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1034,7 +1062,7 @@ class TimChatManager {
|
|||||||
console.log('SDK未ready,等待SDK初始化...')
|
console.log('SDK未ready,等待SDK初始化...')
|
||||||
let waitTime = 0
|
let waitTime = 0
|
||||||
const maxWaitTime = 30000 // 最多等待30秒
|
const maxWaitTime = 30000 // 最多等待30秒
|
||||||
const checkInterval = 1000 // 每秒检查一次
|
const checkInterval = 500 // 每500ms检查一次
|
||||||
let timeoutHandle = null
|
let timeoutHandle = null
|
||||||
|
|
||||||
const checkSDKReady = () => {
|
const checkSDKReady = () => {
|
||||||
@ -2189,7 +2217,7 @@ class TimChatManager {
|
|||||||
async sendImageMessage(imageFile) {
|
async sendImageMessage(imageFile) {
|
||||||
if (!this.tim) {
|
if (!this.tim) {
|
||||||
this.triggerCallback('onError', 'IM未初始化')
|
this.triggerCallback('onError', 'IM未初始化')
|
||||||
return
|
return { success: false, error: 'IM未初始化' }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.conversation) {
|
if (!this.conversation) {
|
||||||
@ -2240,20 +2268,27 @@ class TimChatManager {
|
|||||||
// 触发消息接收回调,让UI立即显示
|
// 触发消息接收回调,让UI立即显示
|
||||||
this.triggerCallback('onMessageReceived', localMessage)
|
this.triggerCallback('onMessageReceived', localMessage)
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('准备创建图片消息,imageFile:', imageFile)
|
||||||
const message = this.tim.createImageMessage({
|
const message = this.tim.createImageMessage({
|
||||||
to: groupID,
|
to: groupID,
|
||||||
conversationType: TIM.TYPES.CONV_GROUP,
|
conversationType: TIM.TYPES.CONV_GROUP,
|
||||||
payload: { file: imageFile }
|
payload: { file: imageFile }
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
console.log('图片消息创建成功:', message)
|
||||||
|
|
||||||
|
if (!message) {
|
||||||
|
throw new Error('createImageMessage 返回空值')
|
||||||
|
}
|
||||||
|
|
||||||
await this.tim.sendMessage(message)
|
await this.tim.sendMessage(message)
|
||||||
localMessage.status = 'success'
|
localMessage.status = 'success'
|
||||||
return { success: true, message: localMessage }
|
return { success: true, message: localMessage }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('图片消息发送失败:', error)
|
console.error('图片消息发送失败:', error)
|
||||||
localMessage.status = 'failed'
|
localMessage.status = 'failed'
|
||||||
return { success: false, error }
|
return { success: false, error: error.message || error }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 发送语音消息
|
// 发送语音消息
|
||||||
@ -2840,7 +2875,11 @@ const initGlobalTIM = async (userID, forceReinit = false) => {
|
|||||||
console.log('强制重新初始化:TIM登出成功')
|
console.log('强制重新初始化:TIM登出成功')
|
||||||
}
|
}
|
||||||
|
|
||||||
await globalTimChatManager.initTIM(userID)
|
const success = await globalTimChatManager.initTIM(userID)
|
||||||
|
if (!success) {
|
||||||
|
console.error('强制重新初始化失败')
|
||||||
|
return false
|
||||||
|
}
|
||||||
console.log('强制重新初始化完成')
|
console.log('强制重新初始化完成')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -2850,7 +2889,11 @@ const initGlobalTIM = async (userID, forceReinit = false) => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
await globalTimChatManager.initTIM(userID)
|
const success = await globalTimChatManager.initTIM(userID)
|
||||||
|
if (!success) {
|
||||||
|
console.error('全局IM初始化失败')
|
||||||
|
return false
|
||||||
|
}
|
||||||
console.log('全局IM初始化成功')
|
console.log('全局IM初始化成功')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user