'use client'; import { FormEvent, KeyboardEvent, MouseEvent, useMemo, useState } from 'react'; import { BotMessageSquare, Image as ImageIcon, Maximize2, MessageCircleQuestionMark, Mic, Search, SendHorizontal, Sparkles, SquarePen, Trash2, X, } from 'lucide-react'; import { WENFAN_HELP_ARTICLES, type HelpArticle } from '@/lib/wenfan-help-articles'; import { createWenfanConversationRecord, deleteWenfanConversationRecord, updateWenfanConversationRecord, type WenfanConversationRecord, } from '@/lib/wenfan-conversation-history'; import { getFallbackHelpMessage, getFallbackHelpSuggestions, searchHelpArticles, shouldShowGenericHelpSuggestions, type HelpSearchResult, } from '@/lib/wenfan-help-search'; type ChatMessage = | { id: string; role: 'assistant'; type: 'intro'; content: string; } | { id: string; role: 'user'; type: 'text'; content: string; } | { id: string; role: 'assistant'; type: 'article'; result: HelpSearchResult; } | { id: string; role: 'assistant'; type: 'fallback'; content: string; suggestions: string[]; }; const INITIAL_MESSAGES: ChatMessage[] = [ { id: 'intro', role: 'assistant', type: 'intro', content: '我是问翻小宝,第一阶段只从内置帮助中心回答系统怎么用,不调用 AI,也不消耗模型 token。你可以问产品、项目、版本、需求池、开发任务、测试用例、Bug 和日志记录。', }, ]; const STARTER_QUESTIONS = getFallbackHelpSuggestions(); type WenfanConversation = WenfanConversationRecord; const INITIAL_CONVERSATIONS: WenfanConversation[] = [ { id: 'history-product-create', title: '怎么新建产品?', messages: INITIAL_MESSAGES }, { id: 'history-version-requirements', title: '怎么把需求纳入版本?', messages: INITIAL_MESSAGES }, { id: 'history-dev-task-submit', title: '开发任务怎么提测?', messages: INITIAL_MESSAGES }, { id: 'history-test-bug', title: '测试用例失败后怎么提 Bug?', messages: INITIAL_MESSAGES }, { id: 'history-activity-log', title: '日志记录会记录哪些行为?', messages: INITIAL_MESSAGES }, ]; export default function WenfanXiaobaoPage() { const [messages, setMessages] = useState(INITIAL_MESSAGES); const [input, setInput] = useState(''); const [conversations, setConversations] = useState(INITIAL_CONVERSATIONS); const [activeConversationId, setActiveConversationId] = useState(null); const visibleHistory = useMemo(() => conversations.slice(0, 12), [conversations]); const userQuestionCount = useMemo( () => messages.filter((message) => message.role === 'user').length, [messages], ); const showGenericSuggestions = shouldShowGenericHelpSuggestions(userQuestionCount); const showStarterQuestions = showGenericSuggestions && userQuestionCount === 0; function handleNewConversation() { const id = `conversation-${Date.now()}`; const record = createWenfanConversationRecord(id, INITIAL_MESSAGES); setConversations((current) => [record, ...current]); setActiveConversationId(id); setMessages(INITIAL_MESSAGES); setInput(''); } function openConversation(conversation: WenfanConversation) { setActiveConversationId(conversation.id); setMessages(conversation.messages); setInput(''); } function deleteConversation(event: MouseEvent, id: string) { event.stopPropagation(); setConversations((current) => deleteWenfanConversationRecord(current, id)); if (activeConversationId === id) { setActiveConversationId(null); setMessages(INITIAL_MESSAGES); setInput(''); } } function askQuestion(rawQuestion: string) { const question = rawQuestion.trim(); if (!question) return; const results = searchHelpArticles(question, WENFAN_HELP_ARTICLES); const nextUserQuestionCount = userQuestionCount + 1; const showFallbackSuggestions = shouldShowGenericHelpSuggestions(nextUserQuestionCount); const userMessage: ChatMessage = { id: `user-${Date.now()}`, role: 'user', type: 'text', content: question, }; const assistantMessage: ChatMessage = results.length > 0 ? { id: `article-${Date.now()}`, role: 'assistant', type: 'article', result: results[0], } : { id: `fallback-${Date.now()}`, role: 'assistant', type: 'fallback', content: getFallbackHelpMessage(showFallbackSuggestions), suggestions: showFallbackSuggestions ? STARTER_QUESTIONS : [], }; const nextMessages = [...messages, userMessage, assistantMessage]; setMessages(nextMessages); setConversations((current) => updateWenfanConversationRecord(current, activeConversationId, nextMessages)); setInput(''); } function handleSubmit(event: FormEvent) { event.preventDefault(); askQuestion(input); } function handleTextareaKeyDown(event: KeyboardEvent) { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); askQuestion(input); } } return (

问翻小宝

内置帮助
{messages.map((message) => ( ))} {showStarterQuestions && (
{STARTER_QUESTIONS.map((question) => ( ))}
)}