"use client"; import { FormEvent, useEffect, useMemo, useRef, useState } from "react"; import { BookOpenCheck, Clock3, Home, MessageCircle, Plus, RotateCcw, SendHorizonal, Settings, ShieldCheck, Trash2, UserRound } from "lucide-react"; import { api } from "@/lib/api"; import type { CallableQAItem } from "@/lib/types"; import { createEmptySession, findQaAnswer, initialMessages, mockCallableQa, suggestedQuestions, summarizeSession, type ChatSession, type ChatMessage } from "@/lib/h5MockQa"; import ChatBubble from "./ChatBubble"; import MockAuthPanel, { type MockUser } from "./MockAuthPanel"; type MobileTab = "qa" | "history" | "me"; const STORAGE_KEY = "hy-qa-h5-sessions"; function nowText() { return new Intl.DateTimeFormat("zh-CN", { hour: "2-digit", minute: "2-digit", hour12: false }).format(new Date()); } function buildAnswerMessage(question: string, qa: CallableQAItem): ChatMessage { return { id: `assistant-${Date.now()}`, role: "assistant", content: qa.standard_answer, source: `${qa.standard_code} / ${qa.primary_topic} / ${qa.course_stage}`, boundary: qa.answer_boundary ?? "答复仅基于已审核答疑资产生成,不能替代专业诊断、法律判断或医疗建议。", createdAt: nowText() }; } function formatSessionTime(value: string) { return new Intl.DateTimeFormat("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hour12: false }).format(new Date(value)); } export default function H5QAExperience() { const [activeTab, setActiveTab] = useState("qa"); const [authOpen, setAuthOpen] = useState(false); const [input, setInput] = useState(""); const [messages, setMessages] = useState(initialMessages); const [sessions, setSessions] = useState([]); const [currentSessionId, setCurrentSessionId] = useState(""); const [callableQa, setCallableQa] = useState([]); const [loadingQa, setLoadingQa] = useState(true); const [user, setUser] = useState(null); const [copiedMessageId, setCopiedMessageId] = useState(null); const bottomRef = useRef(null); const qaSource = useMemo(() => (callableQa.length > 0 ? callableQa : mockCallableQa), [callableQa]); useEffect(() => { const fallback = createEmptySession(); try { const saved = window.localStorage.getItem(STORAGE_KEY); const parsed = saved ? (JSON.parse(saved) as ChatSession[]) : []; const validSessions = parsed.length > 0 ? parsed : [fallback]; setSessions(validSessions); setCurrentSessionId(validSessions[0].id); setMessages(validSessions[0].messages); } catch { setSessions([fallback]); setCurrentSessionId(fallback.id); setMessages(fallback.messages); } }, []); useEffect(() => { if (sessions.length > 0) { window.localStorage.setItem(STORAGE_KEY, JSON.stringify(sessions)); } }, [sessions]); useEffect(() => { api .get("/standard-qa/callable") .then(setCallableQa) .catch(() => setCallableQa([])) .finally(() => setLoadingQa(false)); }, []); useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: "smooth", block: "end" }); }, [messages, activeTab]); function mockLogin(mode: MockUser["mode"]) { setUser({ mode, name: mode === "login" ? "大本营学员" : "新学员", phone: "138****2026" }); setAuthOpen(false); } function persistMessages(nextMessages: ChatMessage[]) { setMessages(nextMessages); setSessions((current) => { const sessionId = currentSessionId || createEmptySession().id; const nextSession = summarizeSession(sessionId, nextMessages); const withoutCurrent = current.filter((session) => session.id !== sessionId); return [nextSession, ...withoutCurrent].slice(0, 20); }); } function startNewSession() { const nextSession = createEmptySession(); setCurrentSessionId(nextSession.id); setMessages(nextSession.messages); setSessions((current) => [nextSession, ...current].slice(0, 20)); setActiveTab("qa"); } function openSession(session: ChatSession) { setCurrentSessionId(session.id); setMessages(session.messages); setActiveTab("qa"); } function clearHistory() { const nextSession = createEmptySession(); setCurrentSessionId(nextSession.id); setMessages(nextSession.messages); setSessions([nextSession]); setActiveTab("qa"); } async function copyMessage(message: ChatMessage) { try { await navigator.clipboard.writeText(message.content); } catch { // Some embedded webviews/headless browsers block clipboard writes. // Keep the UI feedback so the user knows the copy action was triggered. } setCopiedMessageId(message.id); window.setTimeout(() => setCopiedMessageId(null), 1400); } function setFeedback(messageId: string, feedback: "liked" | "disliked") { const nextMessages = messages.map((message) => message.id === messageId ? { ...message, feedback: message.feedback === feedback ? undefined : feedback } : message ); persistMessages(nextMessages); } function ask(question: string) { const trimmed = question.trim(); if (!trimmed) return; const matchedQa = findQaAnswer(trimmed, qaSource); const timestamp = Date.now(); const nextMessages: ChatMessage[] = [ ...messages, { id: `user-${timestamp}`, role: "user", content: trimmed, createdAt: nowText() }, { ...buildAnswerMessage(trimmed, matchedQa), id: `assistant-${timestamp}` } ]; persistMessages(nextMessages); setInput(""); setActiveTab("qa"); } function submit(event: FormEvent) { event.preventDefault(); ask(input); } return (

千问千答助手

{loadingQa ? "正在检查可调用库" : callableQa.length > 0 ? "已连接可调用库" : "演示模式"}
{copiedMessageId ? 已复制回答 : null}
{activeTab === "qa" ? ( <>
回答会优先遵守审核边界;涉及医疗、法律、极端风险或个人隐私时,请以人工复核为准。
{messages.map((message) => ( ))}
{suggestedQuestions.map((item) => ( ))}