2026-01-22 15:13:26 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<view class="chat-page">
|
|
|
|
|
|
<!-- 聊天消息区域 -->
|
|
|
|
|
|
<scroll-view
|
|
|
|
|
|
class="chat-content"
|
|
|
|
|
|
scroll-y="true"
|
|
|
|
|
|
enhanced="true"
|
|
|
|
|
|
bounces="false"
|
|
|
|
|
|
:scroll-into-view="scrollIntoView"
|
|
|
|
|
|
@scroll="onScroll"
|
|
|
|
|
|
@scrolltoupper="handleScrollToUpper"
|
|
|
|
|
|
ref="chatScrollView"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 加载更多提示 -->
|
|
|
|
|
|
<view class="load-more-tip" v-if="messageList.length >= 15">
|
|
|
|
|
|
<view class="loading" v-if="isLoadingMore">
|
|
|
|
|
|
<text class="loading-text">加载中...</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="load-tip" v-else-if="!isCompleted">
|
|
|
|
|
|
<text class="tip-text">↑ 上滑加载更多</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="load-tip" v-else>
|
|
|
|
|
|
<text class="completed-text">已加载全部消息</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 聊天消息列表 -->
|
|
|
|
|
|
<view class="message-list" @click="closeMorePanel">
|
|
|
|
|
|
<view
|
|
|
|
|
|
v-for="(message, index) in messageList"
|
|
|
|
|
|
:key="message.ID"
|
|
|
|
|
|
:id="`msg-${message.ID}`"
|
|
|
|
|
|
class="message-item"
|
|
|
|
|
|
:class="{
|
|
|
|
|
|
'message-right': message.flow === 'out',
|
|
|
|
|
|
'message-left': message.flow === 'in',
|
|
|
|
|
|
}"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 时间分割线 -->
|
|
|
|
|
|
<view
|
|
|
|
|
|
v-if="shouldShowTime(message, index, messageList)"
|
|
|
|
|
|
class="time-divider"
|
|
|
|
|
|
>
|
|
|
|
|
|
<text class="time-text">{{ formatTime(message.lastTime) }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view v-if="isSystemMessage(message)">
|
|
|
|
|
|
<SystemMessage :message="message" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 消息内容 -->
|
|
|
|
|
|
<view v-else class="message-content">
|
2026-01-22 16:35:05 +08:00
|
|
|
|
<!-- 医生头像(左侧) -->
|
2026-01-22 15:13:26 +08:00
|
|
|
|
<image
|
|
|
|
|
|
v-if="message.flow === 'in'"
|
|
|
|
|
|
class="doctor-msg-avatar"
|
2026-01-26 18:08:01 +08:00
|
|
|
|
:src="
|
|
|
|
|
|
chatMember[message.from]?.avatar || '/static/default-avatar.png'
|
|
|
|
|
|
"
|
2026-01-22 15:13:26 +08:00
|
|
|
|
mode="aspectFill"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2026-01-22 16:35:05 +08:00
|
|
|
|
<!-- 患者头像(右侧) -->
|
2026-01-22 15:13:26 +08:00
|
|
|
|
<image
|
|
|
|
|
|
v-if="message.flow === 'out'"
|
|
|
|
|
|
class="user-msg-avatar"
|
2026-01-26 18:08:01 +08:00
|
|
|
|
:src="
|
|
|
|
|
|
chatMember[message.from]?.avatar || '/static/home/avatar.svg'
|
|
|
|
|
|
"
|
2026-01-22 15:13:26 +08:00
|
|
|
|
mode="aspectFill"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 消息内容区域 -->
|
|
|
|
|
|
<view class="message-bubble-container">
|
|
|
|
|
|
<!-- 用户名显示 -->
|
|
|
|
|
|
<view
|
|
|
|
|
|
class="username-label"
|
|
|
|
|
|
:class="{
|
|
|
|
|
|
left: message.flow === 'in',
|
|
|
|
|
|
right: message.flow === 'out',
|
|
|
|
|
|
}"
|
|
|
|
|
|
>
|
|
|
|
|
|
<text class="username-text">{{
|
|
|
|
|
|
chatMember[message.from]?.name
|
|
|
|
|
|
}}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
2026-01-27 15:07:39 +08:00
|
|
|
|
|
2026-01-22 15:13:26 +08:00
|
|
|
|
<view class="message-bubble" :class="getBubbleClass(message)">
|
|
|
|
|
|
<!-- 消息内容 -->
|
|
|
|
|
|
<MessageTypes
|
|
|
|
|
|
:message="message"
|
|
|
|
|
|
:formatTime="formatTime"
|
|
|
|
|
|
:playingVoiceId="playingVoiceId"
|
|
|
|
|
|
@playVoice="playVoice"
|
|
|
|
|
|
@previewImage="previewImage"
|
|
|
|
|
|
@viewDetail="(message) => handleViewDetail(message)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 发送状态 -->
|
|
|
|
|
|
<view v-if="message.flow === 'out'" class="message-status">
|
|
|
|
|
|
<text
|
|
|
|
|
|
v-if="message.status === 'failed'"
|
|
|
|
|
|
class="status-text failed"
|
|
|
|
|
|
>发送失败</text
|
|
|
|
|
|
>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
|
2026-01-26 18:08:01 +08:00
|
|
|
|
<!-- 接受问诊组件 -->
|
|
|
|
|
|
<ConsultAccept
|
|
|
|
|
|
v-if="showConsultAccept"
|
|
|
|
|
|
@accept="handleAcceptConsult"
|
|
|
|
|
|
@reject="handleRejectConsult"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 拒绝原因对话框 -->
|
|
|
|
|
|
<RejectReasonModal
|
|
|
|
|
|
:visible="showRejectReasonModal"
|
|
|
|
|
|
@confirm="handleRejectReasonConfirm"
|
|
|
|
|
|
@cancel="handleRejectReasonCancel"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
2026-01-22 15:13:26 +08:00
|
|
|
|
<!-- 聊天输入组件 -->
|
|
|
|
|
|
<ChatInput
|
2026-01-26 18:08:01 +08:00
|
|
|
|
v-if="!isEvaluationPopupOpen && !showConsultAccept"
|
2026-01-23 16:09:34 +08:00
|
|
|
|
ref="chatInputRef"
|
2026-01-22 15:13:26 +08:00
|
|
|
|
:timChatManager="timChatManager"
|
|
|
|
|
|
:formatTime="formatTime"
|
2026-01-27 15:07:39 +08:00
|
|
|
|
:groupId="chatInfo.conversationID ? chatInfo.conversationID.replace('GROUP', '') : ''"
|
|
|
|
|
|
:userId="openid"
|
|
|
|
|
|
:corpId="corpId"
|
2026-01-22 16:35:05 +08:00
|
|
|
|
@scrollToBottom="() => scrollToBottom(true)"
|
|
|
|
|
|
@messageSent="() => scrollToBottom(true)"
|
2026-01-27 13:42:59 +08:00
|
|
|
|
@endConsult="handleEndConsult"
|
2026-01-22 15:13:26 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { ref, onUnmounted, nextTick, watch, computed } from "vue";
|
|
|
|
|
|
import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
|
|
|
|
|
|
import { storeToRefs } from "pinia";
|
|
|
|
|
|
import useAccountStore from "@/store/account.js";
|
|
|
|
|
|
import { globalTimChatManager } from "@/utils/tim-chat.js";
|
|
|
|
|
|
import {
|
|
|
|
|
|
startIMMonitoring,
|
|
|
|
|
|
stopIMMonitoring,
|
|
|
|
|
|
} from "@/utils/im-status-manager.js";
|
|
|
|
|
|
import {
|
|
|
|
|
|
getVoiceUrl,
|
|
|
|
|
|
validateVoiceUrl,
|
|
|
|
|
|
createAudioContext,
|
|
|
|
|
|
showMessage,
|
|
|
|
|
|
formatTime,
|
|
|
|
|
|
shouldShowTime,
|
|
|
|
|
|
previewImage,
|
|
|
|
|
|
throttle,
|
|
|
|
|
|
clearMessageCache,
|
|
|
|
|
|
handleViewDetail,
|
|
|
|
|
|
checkIMConnectionStatus,
|
|
|
|
|
|
} from "@/utils/chat-utils.js";
|
2026-01-27 15:07:39 +08:00
|
|
|
|
import { sendConsultRejectedMessage, endConsultation, sendArticleMessage } from "@/utils/api.js";
|
2026-01-22 15:13:26 +08:00
|
|
|
|
import useGroupChat from "./hooks/use-group-chat";
|
|
|
|
|
|
import MessageTypes from "./components/message-types.vue";
|
|
|
|
|
|
import ChatInput from "./components/chat-input.vue";
|
|
|
|
|
|
import SystemMessage from "./components/system-message.vue";
|
2026-01-26 18:08:01 +08:00
|
|
|
|
import ConsultAccept from "./components/consult-accept.vue";
|
|
|
|
|
|
import RejectReasonModal from "./components/reject-reason-modal.vue";
|
2026-01-22 15:13:26 +08:00
|
|
|
|
|
|
|
|
|
|
const timChatManager = globalTimChatManager;
|
|
|
|
|
|
|
2026-01-27 15:07:39 +08:00
|
|
|
|
// 获取环境变量
|
|
|
|
|
|
const env = __VITE_ENV__;
|
|
|
|
|
|
const corpId = env.MP_CORP_ID || '';
|
|
|
|
|
|
|
2026-01-22 15:13:26 +08:00
|
|
|
|
// 获取登录状态
|
|
|
|
|
|
const { account, openid, isIMInitialized } = storeToRefs(useAccountStore());
|
|
|
|
|
|
const { initIMAfterLogin } = useAccountStore();
|
|
|
|
|
|
|
2026-01-23 16:09:34 +08:00
|
|
|
|
// 聊天输入组件引用
|
|
|
|
|
|
const chatInputRef = ref(null);
|
|
|
|
|
|
|
2026-01-22 15:13:26 +08:00
|
|
|
|
const groupId = ref("");
|
2026-01-26 18:08:01 +08:00
|
|
|
|
const { chatMember, getGroupInfo } = useGroupChat(groupId);
|
2026-01-22 15:13:26 +08:00
|
|
|
|
|
|
|
|
|
|
// 动态设置导航栏标题
|
|
|
|
|
|
const updateNavigationTitle = () => {
|
|
|
|
|
|
uni.setNavigationBarTitle({
|
2026-01-26 18:08:01 +08:00
|
|
|
|
title: "群聊",
|
2026-01-22 15:13:26 +08:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 聊天信息
|
|
|
|
|
|
const chatInfo = ref({
|
|
|
|
|
|
conversationID: "",
|
|
|
|
|
|
userID: "",
|
2026-01-22 16:35:05 +08:00
|
|
|
|
avatar: "/static/home/avatar.svg",
|
2026-01-22 15:13:26 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 评价弹窗状态
|
|
|
|
|
|
const isEvaluationPopupOpen = ref(false);
|
|
|
|
|
|
|
2026-01-26 18:08:01 +08:00
|
|
|
|
// 接受问诊状态
|
|
|
|
|
|
const showConsultAccept = ref(false);
|
|
|
|
|
|
|
|
|
|
|
|
// 拒绝原因对话框状态
|
|
|
|
|
|
const showRejectReasonModal = ref(false);
|
|
|
|
|
|
|
2026-01-22 15:13:26 +08:00
|
|
|
|
// 消息列表相关状态
|
|
|
|
|
|
const messageList = ref([]);
|
|
|
|
|
|
const isLoading = ref(false);
|
|
|
|
|
|
const scrollIntoView = ref("");
|
|
|
|
|
|
|
|
|
|
|
|
// 分页加载相关状态
|
|
|
|
|
|
const isLoadingMore = ref(false);
|
|
|
|
|
|
const isCompleted = ref(false);
|
|
|
|
|
|
const lastFirstMessageId = ref("");
|
|
|
|
|
|
|
|
|
|
|
|
// 判断是否为系统消息
|
|
|
|
|
|
function isSystemMessage(message) {
|
2026-01-26 18:08:01 +08:00
|
|
|
|
if (message.type !== "TIMCustomElem") {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 检查 payload.data 是否包含系统消息标记
|
|
|
|
|
|
if (message.payload?.data) {
|
2026-01-27 13:42:59 +08:00
|
|
|
|
const data =
|
|
|
|
|
|
typeof message.payload.data === "string"
|
|
|
|
|
|
? JSON.parse(message.payload.data)
|
|
|
|
|
|
: message.payload.data;
|
2026-01-26 18:08:01 +08:00
|
|
|
|
|
|
|
|
|
|
// 检查是否为系统消息类型
|
2026-01-27 13:42:59 +08:00
|
|
|
|
if (data.type === "system_message") {
|
2026-01-26 18:08:01 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查 description 是否为系统消息标记
|
2026-01-27 13:42:59 +08:00
|
|
|
|
if (message.payload?.description === "系统消息标记") {
|
2026-01-26 18:08:01 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 兼容旧的系统消息格式
|
2026-01-27 13:42:59 +08:00
|
|
|
|
if (message.payload?.description === "SYSTEM_NOTIFICATION") {
|
2026-01-26 18:08:01 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
2026-01-27 13:42:59 +08:00
|
|
|
|
console.error("判断系统消息失败:", error);
|
2026-01-26 18:08:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否有待接诊的系统消息
|
|
|
|
|
|
function checkConsultPendingStatus() {
|
|
|
|
|
|
// 查找最新的系统消息
|
|
|
|
|
|
for (let i = messageList.value.length - 1; i >= 0; i--) {
|
|
|
|
|
|
const message = messageList.value[i];
|
2026-01-27 13:42:59 +08:00
|
|
|
|
|
2026-01-26 18:08:01 +08:00
|
|
|
|
if (message.type === "TIMCustomElem" && message.payload?.data) {
|
|
|
|
|
|
try {
|
2026-01-27 13:42:59 +08:00
|
|
|
|
const data =
|
|
|
|
|
|
typeof message.payload.data === "string"
|
|
|
|
|
|
? JSON.parse(message.payload.data)
|
|
|
|
|
|
: message.payload.data;
|
|
|
|
|
|
|
2026-01-26 18:08:01 +08:00
|
|
|
|
// 如果是 consult_pending 类型的系统消息,显示接受组件
|
2026-01-27 13:42:59 +08:00
|
|
|
|
if (
|
|
|
|
|
|
data.type === "system_message" &&
|
|
|
|
|
|
data.messageType === "consult_pending"
|
|
|
|
|
|
) {
|
2026-01-26 18:08:01 +08:00
|
|
|
|
showConsultAccept.value = true;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-01-27 13:42:59 +08:00
|
|
|
|
|
|
|
|
|
|
// 如果是其他系统消息类型(如已接诊、已结束、已拒绝等),隐藏接受组件
|
|
|
|
|
|
if (
|
|
|
|
|
|
data.type === "system_message" &&
|
|
|
|
|
|
(data.messageType === "consult_accepted" ||
|
|
|
|
|
|
data.messageType === "consult_ended" ||
|
|
|
|
|
|
data.messageType === "consult_rejected")
|
|
|
|
|
|
) {
|
2026-01-26 18:08:01 +08:00
|
|
|
|
showConsultAccept.value = false;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
2026-01-27 13:42:59 +08:00
|
|
|
|
console.error("解析系统消息失败:", error);
|
2026-01-26 18:08:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-27 13:42:59 +08:00
|
|
|
|
|
2026-01-26 18:08:01 +08:00
|
|
|
|
// 如果没有找到相关系统消息,隐藏接受组件
|
|
|
|
|
|
showConsultAccept.value = false;
|
2026-01-22 15:13:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取消息气泡样式类
|
|
|
|
|
|
function getBubbleClass(message) {
|
2026-01-22 16:35:05 +08:00
|
|
|
|
// 图片消息不需要气泡背景
|
|
|
|
|
|
if (message.type === "TIMImageElem") {
|
|
|
|
|
|
return "image-bubble";
|
|
|
|
|
|
}
|
2026-01-22 15:13:26 +08:00
|
|
|
|
if (message.type === "TIMCustomElem") {
|
2026-01-27 15:07:39 +08:00
|
|
|
|
return message.flow === "out" ? "" : "";
|
2026-01-22 15:13:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
return message.flow === "out" ? "user-bubble" : "doctor-bubble";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 页面加载
|
|
|
|
|
|
onLoad((options) => {
|
|
|
|
|
|
groupId.value = options.groupID || "";
|
|
|
|
|
|
messageList.value = [];
|
|
|
|
|
|
isLoading.value = false;
|
|
|
|
|
|
if (options.conversationID) {
|
|
|
|
|
|
chatInfo.value.conversationID = options.conversationID;
|
|
|
|
|
|
timChatManager.setConversationID(options.conversationID);
|
|
|
|
|
|
console.log("设置当前会话ID:", options.conversationID);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (options.userID) {
|
|
|
|
|
|
chatInfo.value.userID = options.userID;
|
|
|
|
|
|
}
|
2026-01-27 13:42:59 +08:00
|
|
|
|
|
2026-01-22 15:13:26 +08:00
|
|
|
|
checkLoginAndInitTIM();
|
|
|
|
|
|
updateNavigationTitle();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 检查登录状态并初始化IM
|
|
|
|
|
|
const checkLoginAndInitTIM = async () => {
|
|
|
|
|
|
if (!isIMInitialized.value) {
|
|
|
|
|
|
uni.showLoading({
|
|
|
|
|
|
title: "连接中...",
|
|
|
|
|
|
});
|
|
|
|
|
|
const success = await initIMAfterLogin(openid.value);
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
if (!success) {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: "IM连接失败,请重试",
|
|
|
|
|
|
icon: "none",
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (!timChatManager.isLoggedIn) {
|
|
|
|
|
|
uni.showLoading({
|
|
|
|
|
|
title: "重连中...",
|
|
|
|
|
|
});
|
|
|
|
|
|
const reconnected = await timChatManager.ensureIMConnection();
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
if (!reconnected) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
initTIMCallbacks();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化IM回调函数
|
|
|
|
|
|
const initTIMCallbacks = async () => {
|
|
|
|
|
|
timChatManager.setCallback("onSDKReady", () => {
|
|
|
|
|
|
if (messageList.value.length === 0 && !isLoading.value) {
|
|
|
|
|
|
loadMessageList();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
timChatManager.setCallback("onSDKNotReady", () => {});
|
|
|
|
|
|
|
|
|
|
|
|
timChatManager.setCallback("onMessageReceived", (message) => {
|
|
|
|
|
|
console.log("页面收到消息:", {
|
|
|
|
|
|
messageID: message.ID,
|
|
|
|
|
|
conversationID: message.conversationID,
|
|
|
|
|
|
currentConversationID: chatInfo.value.conversationID,
|
|
|
|
|
|
type: message.type,
|
|
|
|
|
|
flow: message.flow,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 验证消息属于当前群聊
|
|
|
|
|
|
if (message.conversationID !== chatInfo.value.conversationID) {
|
|
|
|
|
|
console.log("⚠️ 消息不属于当前群聊,已过滤");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查消息是否已存在
|
|
|
|
|
|
const existingMessage = messageList.value.find(
|
|
|
|
|
|
(msg) => msg.ID === message.ID
|
|
|
|
|
|
);
|
|
|
|
|
|
if (!existingMessage) {
|
|
|
|
|
|
messageList.value.push(message);
|
|
|
|
|
|
console.log("✓ 添加消息到列表,当前消息数量:", messageList.value.length);
|
2026-01-27 13:42:59 +08:00
|
|
|
|
|
2026-01-26 18:08:01 +08:00
|
|
|
|
// 检查是否有待接诊的系统消息
|
|
|
|
|
|
checkConsultPendingStatus();
|
2026-01-27 13:42:59 +08:00
|
|
|
|
|
2026-01-22 16:35:05 +08:00
|
|
|
|
// 立即滚动到底部,不使用延迟
|
2026-01-22 15:13:26 +08:00
|
|
|
|
nextTick(() => {
|
2026-01-22 16:35:05 +08:00
|
|
|
|
scrollToBottom(true);
|
2026-01-22 15:13:26 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
timChatManager.setCallback("onMessageSent", (data) => {
|
|
|
|
|
|
const { messageID, status } = data;
|
|
|
|
|
|
const message = messageList.value.find((msg) => msg.ID === messageID);
|
|
|
|
|
|
if (message) {
|
|
|
|
|
|
message.status = status;
|
|
|
|
|
|
console.log("更新消息状态:", messageID, status);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
timChatManager.setCallback("onMessageListLoaded", (data) => {
|
|
|
|
|
|
console.log("【onMessageListLoaded】收到消息列表回调");
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
|
|
|
|
|
|
let messages = [];
|
|
|
|
|
|
if (typeof data === "object" && data.messages) {
|
|
|
|
|
|
messages = data.messages;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
messages = data;
|
|
|
|
|
|
}
|
|
|
|
|
|
isLoading.value = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 去重处理 + 严格过滤当前群聊的消息
|
|
|
|
|
|
const uniqueMessages = [];
|
|
|
|
|
|
const seenIds = new Set();
|
|
|
|
|
|
|
|
|
|
|
|
messages.forEach((message) => {
|
|
|
|
|
|
const belongsToCurrentConversation =
|
|
|
|
|
|
message.conversationID === chatInfo.value.conversationID;
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
message.ID &&
|
|
|
|
|
|
!seenIds.has(message.ID) &&
|
|
|
|
|
|
belongsToCurrentConversation
|
|
|
|
|
|
) {
|
|
|
|
|
|
seenIds.add(message.ID);
|
|
|
|
|
|
uniqueMessages.push(message);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
messageList.value = uniqueMessages;
|
|
|
|
|
|
console.log(
|
|
|
|
|
|
"消息列表已更新,原始",
|
|
|
|
|
|
messages.length,
|
|
|
|
|
|
"条,过滤后",
|
|
|
|
|
|
uniqueMessages.length,
|
|
|
|
|
|
"条消息"
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
isCompleted.value = data.isCompleted || false;
|
|
|
|
|
|
isLoadingMore.value = false;
|
|
|
|
|
|
|
2026-01-26 18:08:01 +08:00
|
|
|
|
// 检查是否有待接诊的系统消息
|
|
|
|
|
|
checkConsultPendingStatus();
|
|
|
|
|
|
|
2026-01-22 15:13:26 +08:00
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
if (data.isRefresh) {
|
|
|
|
|
|
console.log("后台刷新完成,保持当前滚动位置");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (data.isPullUp && lastFirstMessageId.value) {
|
|
|
|
|
|
console.log(
|
|
|
|
|
|
"上拉加载完成,定位到加载前的第一条消息:",
|
|
|
|
|
|
lastFirstMessageId.value
|
|
|
|
|
|
);
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
scrollIntoView.value = `msg-${lastFirstMessageId.value}`;
|
|
|
|
|
|
lastFirstMessageId.value = "";
|
|
|
|
|
|
}, 100);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!data.isPullUp) {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
scrollToBottom();
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
scrollToBottom();
|
|
|
|
|
|
}, 100);
|
|
|
|
|
|
}, 200);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
timChatManager.setCallback("onError", (error) => {
|
|
|
|
|
|
console.error("TIM错误:", error);
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: error,
|
|
|
|
|
|
icon: "none",
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
if (timChatManager.tim && timChatManager.isLoggedIn) {
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
loadMessageList();
|
|
|
|
|
|
}, 50);
|
|
|
|
|
|
} else if (timChatManager.tim) {
|
|
|
|
|
|
let checkCount = 0;
|
|
|
|
|
|
const checkInterval = setInterval(() => {
|
|
|
|
|
|
checkCount++;
|
|
|
|
|
|
if (timChatManager.isLoggedIn) {
|
|
|
|
|
|
clearInterval(checkInterval);
|
|
|
|
|
|
loadMessageList();
|
|
|
|
|
|
} else if (checkCount > 10) {
|
|
|
|
|
|
clearInterval(checkInterval);
|
|
|
|
|
|
showMessage("IM登录超时,请重新进入");
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 1000);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 加载消息列表
|
|
|
|
|
|
const loadMessageList = () => {
|
|
|
|
|
|
if (isLoading.value) {
|
|
|
|
|
|
console.log("正在加载中,跳过重复加载");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log(
|
|
|
|
|
|
"【loadMessageList】开始加载消息,会话ID:",
|
|
|
|
|
|
chatInfo.value.conversationID
|
|
|
|
|
|
);
|
|
|
|
|
|
isLoading.value = true;
|
|
|
|
|
|
|
|
|
|
|
|
uni.showLoading({
|
|
|
|
|
|
title: "加载中...",
|
|
|
|
|
|
mask: false,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
timChatManager.enterConversation(chatInfo.value.conversationID || "test1");
|
|
|
|
|
|
|
|
|
|
|
|
// 标记会话为已读
|
|
|
|
|
|
if (
|
|
|
|
|
|
timChatManager.tim &&
|
|
|
|
|
|
timChatManager.isLoggedIn &&
|
|
|
|
|
|
chatInfo.value.conversationID
|
|
|
|
|
|
) {
|
|
|
|
|
|
timChatManager.tim
|
|
|
|
|
|
.setMessageRead({
|
|
|
|
|
|
conversationID: chatInfo.value.conversationID,
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(() => {
|
|
|
|
|
|
console.log("会话已标记为已读:", chatInfo.value.conversationID);
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
|
console.error("标记会话已读失败:", error);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 语音播放相关
|
|
|
|
|
|
let currentAudioContext = null;
|
|
|
|
|
|
const playingVoiceId = ref(null);
|
|
|
|
|
|
|
|
|
|
|
|
// 播放语音
|
|
|
|
|
|
const playVoice = (message) => {
|
|
|
|
|
|
if (playingVoiceId.value === message.ID && currentAudioContext) {
|
|
|
|
|
|
currentAudioContext.stop();
|
|
|
|
|
|
currentAudioContext.destroy();
|
|
|
|
|
|
currentAudioContext = null;
|
|
|
|
|
|
playingVoiceId.value = null;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (currentAudioContext) {
|
|
|
|
|
|
currentAudioContext.stop();
|
|
|
|
|
|
currentAudioContext.destroy();
|
|
|
|
|
|
currentAudioContext = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const voiceUrl = getVoiceUrl(message);
|
|
|
|
|
|
if (!validateVoiceUrl(voiceUrl)) return;
|
|
|
|
|
|
|
|
|
|
|
|
playingVoiceId.value = message.ID;
|
|
|
|
|
|
|
|
|
|
|
|
currentAudioContext = createAudioContext(voiceUrl);
|
|
|
|
|
|
currentAudioContext.onEnded(() => {
|
|
|
|
|
|
currentAudioContext = null;
|
|
|
|
|
|
playingVoiceId.value = null;
|
|
|
|
|
|
});
|
|
|
|
|
|
currentAudioContext.onError(() => {
|
|
|
|
|
|
currentAudioContext = null;
|
|
|
|
|
|
playingVoiceId.value = null;
|
|
|
|
|
|
});
|
|
|
|
|
|
currentAudioContext.play();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 滚动到底部
|
2026-01-22 16:35:05 +08:00
|
|
|
|
const scrollToBottom = (immediate = false) => {
|
2026-01-22 15:13:26 +08:00
|
|
|
|
if (messageList.value.length > 0) {
|
|
|
|
|
|
const lastMessage = messageList.value[messageList.value.length - 1];
|
2026-01-22 16:35:05 +08:00
|
|
|
|
const targetId = `msg-${lastMessage.ID}`;
|
2026-01-26 18:08:01 +08:00
|
|
|
|
|
2026-01-22 16:35:05 +08:00
|
|
|
|
if (immediate) {
|
|
|
|
|
|
// 立即滚动:先清空再设置,触发滚动
|
2026-01-26 18:08:01 +08:00
|
|
|
|
scrollIntoView.value = "";
|
2026-01-22 16:35:05 +08:00
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
scrollIntoView.value = targetId;
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 正常滚动,使用短延迟确保DOM更新
|
2026-01-26 18:08:01 +08:00
|
|
|
|
scrollIntoView.value = "";
|
2026-01-22 16:35:05 +08:00
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
scrollIntoView.value = targetId;
|
|
|
|
|
|
}, 50);
|
|
|
|
|
|
}
|
2026-01-22 15:13:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭功能栏
|
|
|
|
|
|
const closeMorePanel = () => {
|
|
|
|
|
|
uni.$emit("closeMorePanel");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 滚动事件
|
|
|
|
|
|
const onScroll = throttle((e) => {
|
|
|
|
|
|
// 滚动处理
|
|
|
|
|
|
}, 100);
|
|
|
|
|
|
|
|
|
|
|
|
// 处理上滑加载更多
|
|
|
|
|
|
const handleScrollToUpper = async () => {
|
|
|
|
|
|
console.log("【handleScrollToUpper】触发上滑事件,准备加载更多");
|
|
|
|
|
|
console.log(
|
|
|
|
|
|
" 当前状态: isLoadingMore=",
|
|
|
|
|
|
isLoadingMore.value,
|
|
|
|
|
|
"isCompleted=",
|
|
|
|
|
|
isCompleted.value
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (isLoadingMore.value || isCompleted.value) {
|
|
|
|
|
|
console.log(
|
|
|
|
|
|
" ⏭️ 跳过加载:isLoadingMore=",
|
|
|
|
|
|
isLoadingMore.value,
|
|
|
|
|
|
"isCompleted=",
|
|
|
|
|
|
isCompleted.value
|
|
|
|
|
|
);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (messageList.value.length < 15) {
|
|
|
|
|
|
console.log(" ⏭️ 消息数量不足15条,跳过加载更多");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (messageList.value.length > 0) {
|
|
|
|
|
|
lastFirstMessageId.value = messageList.value[0].ID;
|
|
|
|
|
|
console.log(" 📍 记录当前第一条消息ID:", lastFirstMessageId.value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isLoadingMore.value = true;
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
console.log(" 📡 调用 timChatManager.loadMoreMessages()");
|
|
|
|
|
|
const result = await timChatManager.loadMoreMessages();
|
|
|
|
|
|
|
|
|
|
|
|
console.log(" 📥 加载结果:", result);
|
|
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
|
console.log(` ✅ 加载更多成功,新增 ${result.count} 条消息`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log(" ⚠️ 加载失败:", result.message || result.error);
|
|
|
|
|
|
if (result.message === "已加载全部消息") {
|
|
|
|
|
|
console.log(" ✅ 已加载全部消息");
|
|
|
|
|
|
isCompleted.value = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error(" ❌ 加载更多失败:", error);
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: "加载失败,请重试",
|
|
|
|
|
|
icon: "none",
|
|
|
|
|
|
});
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
isLoadingMore.value = false;
|
|
|
|
|
|
console.log(" 🏁 加载完成,isLoadingMore 设置为 false");
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 页面显示
|
|
|
|
|
|
onShow(() => {
|
|
|
|
|
|
if (!account.value || !openid.value) {
|
|
|
|
|
|
uni.redirectTo({
|
|
|
|
|
|
url: "/pages-center/login/login",
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!isIMInitialized.value) {
|
|
|
|
|
|
checkLoginAndInitTIM();
|
|
|
|
|
|
} else if (timChatManager.tim && !timChatManager.isLoggedIn) {
|
|
|
|
|
|
timChatManager.ensureIMConnection();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
startIMMonitoring(30000);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 页面隐藏
|
|
|
|
|
|
onHide(() => {
|
|
|
|
|
|
stopIMMonitoring();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-01-27 15:07:39 +08:00
|
|
|
|
|
2026-01-23 16:09:34 +08:00
|
|
|
|
const sendCommonPhrase = (content) => {
|
|
|
|
|
|
if (chatInputRef.value) {
|
|
|
|
|
|
chatInputRef.value.sendTextMessageFromPhrase(content);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
// 暴露方法给常用语页面调用
|
|
|
|
|
|
defineExpose({
|
2026-01-26 18:08:01 +08:00
|
|
|
|
sendCommonPhrase,
|
2026-01-23 16:09:34 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-01-26 18:08:01 +08:00
|
|
|
|
// 处理接受问诊
|
|
|
|
|
|
const handleAcceptConsult = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
uni.showLoading({
|
2026-01-27 13:42:59 +08:00
|
|
|
|
title: "处理中...",
|
2026-01-26 18:08:01 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 发送接受问诊的系统消息
|
|
|
|
|
|
const customMessage = {
|
|
|
|
|
|
data: JSON.stringify({
|
2026-01-27 13:42:59 +08:00
|
|
|
|
type: "system_message",
|
|
|
|
|
|
messageType: "consult_accepted",
|
|
|
|
|
|
content: "医生已接诊",
|
2026-01-26 18:08:01 +08:00
|
|
|
|
timestamp: Date.now(),
|
|
|
|
|
|
}),
|
2026-01-27 13:42:59 +08:00
|
|
|
|
description: "系统消息标记",
|
|
|
|
|
|
extension: "",
|
2026-01-26 18:08:01 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const message = timChatManager.tim.createCustomMessage({
|
2026-01-27 13:42:59 +08:00
|
|
|
|
to: chatInfo.value.conversationID.replace("GROUP", ""),
|
2026-01-26 18:08:01 +08:00
|
|
|
|
conversationType: timChatManager.TIM.TYPES.CONV_GROUP,
|
|
|
|
|
|
payload: customMessage,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const sendResult = await timChatManager.tim.sendMessage(message);
|
|
|
|
|
|
|
|
|
|
|
|
if (sendResult.code === 0) {
|
|
|
|
|
|
showConsultAccept.value = false;
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
uni.showToast({
|
2026-01-27 13:42:59 +08:00
|
|
|
|
title: "已接受问诊",
|
|
|
|
|
|
icon: "success",
|
2026-01-26 18:08:01 +08:00
|
|
|
|
});
|
|
|
|
|
|
} else {
|
2026-01-27 13:42:59 +08:00
|
|
|
|
throw new Error(sendResult.message || "发送失败");
|
2026-01-26 18:08:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
2026-01-27 13:42:59 +08:00
|
|
|
|
console.error("接受问诊失败:", error);
|
2026-01-26 18:08:01 +08:00
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
uni.showToast({
|
2026-01-27 13:42:59 +08:00
|
|
|
|
title: error.message || "操作失败",
|
|
|
|
|
|
icon: "none",
|
2026-01-26 18:08:01 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 处理拒绝问诊
|
|
|
|
|
|
const handleRejectConsult = () => {
|
|
|
|
|
|
// 显示拒绝原因选择对话框
|
|
|
|
|
|
showRejectReasonModal.value = true;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 处理拒绝原因确认
|
|
|
|
|
|
const handleRejectReasonConfirm = async (reason) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
showRejectReasonModal.value = false;
|
2026-01-27 13:42:59 +08:00
|
|
|
|
|
2026-01-26 18:08:01 +08:00
|
|
|
|
uni.showLoading({
|
2026-01-27 13:42:59 +08:00
|
|
|
|
title: "处理中...",
|
2026-01-26 18:08:01 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 获取医生信息
|
2026-01-27 13:42:59 +08:00
|
|
|
|
const memberName = account.value?.name || "医生";
|
|
|
|
|
|
|
2026-01-26 18:08:01 +08:00
|
|
|
|
// 获取群组ID
|
2026-01-27 13:42:59 +08:00
|
|
|
|
const groupId = chatInfo.value.conversationID.replace("GROUP", "");
|
2026-01-26 18:08:01 +08:00
|
|
|
|
|
|
|
|
|
|
// 调用后端接口发送拒绝消息
|
|
|
|
|
|
const result = await sendConsultRejectedMessage({
|
|
|
|
|
|
groupId,
|
|
|
|
|
|
memberName,
|
|
|
|
|
|
reason,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
|
showConsultAccept.value = false;
|
|
|
|
|
|
uni.showToast({
|
2026-01-27 13:42:59 +08:00
|
|
|
|
title: "已拒绝问诊",
|
|
|
|
|
|
icon: "success",
|
2026-01-26 18:08:01 +08:00
|
|
|
|
});
|
|
|
|
|
|
} else {
|
2026-01-27 13:42:59 +08:00
|
|
|
|
throw new Error(result.message || "发送失败");
|
2026-01-26 18:08:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
2026-01-27 13:42:59 +08:00
|
|
|
|
console.error("拒绝问诊失败:", error);
|
2026-01-26 18:08:01 +08:00
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
uni.showToast({
|
2026-01-27 13:42:59 +08:00
|
|
|
|
title: error.message || "操作失败",
|
|
|
|
|
|
icon: "none",
|
2026-01-26 18:08:01 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 处理拒绝原因取消
|
|
|
|
|
|
const handleRejectReasonCancel = () => {
|
|
|
|
|
|
showRejectReasonModal.value = false;
|
|
|
|
|
|
};
|
2026-01-27 13:42:59 +08:00
|
|
|
|
// 处理结束问诊
|
|
|
|
|
|
const handleEndConsult = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
uni.showLoading({
|
|
|
|
|
|
title: "处理中...",
|
|
|
|
|
|
});
|
|
|
|
|
|
// 调用结束问诊接口,直接传入 groupId
|
|
|
|
|
|
const result = await endConsultation({
|
|
|
|
|
|
groupId: groupId.value,
|
|
|
|
|
|
adminAccount: account.value?.userId || "",
|
|
|
|
|
|
extraData: {
|
|
|
|
|
|
endBy: account.value?.userId || "",
|
|
|
|
|
|
endByName: account.value?.name || "医生",
|
|
|
|
|
|
endReason: "问诊完成",
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
if (result.success) {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: "问诊已结束",
|
|
|
|
|
|
icon: "success",
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
throw new Error(result.message || "操作失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("结束问诊失败:", error);
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: error.message || "操作失败",
|
|
|
|
|
|
icon: "none",
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-01-22 15:13:26 +08:00
|
|
|
|
// 页面卸载
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
clearMessageCache();
|
|
|
|
|
|
|
|
|
|
|
|
timChatManager.setCallback("onSDKReady", null);
|
|
|
|
|
|
timChatManager.setCallback("onSDKNotReady", null);
|
|
|
|
|
|
timChatManager.setCallback("onMessageReceived", null);
|
|
|
|
|
|
timChatManager.setCallback("onMessageListLoaded", null);
|
|
|
|
|
|
timChatManager.setCallback("onError", null);
|
2026-01-26 18:08:01 +08:00
|
|
|
|
// 移除问卷发送监听
|
|
|
|
|
|
uni.$off("sendSurvey");
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 监听问卷发送事件
|
|
|
|
|
|
uni.$on("sendSurvey", async (data) => {
|
|
|
|
|
|
const { survey, corpId, userId, sendSurveyId } = data;
|
|
|
|
|
|
|
|
|
|
|
|
if (!survey || !survey._id) {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: "问卷信息不完整",
|
|
|
|
|
|
icon: "none",
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取环境变量
|
|
|
|
|
|
const env = __VITE_ENV__;
|
|
|
|
|
|
const baseUrl = env.VITE_PATIENT_PAGE_BASE_URL || "";
|
|
|
|
|
|
const surveyUrl = env.VITE_SURVEY_URL || "";
|
|
|
|
|
|
|
|
|
|
|
|
// 获取客户信息
|
|
|
|
|
|
const customerId = chatInfo.value.userID || "";
|
|
|
|
|
|
const customerName = chatInfo.value.customerName || "";
|
|
|
|
|
|
|
|
|
|
|
|
// 创建问卷记录
|
|
|
|
|
|
const { createSurveyRecord } = await import("@/utils/api.js");
|
|
|
|
|
|
const recordRes = await createSurveyRecord({
|
|
|
|
|
|
corpId,
|
|
|
|
|
|
userId,
|
|
|
|
|
|
surveryId: survey._id,
|
|
|
|
|
|
memberId: customerId,
|
|
|
|
|
|
customer: customerName,
|
|
|
|
|
|
sendSurveyId,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!recordRes.success) {
|
|
|
|
|
|
throw new Error(recordRes.message || "创建问卷记录失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const answerId = recordRes.data?.id || "";
|
|
|
|
|
|
|
|
|
|
|
|
// 构建问卷链接
|
|
|
|
|
|
let surveyLink = "";
|
|
|
|
|
|
if (survey.createBy === "system") {
|
|
|
|
|
|
// 系统问卷
|
|
|
|
|
|
surveyLink = `${surveyUrl}?corpId=${corpId}&surveyId=${survey.surveyId}&memberId=${customerId}&sendSurveyId=${sendSurveyId}&userId=${userId}`;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 自定义问卷
|
|
|
|
|
|
surveyLink = `${baseUrl}pages/survery/fill?corpId=${corpId}&surveryId=${
|
|
|
|
|
|
survey._id
|
|
|
|
|
|
}&memberId=${customerId}&answerId=${answerId}&name=${encodeURIComponent(
|
|
|
|
|
|
customerName
|
|
|
|
|
|
)}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建自定义消息
|
|
|
|
|
|
const customMessage = {
|
|
|
|
|
|
data: JSON.stringify({
|
|
|
|
|
|
type: "survey",
|
|
|
|
|
|
title: survey.name || "填写问卷",
|
|
|
|
|
|
desc: "请填写问卷",
|
|
|
|
|
|
url: surveyLink,
|
|
|
|
|
|
imgUrl:
|
|
|
|
|
|
"https://796f-youcan-clouddev-1-8ewcqf31dbb2b5-1317294507.tcb.qcloud.la/other/19-%E9%97%AE%E5%8D%B7.png?sign=55a4cd77c418b2c548b65792a2cf6bce&t=1701328694",
|
|
|
|
|
|
}),
|
|
|
|
|
|
description: "SURVEY",
|
|
|
|
|
|
extension: "",
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 发送自定义消息
|
|
|
|
|
|
const message = timChatManager.tim.createCustomMessage({
|
|
|
|
|
|
to: chatInfo.value.conversationID.replace("GROUP", ""),
|
|
|
|
|
|
conversationType: timChatManager.TIM.TYPES.CONV_GROUP,
|
|
|
|
|
|
payload: customMessage,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const sendResult = await timChatManager.tim.sendMessage(message);
|
|
|
|
|
|
|
|
|
|
|
|
if (sendResult.code === 0) {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: "发送成功",
|
|
|
|
|
|
icon: "success",
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
throw new Error(sendResult.message || "发送失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("发送问卷失败:", error);
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: error.message || "发送失败",
|
|
|
|
|
|
icon: "none",
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-01-22 15:13:26 +08:00
|
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
@import "./chat.scss";
|
|
|
|
|
|
</style>
|