feat: 支持回访内容跳转到会话间发送
This commit is contained in:
parent
eead7920dc
commit
cfa15a445b
@ -334,18 +334,26 @@ async function fetchArchive() {
|
|||||||
await ensureDoctor();
|
await ensureDoctor();
|
||||||
loading('加载中...');
|
loading('加载中...');
|
||||||
try {
|
try {
|
||||||
|
const prevLocalChatGroupId = normalizeGroupId(chatGroupId.value || archive.value.chatGroupId || '');
|
||||||
const res = await api('getCustomerByCustomerId', { customerId: archiveId.value });
|
const res = await api('getCustomerByCustomerId', { customerId: archiveId.value });
|
||||||
if (!res?.success) {
|
if (!res?.success) {
|
||||||
toast(res?.message || '获取档案失败');
|
toast(res?.message || '获取档案失败');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
archive.value = { ...archive.value, ...normalizeArchiveFromApi(res.data) };
|
|
||||||
|
const normalized = normalizeArchiveFromApi(res.data);
|
||||||
|
// 后端经常不返回 chatGroupId(或为空),但本地已探测出可用群聊时不能被覆盖掉,否则返回页面会导致“去聊天”按钮短暂/持续消失
|
||||||
|
if (!normalized.chatGroupId && prevLocalChatGroupId) {
|
||||||
|
normalized.chatGroupId = prevLocalChatGroupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
archive.value = { ...archive.value, ...normalized };
|
||||||
saveToStorage();
|
saveToStorage();
|
||||||
loadTeamMembers();
|
loadTeamMembers();
|
||||||
await fetchTeamGroups(true);
|
await fetchTeamGroups(true);
|
||||||
chatGroupId.value = normalizeGroupId(archive.value.chatGroupId || '');
|
chatGroupId.value = normalizeGroupId(archive.value.chatGroupId || '');
|
||||||
if (!chatGroupId.value) {
|
if (!chatGroupId.value) {
|
||||||
refreshChatRoom();
|
await refreshChatRoom({ force: true });
|
||||||
} else {
|
} else {
|
||||||
const meta = await getChatRoomMeta(chatGroupId.value);
|
const meta = await getChatRoomMeta(chatGroupId.value);
|
||||||
const ok = isChatRoomForArchive(meta, archiveId.value);
|
const ok = isChatRoomForArchive(meta, archiveId.value);
|
||||||
@ -354,7 +362,7 @@ async function fetchArchive() {
|
|||||||
if (!ok || (currentTeamId && metaTeamId && metaTeamId !== currentTeamId)) {
|
if (!ok || (currentTeamId && metaTeamId && metaTeamId !== currentTeamId)) {
|
||||||
chatGroupId.value = '';
|
chatGroupId.value = '';
|
||||||
archive.value.chatGroupId = '';
|
archive.value.chatGroupId = '';
|
||||||
refreshChatRoom();
|
await refreshChatRoom({ force: true });
|
||||||
} else {
|
} else {
|
||||||
archive.value.chatGroupId = chatGroupId.value;
|
archive.value.chatGroupId = chatGroupId.value;
|
||||||
}
|
}
|
||||||
@ -618,7 +626,7 @@ const goEdit = () => {
|
|||||||
const goChat = async () => {
|
const goChat = async () => {
|
||||||
let gid = normalizeGroupId(currentChatGroupId.value || '');
|
let gid = normalizeGroupId(currentChatGroupId.value || '');
|
||||||
if (!gid) {
|
if (!gid) {
|
||||||
await refreshChatRoom();
|
await refreshChatRoom({ force: true });
|
||||||
gid = normalizeGroupId(currentChatGroupId.value || '');
|
gid = normalizeGroupId(currentChatGroupId.value || '');
|
||||||
}
|
}
|
||||||
if (!gid) {
|
if (!gid) {
|
||||||
@ -634,7 +642,7 @@ const goChat = async () => {
|
|||||||
chatGroupId.value = '';
|
chatGroupId.value = '';
|
||||||
archive.value.chatGroupId = '';
|
archive.value.chatGroupId = '';
|
||||||
saveToStorage();
|
saveToStorage();
|
||||||
await refreshChatRoom();
|
await refreshChatRoom({ force: true });
|
||||||
gid = normalizeGroupId(currentChatGroupId.value || '');
|
gid = normalizeGroupId(currentChatGroupId.value || '');
|
||||||
}
|
}
|
||||||
if (!gid) {
|
if (!gid) {
|
||||||
@ -650,6 +658,7 @@ const goChat = async () => {
|
|||||||
|
|
||||||
const isRefreshingChatRoom = ref(false);
|
const isRefreshingChatRoom = ref(false);
|
||||||
let lastRefreshChatRoomAt = 0;
|
let lastRefreshChatRoomAt = 0;
|
||||||
|
let refreshChatRoomPromise = null;
|
||||||
|
|
||||||
function isChatRoomForArchive(meta, customerId) {
|
function isChatRoomForArchive(meta, customerId) {
|
||||||
const cid = String(meta?.customerId || '');
|
const cid = String(meta?.customerId || '');
|
||||||
@ -681,16 +690,24 @@ function parseAnyTimeMs(v) {
|
|||||||
return d.isValid() ? d.valueOf() : 0;
|
return d.isValid() ? d.valueOf() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshChatRoom() {
|
async function refreshChatRoom(options = {}) {
|
||||||
|
const force = Boolean(options?.force);
|
||||||
const customerId = String(archiveId.value || '');
|
const customerId = String(archiveId.value || '');
|
||||||
if (!customerId) return;
|
if (!customerId) return;
|
||||||
if (isRefreshingChatRoom.value) return;
|
|
||||||
|
// 如果正在刷新,复用同一个 promise,确保调用方可以 await 到结果
|
||||||
|
if (isRefreshingChatRoom.value && refreshChatRoomPromise) {
|
||||||
|
await refreshChatRoomPromise;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (now - lastRefreshChatRoomAt < 5000) return;
|
if (!force && now - lastRefreshChatRoomAt < 5000) return;
|
||||||
lastRefreshChatRoomAt = now;
|
lastRefreshChatRoomAt = now;
|
||||||
|
|
||||||
isRefreshingChatRoom.value = true;
|
isRefreshingChatRoom.value = true;
|
||||||
try {
|
refreshChatRoomPromise = (async () => {
|
||||||
|
try {
|
||||||
const corpId = getCorpId();
|
const corpId = getCorpId();
|
||||||
const teamId = getCurrentTeamId();
|
const teamId = getCurrentTeamId();
|
||||||
|
|
||||||
@ -742,11 +759,15 @@ async function refreshChatRoom() {
|
|||||||
chatGroupId.value = '';
|
chatGroupId.value = '';
|
||||||
archive.value.chatGroupId = '';
|
archive.value.chatGroupId = '';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore
|
// ignore
|
||||||
} finally {
|
} finally {
|
||||||
isRefreshingChatRoom.value = false;
|
isRefreshingChatRoom.value = false;
|
||||||
}
|
refreshChatRoomPromise = null;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
await refreshChatRoomPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeCall = () => {
|
const makeCall = () => {
|
||||||
|
|||||||
@ -90,9 +90,9 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<button
|
<button
|
||||||
v-if="fromChat"
|
v-if="canShowSendButton(i)"
|
||||||
class="action-btn send-btn"
|
class="action-btn send-btn"
|
||||||
@click.stop="sendFollowUp(i)"
|
@click.stop="goChatAndSend(i)"
|
||||||
>
|
>
|
||||||
发送
|
发送
|
||||||
</button>
|
</button>
|
||||||
@ -216,7 +216,6 @@ import dayjs from "dayjs";
|
|||||||
import api from "@/utils/api";
|
import api from "@/utils/api";
|
||||||
import useAccountStore from "@/store/account";
|
import useAccountStore from "@/store/account";
|
||||||
import { toast } from "@/utils/widget";
|
import { toast } from "@/utils/widget";
|
||||||
import { handleFollowUpMessages } from "@/utils/send-message-helper";
|
|
||||||
import {
|
import {
|
||||||
getTodoEventTypeLabel,
|
getTodoEventTypeLabel,
|
||||||
getTodoEventTypeOptions,
|
getTodoEventTypeOptions,
|
||||||
@ -235,12 +234,27 @@ const { account, doctorInfo } = storeToRefs(accountStore);
|
|||||||
const { getDoctorInfo } = accountStore;
|
const { getDoctorInfo } = accountStore;
|
||||||
|
|
||||||
function getUserId() {
|
function getUserId() {
|
||||||
return doctorInfo.value?.userid || "";
|
const d = doctorInfo.value || {};
|
||||||
|
const a = account.value || {};
|
||||||
|
return String(d.userid || d.userId || d.corpUserId || a.userid || a.userId || "") || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCorpId() {
|
function getCorpId() {
|
||||||
const team = uni.getStorageSync("ykt_case_current_team") || {};
|
const team = uni.getStorageSync("ykt_case_current_team") || {};
|
||||||
return team.corpId || doctorInfo.value?.corpId || "";
|
const d = doctorInfo.value || {};
|
||||||
|
const a = account.value || {};
|
||||||
|
return String(d.corpId || a.corpId || team.corpId || "") || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentTeamId() {
|
||||||
|
const team = uni.getStorageSync("ykt_case_current_team") || {};
|
||||||
|
return String(team?.teamId || team?._id || team?.id || "") || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeGroupId(v) {
|
||||||
|
const s = String(v || "").trim();
|
||||||
|
if (!s) return "";
|
||||||
|
return s.startsWith("GROUP") ? s.slice(5) : s;
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusTabs = [
|
const statusTabs = [
|
||||||
@ -270,6 +284,11 @@ const query = reactive({
|
|||||||
const list = ref([]);
|
const list = ref([]);
|
||||||
const total = ref(0);
|
const total = ref(0);
|
||||||
|
|
||||||
|
const chatGroupId = ref("");
|
||||||
|
const currentChatGroupId = computed(() => normalizeGroupId(chatGroupId.value || ""));
|
||||||
|
|
||||||
|
const PENDING_FOLLOWUP_SEND_STORAGE_KEY = "ykt_followup_pending_send";
|
||||||
|
|
||||||
const page = ref(1);
|
const page = ref(1);
|
||||||
const pageSize = 10;
|
const pageSize = 10;
|
||||||
const pages = ref(1);
|
const pages = ref(1);
|
||||||
@ -511,75 +530,163 @@ function toDetail(todo) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendFollowUp(todo) {
|
function hasSendContent(todo) {
|
||||||
if (!todo.sendContent && (!todo.fileList || todo.fileList.length === 0)) {
|
return Boolean(todo?.sendContent) || (Array.isArray(todo?.fileList) && todo.fileList.length > 0);
|
||||||
toast("没有发送内容");
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
function isExecutorMe(todo) {
|
||||||
|
const me = String(getUserId() || "");
|
||||||
|
const executor = String(todo?.executorUserId || "");
|
||||||
|
if (!me || !executor) return false;
|
||||||
|
return me === executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
function canShowSendButton(todo) {
|
||||||
|
if (!hasSendContent(todo)) return false;
|
||||||
|
if (!isExecutorMe(todo)) return false;
|
||||||
|
// 当前患者无会话则不展示
|
||||||
|
return Boolean(currentChatGroupId.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFollowUpMessages(todo) {
|
||||||
const messages = [];
|
const messages = [];
|
||||||
|
if (todo?.sendContent) {
|
||||||
// 1. 发送文字内容
|
messages.push({ type: "text", content: String(todo.sendContent) });
|
||||||
if (todo.sendContent) {
|
|
||||||
messages.push({
|
|
||||||
type: "text",
|
|
||||||
content: todo.sendContent,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
console.log("==============>fileList", todo.fileList);
|
|
||||||
|
|
||||||
// 2. 处理文件列表(图片、宣教文章、问卷)
|
if (Array.isArray(todo?.fileList)) {
|
||||||
if (Array.isArray(todo.fileList)) {
|
|
||||||
for (const file of todo.fileList) {
|
for (const file of todo.fileList) {
|
||||||
if (file.type === "image" && file.URL) {
|
const outerType = String(file?.type || "");
|
||||||
// 发送图片
|
|
||||||
|
let innerFile = file?.file;
|
||||||
|
if (typeof innerFile === "string") {
|
||||||
|
try {
|
||||||
|
innerFile = JSON.parse(innerFile);
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerFile = innerFile && typeof innerFile === "object" ? innerFile : null;
|
||||||
|
|
||||||
|
const innerType = String(innerFile?.type || "");
|
||||||
|
const outerUrl = String(file?.URL || file?.url || "");
|
||||||
|
const innerUrl = String(innerFile?.url || "");
|
||||||
|
|
||||||
|
// 兼容 followup-detail.vue 的判定方式
|
||||||
|
let fileType = "";
|
||||||
|
if (outerType === "image" || innerType.includes("image")) fileType = "image";
|
||||||
|
else if (innerType === "article") fileType = "article";
|
||||||
|
else if (innerType === "questionnaire") fileType = "questionnaire";
|
||||||
|
else fileType = outerType;
|
||||||
|
|
||||||
|
const url = fileType === "article" || fileType === "questionnaire" ? (innerUrl || outerUrl) : (outerUrl || innerUrl);
|
||||||
|
|
||||||
|
if (fileType === "image" && url) {
|
||||||
messages.push({
|
messages.push({
|
||||||
type: "image",
|
type: "image",
|
||||||
content: file.URL,
|
content: url,
|
||||||
name: file.file?.name || file.name || "图片",
|
name: innerFile?.name || file?.name || "图片",
|
||||||
});
|
});
|
||||||
} else if (file.file.type === "article" && file.file?.url) {
|
continue;
|
||||||
// 发送宣教文章 - 从 URL 中解析 id
|
}
|
||||||
const articleId = extractIdFromUrl(file.file.url);
|
|
||||||
|
if (fileType === "article") {
|
||||||
|
const fallbackArticleId = String(innerFile?._id || file?._id || innerFile?.articleId || file?.articleId || "") || "";
|
||||||
|
const extractedId = extractIdFromUrl(url);
|
||||||
|
const articleId = String(extractedId || fallbackArticleId || "");
|
||||||
|
|
||||||
|
// url 兜底:如果后端没给文章跳转链接,则用 articleId+corpId 拼接
|
||||||
|
let articleUrl = String(url || "");
|
||||||
|
if (!articleUrl && articleId) {
|
||||||
|
const corpId = getCorpId();
|
||||||
|
articleUrl = `${__VITE_ENV__?.MP_PATIENT_PAGE_BASE_URL || ""}pages/article/index?id=${encodeURIComponent(
|
||||||
|
articleId
|
||||||
|
)}&corpId=${encodeURIComponent(corpId || "")}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没有 id 和 url 时,跳过
|
||||||
|
if (!articleId && !articleUrl) continue;
|
||||||
|
|
||||||
messages.push({
|
messages.push({
|
||||||
type: "article",
|
type: "article",
|
||||||
content: {
|
content: {
|
||||||
_id: articleId,
|
_id: articleId,
|
||||||
title: file.file?.name || "宣教文章",
|
title: innerFile?.name || file?.name || "宣教文章",
|
||||||
url: file.file?.url || file.URL,
|
url: articleUrl,
|
||||||
subtitle: file.file?.subtitle || "",
|
subtitle: innerFile?.subtitle || "",
|
||||||
cover: file.file?.cover || "",
|
cover: innerFile?.cover || file?.URL || "",
|
||||||
articleId: articleId,
|
articleId: articleId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else if (file.file.type === "questionnaire" && file.file?.surveryId) {
|
continue;
|
||||||
// 发送问卷
|
}
|
||||||
|
|
||||||
|
if (fileType === "questionnaire") {
|
||||||
|
const surveryId = innerFile?.surveryId || file?.surveryId;
|
||||||
|
if (!surveryId) continue;
|
||||||
|
const surveyId = String(innerFile?._id || file?._id || surveryId || "");
|
||||||
messages.push({
|
messages.push({
|
||||||
type: "questionnaire",
|
type: "questionnaire",
|
||||||
content: {
|
content: {
|
||||||
_id: file.file?._id || file._id,
|
_id: surveyId,
|
||||||
name: file.file?.name || file.name || "问卷",
|
name: innerFile?.name || file?.name || "问卷",
|
||||||
surveryId: file.file?.surveryId || file.surveryId,
|
surveryId,
|
||||||
url: file.file?.url || file.URL,
|
url: String(url || ""),
|
||||||
|
createBy: innerFile?.createBy,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用统一的消息发送处理函数
|
return messages;
|
||||||
const success = await handleFollowUpMessages(messages, {
|
}
|
||||||
userId: getUserId(),
|
|
||||||
customerId: props.archiveId,
|
async function goChatAndSend(todo) {
|
||||||
customerName: props.data?.name || "",
|
if (!canShowSendButton(todo)) return;
|
||||||
corpId: getCorpId(),
|
if (!props.archiveId) return;
|
||||||
env: __VITE_ENV__,
|
|
||||||
|
let gid = normalizeGroupId(currentChatGroupId.value || "");
|
||||||
|
if (!gid) {
|
||||||
|
await refreshChatRoom();
|
||||||
|
gid = normalizeGroupId(currentChatGroupId.value || "");
|
||||||
|
}
|
||||||
|
if (!gid) {
|
||||||
|
toast("暂无可进入的会话");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = buildFollowUpMessages(todo);
|
||||||
|
if (!messages.length) {
|
||||||
|
console.warn("[followup] buildFollowUpMessages empty:", {
|
||||||
|
sendContent: todo?.sendContent,
|
||||||
|
fileList: todo?.fileList,
|
||||||
|
});
|
||||||
|
toast("发送内容解析失败");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const conversationID = `GROUP${gid}`;
|
||||||
|
|
||||||
|
uni.setStorageSync(PENDING_FOLLOWUP_SEND_STORAGE_KEY, {
|
||||||
|
createdAt: Date.now(),
|
||||||
|
groupId: gid,
|
||||||
|
conversationID,
|
||||||
|
messages,
|
||||||
|
context: {
|
||||||
|
userId: getUserId(),
|
||||||
|
customerId: props.archiveId,
|
||||||
|
customerName: props.data?.name || "",
|
||||||
|
corpId: getCorpId(),
|
||||||
|
env: __VITE_ENV__,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (success) {
|
uni.navigateTo({
|
||||||
toast("消息已发送");
|
url: `/pages/message/index?conversationID=${encodeURIComponent(
|
||||||
uni.navigateBack();
|
conversationID
|
||||||
}
|
)}&groupID=${encodeURIComponent(gid)}&fromCase=true&pendingFollowUpSend=1`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -606,6 +713,80 @@ function extractIdFromUrl(url) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isRefreshingChatRoom = ref(false);
|
||||||
|
let lastRefreshChatRoomAt = 0;
|
||||||
|
|
||||||
|
function parseAnyTimeMs(v) {
|
||||||
|
if (v === null || v === undefined) return 0;
|
||||||
|
if (typeof v === "number") return v;
|
||||||
|
const s = String(v).trim();
|
||||||
|
if (!s) return 0;
|
||||||
|
if (/^\d{10,13}$/.test(s)) return Number(s.length === 10 ? `${s}000` : s);
|
||||||
|
const d = dayjs(s);
|
||||||
|
return d.isValid() ? d.valueOf() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshChatRoom() {
|
||||||
|
const customerId = String(props.archiveId || "");
|
||||||
|
if (!customerId) return;
|
||||||
|
if (isRefreshingChatRoom.value) return;
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastRefreshChatRoomAt < 5000) return;
|
||||||
|
lastRefreshChatRoomAt = now;
|
||||||
|
|
||||||
|
isRefreshingChatRoom.value = true;
|
||||||
|
try {
|
||||||
|
await ensureDoctor();
|
||||||
|
const corpId = getCorpId();
|
||||||
|
const teamId = getCurrentTeamId();
|
||||||
|
|
||||||
|
const baseQuery = {
|
||||||
|
corpId,
|
||||||
|
customerId,
|
||||||
|
page: 1,
|
||||||
|
pageSize: 50,
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryWithTeam = teamId ? { ...baseQuery, teamId } : baseQuery;
|
||||||
|
let detailRes = await api("getGroupList", queryWithTeam, false);
|
||||||
|
let details = Array.isArray(detailRes?.data?.list) ? detailRes.data.list : [];
|
||||||
|
|
||||||
|
if (!details.length && teamId) {
|
||||||
|
detailRes = await api("getGroupList", baseQuery, false);
|
||||||
|
details = Array.isArray(detailRes?.data?.list) ? detailRes.data.list : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!detailRes?.success || !details.length) {
|
||||||
|
chatGroupId.value = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentTeamId = getCurrentTeamId();
|
||||||
|
const detailsForCurrentTeam = currentTeamId
|
||||||
|
? details.filter((g) => String(g?.teamId || g?.team?._id || g?.team?.teamId || "") === currentTeamId)
|
||||||
|
: [];
|
||||||
|
const candidates = detailsForCurrentTeam.length ? detailsForCurrentTeam : details;
|
||||||
|
|
||||||
|
const statusRank = (s) => (s === "processing" ? 3 : s === "pending" ? 2 : 1);
|
||||||
|
candidates.sort((a, b) => {
|
||||||
|
const ra = statusRank(String(a?.orderStatus || ""));
|
||||||
|
const rb = statusRank(String(b?.orderStatus || ""));
|
||||||
|
if (rb !== ra) return rb - ra;
|
||||||
|
const ta = parseAnyTimeMs(a?.updatedAt) || parseAnyTimeMs(a?.createdAt);
|
||||||
|
const tb = parseAnyTimeMs(b?.updatedAt) || parseAnyTimeMs(b?.createdAt);
|
||||||
|
return tb - ta;
|
||||||
|
});
|
||||||
|
|
||||||
|
const best = candidates[0] || {};
|
||||||
|
const gid = normalizeGroupId(best.groupId || best.groupID || best.group_id || "");
|
||||||
|
chatGroupId.value = gid ? String(gid) : "";
|
||||||
|
} catch (e) {
|
||||||
|
// ignore
|
||||||
|
} finally {
|
||||||
|
isRefreshingChatRoom.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---- filter popup ----
|
// ---- filter popup ----
|
||||||
const filterPopupRef = ref(null);
|
const filterPopupRef = ref(null);
|
||||||
const state = ref(null);
|
const state = ref(null);
|
||||||
@ -692,6 +873,7 @@ onMounted(() => {
|
|||||||
if (userId && name)
|
if (userId && name)
|
||||||
userNameMap.value = { ...(userNameMap.value || {}), [userId]: name };
|
userNameMap.value = { ...(userNameMap.value || {}), [userId]: name };
|
||||||
loadTeams();
|
loadTeams();
|
||||||
|
refreshChatRoom();
|
||||||
resetList();
|
resetList();
|
||||||
uni.$on("archive-detail:followup-changed", resetList);
|
uni.$on("archive-detail:followup-changed", resetList);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -182,6 +182,7 @@ import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
|
|||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import useAccountStore from "@/store/account.js";
|
import useAccountStore from "@/store/account.js";
|
||||||
import { globalTimChatManager, TIM } from "@/utils/tim-chat.js";
|
import { globalTimChatManager, TIM } from "@/utils/tim-chat.js";
|
||||||
|
import { handleFollowUpMessages } from "@/utils/send-message-helper";
|
||||||
import {
|
import {
|
||||||
startIMMonitoring,
|
startIMMonitoring,
|
||||||
stopIMMonitoring,
|
stopIMMonitoring,
|
||||||
@ -210,6 +211,63 @@ import AIAssistantButtons from "./components/ai-assistant-buttons.vue";
|
|||||||
|
|
||||||
const timChatManager = globalTimChatManager;
|
const timChatManager = globalTimChatManager;
|
||||||
|
|
||||||
|
const PENDING_FOLLOWUP_SEND_STORAGE_KEY = "ykt_followup_pending_send";
|
||||||
|
const pendingFollowUpSendConsumed = ref(false);
|
||||||
|
const initialMessageListLoaded = ref(false);
|
||||||
|
|
||||||
|
function normalizeGroupId(v) {
|
||||||
|
const s = String(v || "").trim();
|
||||||
|
if (!s) return "";
|
||||||
|
return s.startsWith("GROUP") ? s.slice(5) : s;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function tryConsumePendingFollowUpSend() {
|
||||||
|
if (pendingFollowUpSendConsumed.value) return;
|
||||||
|
|
||||||
|
// 等待 IM 就绪与首屏消息加载完成,避免发送的本地消息被列表回调覆盖
|
||||||
|
if (!timChatManager?.isLoggedIn) return;
|
||||||
|
if (!initialMessageListLoaded.value) return;
|
||||||
|
|
||||||
|
const raw = uni.getStorageSync(PENDING_FOLLOWUP_SEND_STORAGE_KEY);
|
||||||
|
const payload = raw && typeof raw === "object" ? raw : null;
|
||||||
|
if (!payload) return;
|
||||||
|
|
||||||
|
const createdAt = Number(payload.createdAt || 0) || 0;
|
||||||
|
// 过期就清理,避免误发送
|
||||||
|
if (createdAt && Date.now() - createdAt > 5 * 60 * 1000) {
|
||||||
|
uni.removeStorageSync(PENDING_FOLLOWUP_SEND_STORAGE_KEY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payloadConversationID = String(payload.conversationID || "");
|
||||||
|
const payloadGroupId = normalizeGroupId(payload.groupId || "");
|
||||||
|
const currentConversationID = String(chatInfo.value.conversationID || "");
|
||||||
|
const currentGroupId = normalizeGroupId(groupId.value || "");
|
||||||
|
|
||||||
|
// 必须匹配当前会话,才允许发送
|
||||||
|
if (!payloadConversationID || payloadConversationID !== currentConversationID) return;
|
||||||
|
if (payloadGroupId && currentGroupId && payloadGroupId !== currentGroupId) return;
|
||||||
|
|
||||||
|
const messages = Array.isArray(payload.messages) ? payload.messages : [];
|
||||||
|
const context = payload.context && typeof payload.context === "object" ? payload.context : {};
|
||||||
|
if (!messages.length) {
|
||||||
|
uni.removeStorageSync(PENDING_FOLLOWUP_SEND_STORAGE_KEY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingFollowUpSendConsumed.value = true;
|
||||||
|
// 先清理再发,避免页面重复初始化导致二次发送
|
||||||
|
uni.removeStorageSync(PENDING_FOLLOWUP_SEND_STORAGE_KEY);
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
const ok = await handleFollowUpMessages(messages, context);
|
||||||
|
if (ok) {
|
||||||
|
uni.showToast({ title: "消息已发送", icon: "success" });
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: "部分发送失败", icon: "none" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取环境变量
|
// 获取环境变量
|
||||||
const env = __VITE_ENV__;
|
const env = __VITE_ENV__;
|
||||||
const corpId = env.MP_CORP_ID || "";
|
const corpId = env.MP_CORP_ID || "";
|
||||||
@ -527,15 +585,45 @@ const initTIMCallbacks = async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
messageList.value = uniqueMessages;
|
// 合并现有 messageList,避免首屏加载覆盖刚发送的本地消息
|
||||||
|
const merged = [];
|
||||||
|
const mergedSeen = new Set();
|
||||||
|
const existing = Array.isArray(messageList.value) ? messageList.value : [];
|
||||||
|
|
||||||
|
for (const m of existing) {
|
||||||
|
if (!m || !m.ID) continue;
|
||||||
|
if (m.conversationID !== chatInfo.value.conversationID) continue;
|
||||||
|
if (mergedSeen.has(m.ID)) continue;
|
||||||
|
mergedSeen.add(m.ID);
|
||||||
|
merged.push(m);
|
||||||
|
}
|
||||||
|
for (const m of uniqueMessages) {
|
||||||
|
if (!m || !m.ID) continue;
|
||||||
|
if (mergedSeen.has(m.ID)) continue;
|
||||||
|
mergedSeen.add(m.ID);
|
||||||
|
merged.push(m);
|
||||||
|
}
|
||||||
|
merged.sort((a, b) => {
|
||||||
|
const ta = Number(a?.lastTime || a?.time || 0) || 0;
|
||||||
|
const tb = Number(b?.lastTime || b?.time || 0) || 0;
|
||||||
|
return ta - tb;
|
||||||
|
});
|
||||||
|
|
||||||
|
messageList.value = merged;
|
||||||
console.log(
|
console.log(
|
||||||
"消息列表已更新,原始",
|
"消息列表已更新,原始",
|
||||||
messages.length,
|
messages.length,
|
||||||
"条,过滤后",
|
"条,过滤后",
|
||||||
uniqueMessages.length,
|
messageList.value.length,
|
||||||
"条消息"
|
"条消息"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!data.isPullUp && !data.isRefresh) {
|
||||||
|
initialMessageListLoaded.value = true;
|
||||||
|
// 首屏加载完成后再尝试消费待发送 payload
|
||||||
|
tryConsumePendingFollowUpSend();
|
||||||
|
}
|
||||||
|
|
||||||
isCompleted.value = data.isCompleted || false;
|
isCompleted.value = data.isCompleted || false;
|
||||||
isLoadingMore.value = false;
|
isLoadingMore.value = false;
|
||||||
|
|
||||||
@ -620,6 +708,9 @@ const loadMessageList = async () => {
|
|||||||
|
|
||||||
timChatManager.enterConversation(chatInfo.value.conversationID || "test1");
|
timChatManager.enterConversation(chatInfo.value.conversationID || "test1");
|
||||||
|
|
||||||
|
// 若从病历回访记录带入待发送内容,则进入会话后自动发送
|
||||||
|
tryConsumePendingFollowUpSend();
|
||||||
|
|
||||||
// 标记会话为已读 - 确保清空未读数
|
// 标记会话为已读 - 确保清空未读数
|
||||||
if (
|
if (
|
||||||
timChatManager.tim &&
|
timChatManager.tim &&
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user