diff --git a/pages.json b/pages.json
index 71172d8..6eb635a 100644
--- a/pages.json
+++ b/pages.json
@@ -156,6 +156,33 @@
}
]
},
+ {
+ "root": "pages/ai-consult",
+ "name": "ai-consult",
+ "pages": [
+ {
+ "path": "list",
+ "style": {
+ "navigationBarTitleText": "咨询记录",
+ "disableScroll": true
+ }
+ },
+ {
+ "path": "chat",
+ "style": {
+ "navigationBarTitleText": "AI咨询助理",
+ "disableScroll": true
+ }
+ },
+ {
+ "path": "entry",
+ "style": {
+ "navigationBarTitleText": "AI咨询助理",
+ "disableScroll": true
+ }
+ }
+ ]
+ },
{
"root": "pages/experience-coupon",
"name": "experience-coupon",
diff --git a/pages/ai-consult/chat.vue b/pages/ai-consult/chat.vue
new file mode 100644
index 0000000..3942999
--- /dev/null
+++ b/pages/ai-consult/chat.vue
@@ -0,0 +1,158 @@
+
+
+
+
+
+ {{ formatMessageTime(item.createTime) }}
+
+ {{ displayContent(item) }}
+
+ AI
+
+ {{ session?.assistantName || 'AI咨询助理' }}
+
+ {{ displayContent(item) }}
+
+ 发送失败,请重试
+
+ 我
+
+
+
+ AI{{ session?.assistantName || 'AI咨询助理' }}
+
+
+
+
+
+
+
+
+ 本次AI咨询已结束
+
+
+
+
+
+
diff --git a/pages/ai-consult/entry.vue b/pages/ai-consult/entry.vue
new file mode 100644
index 0000000..13974e0
--- /dev/null
+++ b/pages/ai-consult/entry.vue
@@ -0,0 +1,100 @@
+
+
+
+ 请选择本次咨询档案
+
+
+
+
+ {{ item.sex }}
+ /
+ {{ item.age }}岁
+ ,
+ {{ item.mobile }}
+
+ 证件号:{{ item.idCard || '--' }}
+
+
+
+
+ 正在前往建档...
+
+
+
+
+
+
diff --git a/pages/ai-consult/list.vue b/pages/ai-consult/list.vue
new file mode 100644
index 0000000..b7ac116
--- /dev/null
+++ b/pages/ai-consult/list.vue
@@ -0,0 +1,46 @@
+
+
+
+
+ {{ item.name }}
+
+
+ {{ item.name }}
+
+
+
+
+
+ {{ item.assistantName }}{{ item.statusLabel }}
+ {{ item.sourceLabel }}
+ 团队:{{ item.teamName || item.teamId || '-' }}
+ 档案:{{ item.customerName || '-' }}
+ 最近互动:{{ format(item.lastActiveTime) }}
+ 已触发敏感词
+
+
+
+
+
+
diff --git a/pages/archive/edit-archive.vue b/pages/archive/edit-archive.vue
index 9cf07f3..5387b66 100644
--- a/pages/archive/edit-archive.vue
+++ b/pages/archive/edit-archive.vue
@@ -61,6 +61,7 @@ const referenceCustomer = ref(null)
const healthTypes = ref([]);
const enableHis = ref(false);
const source = ref('');
+const redirectUrl = ref('');
const formData = computed(() => {
return { ...customer.value, ...form.value, mobile: account.value?.mobile }
@@ -173,6 +174,10 @@ async function addArchive() {
uni.navigateBack();
return;
}
+ if (redirectUrl.value) {
+ uni.redirectTo({ url: redirectUrl.value });
+ return;
+ }
// getTeam(corpId.value, teamId.value, res.data.id);
if (healthTypes.value.length) {
const nextType = healthTypes.value[0];
@@ -341,6 +346,7 @@ onLoad(options => {
corpUserId.value = options.corpUserId || '';
referenceCustomerId.value = options.referenceCustomerId || '';
source.value = options.source || '';
+ redirectUrl.value = options.redirectUrl || '';
if (referenceCustomerId.value) {
getReferenceCustomer();
}
@@ -352,4 +358,4 @@ useLoad(options => {
})
-
\ No newline at end of file
+
diff --git a/pages/home/consult.vue b/pages/home/consult.vue
index 5941631..bc60135 100644
--- a/pages/home/consult.vue
+++ b/pages/home/consult.vue
@@ -216,23 +216,26 @@ defineExpose({
}
.consult-grid {
- height: 208rpx;
+ min-height: 208rpx;
box-sizing: border-box;
background: #fff;
border-radius: 16rpx;
- padding: 24rpx 30rpx;
+ padding: 26rpx 22rpx;
display: grid;
grid-template-columns: repeat(4, 1fr);
- align-items: center;
- gap: 32rpx;
+ grid-auto-rows: 120rpx;
+ align-items: start;
+ column-gap: 0;
+ row-gap: 24rpx;
box-shadow: 0 8rpx 10rpx 0 rgba(60, 169, 145, 0.06);
}
.consult-item {
+ min-width: 0;
display: flex;
flex-direction: column;
align-items: center;
- gap: 12rpx;
+ gap: 10rpx;
cursor: pointer;
}
@@ -242,7 +245,7 @@ defineExpose({
.item-icon {
width: 80rpx;
- height: 80;
+ height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
@@ -270,9 +273,12 @@ defineExpose({
}
.item-label {
- font-size: 30rpx;
- color: #666d76;
+ max-width: 100%;
+ color: #596273;
+ font-size: 26rpx;
+ line-height: 36rpx;
text-align: center;
font-weight: 400;
+ white-space: nowrap;
}
diff --git a/pages/login/login.vue b/pages/login/login.vue
index c7c1284..5b40b73 100644
--- a/pages/login/login.vue
+++ b/pages/login/login.vue
@@ -132,10 +132,11 @@ async function bindTeam() {
return toast("关联团队失败");
}
await syncExternalUserRelation();
- const res1 = await api('getWxAppCustomerCount', { miniAppId: account.value.openid, corpId: team.value.corpId, teamId: team.value.teamId });
if (redirectUrl.value) {
- await attempToPage(redirectUrl.value)
- } else if (res1 && res1.data > 0) {
+ return attempToPage(redirectUrl.value);
+ }
+ const res1 = await api('getWxAppCustomerCount', { miniAppId: account.value.openid, corpId: team.value.corpId, teamId: team.value.teamId });
+ if (res1 && res1.data > 0) {
toHome();
} else {
attempToPage(`/pages/archive/edit-archive?corpUserId=${team.value.corpUserId || ''}&teamId=${team.value.teamId}&corpId=${team.value.corpId}`)
@@ -181,7 +182,7 @@ onLoad((opts) => {
if (opts.source === "teamInvite") {
team.value = get("invite-team-info");
if (!redirectUrl.value && team.value) {
- redirectUrl.value = `/pages/archive/edit-archive?corpUserId=${team.value.corpUserId || ''}&teamId=${team.value.teamId}&corpId=${team.value.corpId}`;
+ redirectUrl.value = team.value.redirectUrl || `/pages/archive/edit-archive?corpUserId=${team.value.corpUserId || ''}&teamId=${team.value.teamId}&corpId=${team.value.corpId}`;
}
return;
}
diff --git a/pages/login/redirect-page.vue b/pages/login/redirect-page.vue
index c20f9cd..4e4906d 100644
--- a/pages/login/redirect-page.vue
+++ b/pages/login/redirect-page.vue
@@ -75,8 +75,13 @@ async function syncExternalUserRelation({ corpId, externalUserId, corpUserId })
}
}
-async function changeTeam({ teamId, corpId, corpUserId, externalUserId, qrid, referenceCustomerId }) {
+function getAiConsultEntryUrl({ teamId, corpId, qrid }) {
+ return `/pages/ai-consult/entry?teamId=${teamId || ''}&corpId=${corpId || ''}&qrid=${qrid || ''}`;
+}
+
+async function changeTeam({ teamId, corpId, corpUserId, externalUserId, qrid, referenceCustomerId, type }) {
const normalizedCorpId = normalizeCorpId(corpId);
+ const redirectUrl = type === 'aiConsult' ? getAiConsultEntryUrl({ teamId, corpId: normalizedCorpId, qrid }) : '';
loading.value = true;
const res = await api("getTeamData", { teamId, corpId: normalizedCorpId, withCorpName: true });
loading.value = false;
@@ -96,13 +101,14 @@ async function changeTeam({ teamId, corpId, corpUserId, externalUserId, qrid, re
});
await login('', { forceVerify: true })
if (account.value && account.value.mobile) {
- bindTeam({ corpUserId, externalUserId })
+ bindTeam({ corpUserId, externalUserId, redirectUrl })
} else {
set("invite-team-info", {
corpUserId,
externalUserId,
qrid,
referenceCustomerId,
+ redirectUrl,
corpId: team.value.corpId,
teamId: team.value.teamId,
corpName: team.value.corpName,
@@ -121,7 +127,7 @@ async function changeTeam({ teamId, corpId, corpUserId, externalUserId, qrid, re
}
}
-async function bindTeam({ corpUserId, externalUserId }) {
+async function bindTeam({ corpUserId, externalUserId, redirectUrl = '' }) {
const res = await api('bindWxappWithTeam', { appid, corpId: team.value.corpId, teamId: team.value.teamId, openid: account.value.openid });
if (!res || !res.success) {
return toast("关联团队失败");
@@ -131,6 +137,9 @@ async function bindTeam({ corpUserId, externalUserId }) {
externalUserId,
corpUserId,
});
+ if (redirectUrl) {
+ return uni.redirectTo({ url: redirectUrl });
+ }
const res1 = await api('getWxAppCustomerCount', { miniAppId: account.value.openid, corpId: team.value.corpId, teamId: team.value.teamId });
if (res1 && res1.data > 0) {
uni.switchTab({
diff --git a/pages/message/components/special-message/evaluation.vue b/pages/message/components/special-message/evaluation.vue
index 72054c1..a7738c5 100644
--- a/pages/message/components/special-message/evaluation.vue
+++ b/pages/message/components/special-message/evaluation.vue
@@ -85,7 +85,7 @@
\ No newline at end of file
+
diff --git a/pages/message/message.vue b/pages/message/message.vue
index 571b47d..2bded82 100644
--- a/pages/message/message.vue
+++ b/pages/message/message.vue
@@ -8,10 +8,11 @@
加载中...
-
-
+
+
{{
conversation.unreadCount > 99 ? "99+" : conversation.unreadCount
@@ -21,13 +22,14 @@
+ AI咨询
咨询人 | {{ conversation.patientName }}
@@ -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;
diff --git a/static/home/ai-consult-assistant.png b/static/home/ai-consult-assistant.png
new file mode 100644
index 0000000..e1dfc8d
Binary files /dev/null and b/static/home/ai-consult-assistant.png differ
diff --git a/utils/api.js b/utils/api.js
index 4d3ad0a..d4ad2be 100644
--- a/utils/api.js
+++ b/utils/api.js
@@ -22,6 +22,14 @@ const urlsConfig = {
getJoinedTeams: 'getJoinedTeams',
getCorpBusinessCardStatus: 'getCorpBusinessCardStatus',
getCorpInfo: 'getCorpInfo',
+ getAiConsultPermission: 'getAiConsultPermission',
+ getAiConsultAssistants: 'getAiConsultAssistants',
+ getAiConsultSessions: 'getAiConsultSessions',
+ getUnifiedConsultSessions: 'getUnifiedConsultSessions',
+ getAiConsultSessionDetail: 'getAiConsultSessionDetail',
+ openAiConsultSession: 'openAiConsultSession',
+ sendAiConsultMessage: 'sendAiConsultMessage',
+ closeAiConsultSession: 'closeAiConsultSession',
},
knowledgeBase: {