no message

This commit is contained in:
wangdongbo 2026-08-11 09:21:20 +08:00
parent 68c6db7422
commit bbf9e81f1f
5 changed files with 86 additions and 34 deletions

View File

@ -162,7 +162,7 @@
"pages": [
{
"path": "list",
"style": { "navigationBarTitleText": "AI咨询记录", "disableScroll": true }
"style": { "navigationBarTitleText": "咨询记录", "disableScroll": true }
},
{
"path": "chat",

View File

@ -2,13 +2,17 @@
<full-page @reachBottom="loadMore">
<template #header>
<scroll-view scroll-x class="tabs" :show-scrollbar="false">
<view v-for="item in sources" :key="item.value" class="tab" :class="{ active: source === item.value }" @click="selectSource(item.value)">{{ item.name }}</view>
</scroll-view>
<scroll-view scroll-x class="tabs customer-tabs" :show-scrollbar="false">
<view v-for="item in customers" :key="item.value" class="tab" :class="{ active: customerId === item.value }" @click="selectCustomer(item.value)">{{ item.name }}</view>
</scroll-view>
</template>
<view class="page">
<empty-data v-if="!loading && list.length === 0" text="暂无AI咨询记录" />
<view v-for="item in list" :key="item._id" class="card" @click="openChat(item)">
<view class="title"><text>{{ item.assistantName }}</text><text class="status" :class="item.status">{{ statusText(item) }}</text></view>
<empty-data v-if="!loading && list.length === 0" text="暂无咨询记录" />
<view v-for="item in list" :key="`${item.source}-${item.id}`" class="card" @click="openChat(item)">
<view class="title"><text>{{ item.assistantName }}</text><text class="status" :class="item.status">{{ item.statusLabel }}</text></view>
<view class="line"><text class="source" :class="item.source">{{ item.sourceLabel }}</text></view>
<view class="line">团队{{ item.teamName || item.teamId || '-' }}</view>
<view class="line">档案{{ item.customerName || '-' }}</view>
<view class="line">最近互动{{ format(item.lastActiveTime) }}</view>
@ -26,15 +30,17 @@ import { storeToRefs } from 'pinia';
import FullPage from '@/components/full-page.vue';
import EmptyData from '@/components/empty-data.vue';
const { openid } = storeToRefs(useAccountStore());
const corpId = ref(''); const customerId = ref(''); const customers = ref([{ name: '全部档案', value: '' }]);
const corpId = ref(''); const customerId = ref(''); const source = ref('');
const sources = [{ name: '全部咨询', value: '' }, { name: 'AI咨询', value: 'ai' }, { name: 'IM咨询', value: 'im' }];
const customers = ref([{ name: '全部档案', value: '' }]);
const list = ref([]); const page = ref(1); const pages = ref(0); const loading = ref(false);
const format = value => value ? new Date(value).toLocaleString('zh-CN', { hour12: false }) : '-';
const statusText = item => item.status === 'active' ? '咨询中' : item.endReason === 'sensitive_word' ? '敏感词结束' : '已结束';
async function loadCustomers() { const res = await api('getMiniAppCustomers', { corpId: corpId.value, miniAppId: openid.value || uni.getStorageSync('openid') }, false); if (res?.success) customers.value = [customers.value[0], ...(res.data || []).map(item => ({ name: item.name || '未命名', value: item._id }))]; }
async function load(reset = false) { if (loading.value) return; if (reset) { page.value = 1; list.value = []; } loading.value = true; const res = await api('getAiConsultSessions', { corpId: corpId.value, customerId: customerId.value, miniAppId: openid.value || uni.getStorageSync('openid'), page: page.value, pageSize: 20 }, false); if (res?.success) { list.value = page.value === 1 ? res.list : [...list.value, ...res.list]; pages.value = res.pages || 0; } loading.value = false; }
async function load(reset = false) { if (loading.value) return; if (reset) { page.value = 1; list.value = []; } loading.value = true; const res = await api('getUnifiedConsultSessions', { corpId: corpId.value, customerId: customerId.value, miniAppId: openid.value || uni.getStorageSync('openid'), source: source.value, page: page.value, pageSize: 20 }, false); if (res?.success) { list.value = page.value === 1 ? res.list : [...list.value, ...res.list]; pages.value = res.pages || 0; } loading.value = false; }
function selectCustomer(value) { if (customerId.value === value) return; customerId.value = value; load(true); }
function selectSource(value) { if (source.value === value) return; source.value = value; load(true); }
function loadMore() { if (!loading.value && page.value < pages.value) { page.value += 1; load(); } }
function openChat(item) { uni.navigateTo({ url: `/pages/ai-consult/chat?corpId=${item.corpId}&sessionId=${item._id}` }); }
function openChat(item) { const url = item.source === 'im' ? `/pages/message/index?conversationID=GROUP${item.groupId}&groupID=${item.groupId}` : `/pages/ai-consult/chat?corpId=${item.corpId}&sessionId=${item.id}`; uni.navigateTo({ url }); }
onLoad(async opts => { corpId.value = opts.corpId || ''; await loadCustomers(); await load(true); });
</script>
<style scoped>.page{min-height:100vh;background:#f7f8fa;padding:24rpx}.tabs{white-space:nowrap;background:#fff;padding:18rpx 20rpx}.tab{display:inline-block;padding:10rpx 22rpx;margin-right:16rpx;border-radius:28rpx;color:#666;background:#f5f5f5}.tab.active{color:#0877f1;background:#e8f3ff}.card{background:#fff;border-radius:16rpx;padding:24rpx;margin-bottom:20rpx}.title{display:flex;justify-content:space-between;font-size:32rpx;font-weight:600;margin-bottom:16rpx}.status{font-size:24rpx;color:#ff8a00}.status.active{color:#18a058}.line{font-size:26rpx;color:#777;line-height:44rpx}.warn{font-size:24rpx;color:#e34d59;margin-top:8rpx}</style>
<style scoped>.page{min-height:100vh;background:#f7f8fa;padding:24rpx}.tabs{white-space:nowrap;background:#fff;padding:18rpx 20rpx}.customer-tabs{border-top:1rpx solid #f2f3f5;padding-top:12rpx}.tab{display:inline-block;padding:10rpx 22rpx;margin-right:16rpx;border-radius:28rpx;color:#666;background:#f5f5f5}.tab.active{color:#0877f1;background:#e8f3ff}.card{background:#fff;border-radius:16rpx;padding:24rpx;margin-bottom:20rpx}.title{display:flex;justify-content:space-between;font-size:32rpx;font-weight:600;margin-bottom:16rpx}.status{font-size:24rpx;color:#ff8a00}.status.active,.status.processing{color:#18a058}.line{font-size:26rpx;color:#777;line-height:44rpx}.source{font-size:22rpx;border-radius:6rpx;padding:2rpx 10rpx;color:#0877f1;background:#e8f3ff}.source.im{color:#7c55d1;background:#f1ebff}.warn{font-size:24rpx;color:#e34d59;margin-top:8rpx}</style>

View File

@ -49,7 +49,6 @@ const props = defineProps({
const { account } = storeToRefs(useAccount());
const consultantPopup = ref(null);
const badgeMap = ref({});
const aiConsultEnabled = ref(false);
let loading = false;
const consultItems = ref([
@ -81,12 +80,6 @@ const consultItems = ref([
path: "/pages/rate/rate-list",
badge: 'rate'
},
{
id: "aiConsultRecord",
label: "AI咨询记录",
icon: "/static/home/ai-consult-assistant.png",
path: "/pages/ai-consult/list",
},
]);
const hideMenus = computed(() => {
const result = {};
@ -94,7 +87,6 @@ const hideMenus = computed(() => {
if (!(props.team && props.team.createSource === 'yzs')) {
result.chat = true;
}
if (!aiConsultEnabled.value) result.aiConsultRecord = true;
return result;
})
@ -199,15 +191,8 @@ async function getBadgeCount() {
loading = false;
}
async function loadAiConsultPermission() {
if (!props.corpId) return;
const res = await api('getAiConsultPermission', { corpId: props.corpId }, false);
aiConsultEnabled.value = Boolean(res?.success && res?.data?.enabled);
}
watch(() => [props.customers, props.teamId, props.corpId], n => {
getBadgeCount()
loadAiConsultPermission()
}, { immediate: true })
defineExpose({

View File

@ -8,10 +8,11 @@
<text class="loading-text">加载中...</text>
</view>
<!-- 消息列表项 -->
<view v-for="conversation in conversationList" :key="conversation.groupID || conversation.conversationID"
<view v-for="conversation in conversationList" :key="conversation.source === 'ai' ? `ai-${conversation.sessionId}` : (conversation.groupID || conversation.conversationID)"
class="message-item" @click="handleClickConversation(conversation)">
<view class="avatar-container">
<GroupAvatar :avatarList="getAvatarList(conversation.groupID)" :size="96" classType="square" />
<image v-if="conversation.source === 'ai'" class="avatar" src="/static/home/ai-consult-assistant.png" mode="aspectFill" />
<GroupAvatar v-else :avatarList="getAvatarList(conversation.groupID)" :size="96" classType="square" />
<view v-if="conversation.unreadCount > 0" class="unread-badge">
<text class="unread-text">{{
conversation.unreadCount > 99 ? "99+" : conversation.unreadCount
@ -21,13 +22,14 @@
<view class="content">
<view class="header">
<text class="name">{{ conversation.teamName }}</text>
<text class="name">{{ conversation.displayName || conversation.teamName }}</text>
<text class="time">{{
formatMessageTime(conversation.lastMessageTime)
}}</text>
</view>
<view class="patient-info">
<text v-if="conversation.source === 'ai'" class="source-tag">AI咨询</text>
咨询人 | {{ conversation.patientName }}
</view>
<view class="message-preview">
@ -72,6 +74,7 @@ import { mergeConversationWithGroupDetails } from "@/utils/conversation-merger.j
import { globalUnreadListenerManager } from "@/utils/global-unread-listener.js";
import { get } from "@/utils/cache";
import { normalizeCorpId } from "@/utils/api-base-config";
import api from "@/utils/api";
import useGroupAvatars from "./hooks/use-group-avatars.js";
import GroupAvatar from "@/components/group-avatar.vue";
import {
@ -229,9 +232,10 @@ const loadConversationList = async () => {
const result = await globalTimChatManager.getGroupList();
if (result && result.success && result.groupList) {
//
conversationList.value = await mergeConversationWithGroupDetails(
const imConversations = await mergeConversationWithGroupDetails(
result.groupList
);
conversationList.value = mergeConsultationList(imConversations);
console.log(
"群聊列表加载成功,共",
conversationList.value.length,
@ -250,6 +254,46 @@ const loadConversationList = async () => {
}
};
function mergeConsultationList(imConversations) {
const aiConversations = conversationList.value.filter((item) => item.source === "ai");
return [...imConversations, ...aiConversations].sort(
(left, right) => (right.lastMessageTime || 0) - (left.lastMessageTime || 0)
);
}
const loadAiConsultationList = async () => {
const corpId = getCurrentTeamCorpId();
const miniAppId = openid.value || account.value?.openid || "";
if (!corpId || !miniAppId) return;
try {
const result = await api("getUnifiedConsultSessions", {
corpId,
miniAppId,
source: "ai",
page: 1,
pageSize: 100,
}, false);
if (!result?.success) return;
const imConversations = conversationList.value.filter((item) => item.source !== "ai");
const aiConversations = (result.list || []).map((item) => ({
source: "ai",
sessionId: item.id,
corpId: item.corpId,
displayName: item.assistantName || "AI咨询助理",
teamName: item.teamName || "",
patientName: item.customerName || "未命名患者",
lastMessage: item.statusLabel || "AI咨询",
lastMessageTime: item.lastActiveTime || 0,
unreadCount: 0,
}));
conversationList.value = [...imConversations, ...aiConversations].sort(
(left, right) => (right.lastMessageTime || 0) - (left.lastMessageTime || 0)
);
} catch (error) {
console.log("加载AI咨询记录异常:", error.message);
}
};
//
let updateTimer = null;
@ -438,6 +482,13 @@ const formatMessageTime = (timestamp) => {
const handleClickConversation = async (conversation) => {
console.log("点击会话:", conversation);
if (conversation.source === "ai") {
uni.navigateTo({
url: `/pages/ai-consult/chat?corpId=${conversation.corpId}&sessionId=${conversation.sessionId}`,
});
return;
}
//
const conversationIndex = conversationList.value.findIndex(
(conv) => conv.conversationID === conversation.conversationID
@ -480,11 +531,11 @@ const handleLoadMore = () => {
//
const handleRefresh = async () => {
if (!hasImCorpId.value) return;
refreshing.value = true;
try {
await loadConversationList();
if (hasImCorpId.value) await loadConversationList();
await loadAiConsultationList();
} finally {
refreshing.value = false;
}
@ -499,7 +550,8 @@ async function init() {
console.log("IM初始化失败继续加载列表");
}
//
await loadConversationList();
if (imReady) await loadConversationList();
await loadAiConsultationList();
//
setupConversationListener();
} catch (error) {
@ -580,9 +632,7 @@ onHide(() => {
});
watch(hasImCorpId, (n, o) => {
if (n && !o) {
init();
}
if (n || !o) init();
}, { immediate: true })
//
onUnmounted(() => {
@ -783,6 +833,16 @@ onUnmounted(() => {
padding-bottom: 10rpx;
}
.source-tag {
display: inline-block;
margin-right: 10rpx;
padding: 1rpx 8rpx;
border-radius: 6rpx;
color: #0877f1;
background: #e8f3ff;
font-size: 22rpx;
}
.subscribe-entry {
position: fixed;
right: 32rpx;

View File

@ -23,6 +23,7 @@ const urlsConfig = {
getAiConsultPermission: 'getAiConsultPermission',
getAiConsultAssistants: 'getAiConsultAssistants',
getAiConsultSessions: 'getAiConsultSessions',
getUnifiedConsultSessions: 'getUnifiedConsultSessions',
getAiConsultSessionDetail: 'getAiConsultSessionDetail',
openAiConsultSession: 'openAiConsultSession',
sendAiConsultMessage: 'sendAiConsultMessage',