《60天AI学习计划启动 | Day 50: 知识中心页面(搜索 + 文档 + QA 一体化)》
Day 50:知识中心页面(搜索 + 文档 + QA 一体化)
学习目标
- 设计 一个“搜索 + 文档列表 + 文档详情 + QA 区”的整体布局
- 抽象 文档与搜索结果的数据结构
- 实现 一个最小可用的知识中心页面骨架(前端视角)
核心知识点
-
页面布局(典型三栏):
- 左:搜索框 + 过滤条件 + 搜索结果列表
- 中:选中文档详情(标题 + 内容预览 + 版本信息)
- 右/下:基于当前文档的 RAG 问答区
-
数据模型示例:
export interface KnowledgeDoc { id: string title: string summary: string content?: string tags?: string[] updatedAt: number } export interface SearchResult { docId: string highlight: string score: number }
实战代码示例(页面骨架)
import React, { useState } from 'react'
import type { KnowledgeDoc, SearchResult } from './types'
interface Props {
docs: KnowledgeDoc[]
}
export const KnowledgeCenter: React.FC<Props> = ({ docs }) => {
const [query, setQuery] = useState('')
const [results, setResults] = useState<SearchResult[]>([])
const [activeDoc, setActiveDoc] = useState<KnowledgeDoc | null>(null)
const [question, setQuestion] = useState('')
const [answer, setAnswer] = useState('')
const runSearch = () => {
// demo:前端简单过滤,本质应调后端搜索
const list = docs
.filter(d => d.title.includes(query) || d.summary.includes(query))
.map(d => ({
docId: d.id,
highlight: d.summary.slice(0, 60),
score: 1
}))
setResults(list)
if (list[0]) {
const doc = docs.find(d => d.id === list[0].docId) || null
setActiveDoc(doc)
}
}
const ask = async () => {
if (!activeDoc || !question.trim()) return
const res = await fetch('/api/knowledge-qa', {
method: 'POST',
headers: { 'Content-Type':'application/json' },
body: JSON.stringify({ docId: activeDoc.id, question: question.trim() })
})
const data = await res.json()
setAnswer(data.answer ?? '')
}
return (
<div style={{ display:'grid', gridTemplateColumns:'260px 1fr 320px', height:'100vh' }}>
{/* 左:搜索 + 结果 */}
<div style={{ borderRight:'1px solid #eee', padding:8 }}>
<h3>知识搜索</h3>
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="输入关键词"
style={{ width:'100%' }}
/>
<button onClick={runSearch}>搜索</button>
<ul>
{results.map(r => {
const doc = docs.find(d => d.id === r.docId)!
return (
<li key={r.docId} onClick={() => setActiveDoc(doc)} style={{ cursor:'pointer', marginTop:4 }}>
<div>{doc.title}</div>
<div style={{ fontSize:12, color:'#666' }}>{r.highlight}...</div>
</li>
)
})}
</ul>
</div>
{/* 中:文档详情 */}
<div style={{ borderRight:'1px solid #eee', padding:8, overflowY:'auto' }}>
{activeDoc ? (
<>
<h2>{activeDoc.title}</h2>
<p style={{ color:'#666' }}>{activeDoc.summary}</p>
<pre style={{ whiteSpace:'pre-wrap' }}>{activeDoc.content}</pre>
</>
) : (
<div>请选择左侧文档</div>
)}
</div>
{/* 右:QA 区 */}
<div style={{ padding:8, display:'flex', flexDirection:'column' }}>
<h3>基于当前文档的问答</h3>
<textarea
rows={4}
value={question}
onChange={e => setQuestion(e.target.value)}
placeholder="针对右侧文档提问"
/>
<button onClick={ask}>提问</button>
<div style={{ marginTop:8, flex:1, overflowY:'auto' }}>
<pre style={{ whiteSpace:'pre-wrap' }}>{answer}</pre>
</div>
</div>
</div>
)
}
明日学习计划预告(Day 51)
- 主题:第二个综合实战项目选型与需求拆解
- 方向:
- 选一个「前端 + AI」具体场景(代码助手 / 评审助手 / 设计检查等)
- 写出用户故事、主要功能列表、数据流草图,为 Day 52–55 实战做准备

浙公网安备 33010602011771号