no message
This commit is contained in:
parent
68c6db7422
commit
bbf9e81f1f
@ -162,7 +162,7 @@
|
|||||||
"pages": [
|
"pages": [
|
||||||
{
|
{
|
||||||
"path": "list",
|
"path": "list",
|
||||||
"style": { "navigationBarTitleText": "AI咨询记录", "disableScroll": true }
|
"style": { "navigationBarTitleText": "咨询记录", "disableScroll": true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "chat",
|
"path": "chat",
|
||||||
|
|||||||
@ -2,13 +2,17 @@
|
|||||||
<full-page @reachBottom="loadMore">
|
<full-page @reachBottom="loadMore">
|
||||||
<template #header>
|
<template #header>
|
||||||
<scroll-view scroll-x class="tabs" :show-scrollbar="false">
|
<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>
|
<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>
|
</scroll-view>
|
||||||
</template>
|
</template>
|
||||||
<view class="page">
|
<view class="page">
|
||||||
<empty-data v-if="!loading && list.length === 0" text="暂无AI咨询记录" />
|
<empty-data v-if="!loading && list.length === 0" text="暂无咨询记录" />
|
||||||
<view v-for="item in list" :key="item._id" class="card" @click="openChat(item)">
|
<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">{{ statusText(item) }}</text></view>
|
<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.teamName || item.teamId || '-' }}</view>
|
||||||
<view class="line">档案:{{ item.customerName || '-' }}</view>
|
<view class="line">档案:{{ item.customerName || '-' }}</view>
|
||||||
<view class="line">最近互动:{{ format(item.lastActiveTime) }}</view>
|
<view class="line">最近互动:{{ format(item.lastActiveTime) }}</view>
|
||||||
@ -26,15 +30,17 @@ import { storeToRefs } from 'pinia';
|
|||||||
import FullPage from '@/components/full-page.vue';
|
import FullPage from '@/components/full-page.vue';
|
||||||
import EmptyData from '@/components/empty-data.vue';
|
import EmptyData from '@/components/empty-data.vue';
|
||||||
const { openid } = storeToRefs(useAccountStore());
|
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 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 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 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 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 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); });
|
onLoad(async opts => { corpId.value = opts.corpId || ''; await loadCustomers(); await load(true); });
|
||||||
</script>
|
</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>
|
||||||
|
|||||||
@ -49,7 +49,6 @@ const props = defineProps({
|
|||||||
const { account } = storeToRefs(useAccount());
|
const { account } = storeToRefs(useAccount());
|
||||||
const consultantPopup = ref(null);
|
const consultantPopup = ref(null);
|
||||||
const badgeMap = ref({});
|
const badgeMap = ref({});
|
||||||
const aiConsultEnabled = ref(false);
|
|
||||||
let loading = false;
|
let loading = false;
|
||||||
|
|
||||||
const consultItems = ref([
|
const consultItems = ref([
|
||||||
@ -81,12 +80,6 @@ const consultItems = ref([
|
|||||||
path: "/pages/rate/rate-list",
|
path: "/pages/rate/rate-list",
|
||||||
badge: 'rate'
|
badge: 'rate'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: "aiConsultRecord",
|
|
||||||
label: "AI咨询记录",
|
|
||||||
icon: "/static/home/ai-consult-assistant.png",
|
|
||||||
path: "/pages/ai-consult/list",
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
const hideMenus = computed(() => {
|
const hideMenus = computed(() => {
|
||||||
const result = {};
|
const result = {};
|
||||||
@ -94,7 +87,6 @@ const hideMenus = computed(() => {
|
|||||||
if (!(props.team && props.team.createSource === 'yzs')) {
|
if (!(props.team && props.team.createSource === 'yzs')) {
|
||||||
result.chat = true;
|
result.chat = true;
|
||||||
}
|
}
|
||||||
if (!aiConsultEnabled.value) result.aiConsultRecord = true;
|
|
||||||
return result;
|
return result;
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -199,15 +191,8 @@ async function getBadgeCount() {
|
|||||||
loading = false;
|
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 => {
|
watch(() => [props.customers, props.teamId, props.corpId], n => {
|
||||||
getBadgeCount()
|
getBadgeCount()
|
||||||
loadAiConsultPermission()
|
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
|
|||||||
@ -8,10 +8,11 @@
|
|||||||
<text class="loading-text">加载中...</text>
|
<text class="loading-text">加载中...</text>
|
||||||
</view>
|
</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)">
|
class="message-item" @click="handleClickConversation(conversation)">
|
||||||
<view class="avatar-container">
|
<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">
|
<view v-if="conversation.unreadCount > 0" class="unread-badge">
|
||||||
<text class="unread-text">{{
|
<text class="unread-text">{{
|
||||||
conversation.unreadCount > 99 ? "99+" : conversation.unreadCount
|
conversation.unreadCount > 99 ? "99+" : conversation.unreadCount
|
||||||
@ -21,13 +22,14 @@
|
|||||||
|
|
||||||
<view class="content">
|
<view class="content">
|
||||||
<view class="header">
|
<view class="header">
|
||||||
<text class="name">{{ conversation.teamName }}</text>
|
<text class="name">{{ conversation.displayName || conversation.teamName }}</text>
|
||||||
|
|
||||||
<text class="time">{{
|
<text class="time">{{
|
||||||
formatMessageTime(conversation.lastMessageTime)
|
formatMessageTime(conversation.lastMessageTime)
|
||||||
}}</text>
|
}}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="patient-info">
|
<view class="patient-info">
|
||||||
|
<text v-if="conversation.source === 'ai'" class="source-tag">AI咨询</text>
|
||||||
咨询人 | {{ conversation.patientName }}
|
咨询人 | {{ conversation.patientName }}
|
||||||
</view>
|
</view>
|
||||||
<view class="message-preview">
|
<view class="message-preview">
|
||||||
@ -72,6 +74,7 @@ import { mergeConversationWithGroupDetails } from "@/utils/conversation-merger.j
|
|||||||
import { globalUnreadListenerManager } from "@/utils/global-unread-listener.js";
|
import { globalUnreadListenerManager } from "@/utils/global-unread-listener.js";
|
||||||
import { get } from "@/utils/cache";
|
import { get } from "@/utils/cache";
|
||||||
import { normalizeCorpId } from "@/utils/api-base-config";
|
import { normalizeCorpId } from "@/utils/api-base-config";
|
||||||
|
import api from "@/utils/api";
|
||||||
import useGroupAvatars from "./hooks/use-group-avatars.js";
|
import useGroupAvatars from "./hooks/use-group-avatars.js";
|
||||||
import GroupAvatar from "@/components/group-avatar.vue";
|
import GroupAvatar from "@/components/group-avatar.vue";
|
||||||
import {
|
import {
|
||||||
@ -229,9 +232,10 @@ const loadConversationList = async () => {
|
|||||||
const result = await globalTimChatManager.getGroupList();
|
const result = await globalTimChatManager.getGroupList();
|
||||||
if (result && result.success && result.groupList) {
|
if (result && result.success && result.groupList) {
|
||||||
// 合并后端群组详细信息(已包含格式化和排序)
|
// 合并后端群组详细信息(已包含格式化和排序)
|
||||||
conversationList.value = await mergeConversationWithGroupDetails(
|
const imConversations = await mergeConversationWithGroupDetails(
|
||||||
result.groupList
|
result.groupList
|
||||||
);
|
);
|
||||||
|
conversationList.value = mergeConsultationList(imConversations);
|
||||||
console.log(
|
console.log(
|
||||||
"群聊列表加载成功,共",
|
"群聊列表加载成功,共",
|
||||||
conversationList.value.length,
|
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;
|
let updateTimer = null;
|
||||||
|
|
||||||
@ -438,6 +482,13 @@ const formatMessageTime = (timestamp) => {
|
|||||||
const handleClickConversation = async (conversation) => {
|
const handleClickConversation = async (conversation) => {
|
||||||
console.log("点击会话:", 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(
|
const conversationIndex = conversationList.value.findIndex(
|
||||||
(conv) => conv.conversationID === conversation.conversationID
|
(conv) => conv.conversationID === conversation.conversationID
|
||||||
@ -480,11 +531,11 @@ const handleLoadMore = () => {
|
|||||||
|
|
||||||
// 下拉刷新
|
// 下拉刷新
|
||||||
const handleRefresh = async () => {
|
const handleRefresh = async () => {
|
||||||
if (!hasImCorpId.value) return;
|
|
||||||
refreshing.value = true;
|
refreshing.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await loadConversationList();
|
if (hasImCorpId.value) await loadConversationList();
|
||||||
|
await loadAiConsultationList();
|
||||||
} finally {
|
} finally {
|
||||||
refreshing.value = false;
|
refreshing.value = false;
|
||||||
}
|
}
|
||||||
@ -499,7 +550,8 @@ async function init() {
|
|||||||
console.log("IM初始化失败,继续加载列表");
|
console.log("IM初始化失败,继续加载列表");
|
||||||
}
|
}
|
||||||
// 先加载初始会话列表
|
// 先加载初始会话列表
|
||||||
await loadConversationList();
|
if (imReady) await loadConversationList();
|
||||||
|
await loadAiConsultationList();
|
||||||
// 再设置监听器,后续通过事件更新列表
|
// 再设置监听器,后续通过事件更新列表
|
||||||
setupConversationListener();
|
setupConversationListener();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -580,9 +632,7 @@ onHide(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
watch(hasImCorpId, (n, o) => {
|
watch(hasImCorpId, (n, o) => {
|
||||||
if (n && !o) {
|
if (n || !o) init();
|
||||||
init();
|
|
||||||
}
|
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
// 页面卸载
|
// 页面卸载
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@ -783,6 +833,16 @@ onUnmounted(() => {
|
|||||||
padding-bottom: 10rpx;
|
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 {
|
.subscribe-entry {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 32rpx;
|
right: 32rpx;
|
||||||
|
|||||||
@ -23,6 +23,7 @@ const urlsConfig = {
|
|||||||
getAiConsultPermission: 'getAiConsultPermission',
|
getAiConsultPermission: 'getAiConsultPermission',
|
||||||
getAiConsultAssistants: 'getAiConsultAssistants',
|
getAiConsultAssistants: 'getAiConsultAssistants',
|
||||||
getAiConsultSessions: 'getAiConsultSessions',
|
getAiConsultSessions: 'getAiConsultSessions',
|
||||||
|
getUnifiedConsultSessions: 'getUnifiedConsultSessions',
|
||||||
getAiConsultSessionDetail: 'getAiConsultSessionDetail',
|
getAiConsultSessionDetail: 'getAiConsultSessionDetail',
|
||||||
openAiConsultSession: 'openAiConsultSession',
|
openAiConsultSession: 'openAiConsultSession',
|
||||||
sendAiConsultMessage: 'sendAiConsultMessage',
|
sendAiConsultMessage: 'sendAiConsultMessage',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user