feat: 咨询消息轮询优化 — Web 自动刷新 + 患者端聊天详情页
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

Web 端:
- ConsultationDetail 添加 10s 自动轮询新消息(after_id 增量拉取)
- consultations API 补充 after_id 参数

小程序患者端:
- 新增 consultation service 消息 API(listMessages/sendMessage/markSessionRead)
- 新增聊天详情页(8s 轮询 + 发送消息 + 自动标记已读)
- 咨询列表页点击跳转详情页(替换"即将上线"占位)
This commit is contained in:
iven
2026-04-26 14:40:46 +08:00
parent 4f4a44ddb6
commit 5bb6105127
7 changed files with 400 additions and 4 deletions

View File

@@ -86,7 +86,7 @@ export const consultationApi = {
listMessages: async (
sessionId: string,
params: { page?: number; page_size?: number },
params: { page?: number; page_size?: number; after_id?: string },
) => {
const { data } = await client.get<{
success: boolean;

View File

@@ -9,6 +9,7 @@ import { useThemeMode } from '../../hooks/useThemeMode';
import { AuthButton } from '../../components/AuthButton';
const PAGE_SIZE = 30;
const POLL_INTERVAL = 10_000;
function formatTime(value: string): string {
return new Date(value).toLocaleString('zh-CN', {
@@ -54,6 +55,7 @@ export default function ConsultationDetail() {
const chatEndRef = useRef<HTMLDivElement>(null);
const shouldScrollRef = useRef(true);
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null);
const isDark = useThemeMode();
@@ -103,6 +105,39 @@ export default function ConsultationDetail() {
fetchMessages(1, false);
}, [fetchSession, fetchMessages]);
// Poll new messages while session is active
useEffect(() => {
if (!session || session.status === 'closed') return;
const stopPolling = () => {
if (pollRef.current) {
clearInterval(pollRef.current);
pollRef.current = null;
}
};
stopPolling();
pollRef.current = setInterval(async () => {
if (!sessionId) return;
try {
const lastId = messages.length > 0 ? messages[messages.length - 1].id : undefined;
const result = await consultationApi.listMessages(sessionId, {
page: 1,
page_size: 50,
after_id: lastId,
});
if (result.data.length > 0) {
setMessages((prev) => [...prev, ...result.data]);
shouldScrollRef.current = true;
}
} catch {
// silent
}
}, POLL_INTERVAL);
return stopPolling;
}, [session?.status, sessionId, messages.length]);
// Auto-scroll to bottom on new messages
useEffect(() => {
if (shouldScrollRef.current && chatEndRef.current) {