no message
This commit is contained in:
parent
68c6db7422
commit
bbf9e81f1f
@ -162,7 +162,7 @@
|
||||
"pages": [
|
||||
{
|
||||
"path": "list",
|
||||
"style": { "navigationBarTitleText": "AI咨询记录", "disableScroll": true }
|
||||
"style": { "navigationBarTitleText": "咨询记录", "disableScroll": true }
|
||||
},
|
||||
{
|
||||
"path": "chat",
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -23,6 +23,7 @@ const urlsConfig = {
|
||||
getAiConsultPermission: 'getAiConsultPermission',
|
||||
getAiConsultAssistants: 'getAiConsultAssistants',
|
||||
getAiConsultSessions: 'getAiConsultSessions',
|
||||
getUnifiedConsultSessions: 'getUnifiedConsultSessions',
|
||||
getAiConsultSessionDetail: 'getAiConsultSessionDetail',
|
||||
openAiConsultSession: 'openAiConsultSession',
|
||||
sendAiConsultMessage: 'sendAiConsultMessage',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user