前端实战:用纯HTML/CSS/JS构建CPA备考知识库系统——搜索+记忆卡+知识图谱+AI问答
系统架构
┌──────────────────────────────────────────────┐
│ 四大功能模块 │
├──────────────┬──────────────┬────────────────┤
│ 知识检索 │ 记忆卡片 │ 知识图谱 │
│ 173篇文档 │ 87张卡片 │ 30个概念节点 │
│ 模糊搜索 │ 间隔复习 │ Canvas绘制 │
│ 评分排序 │ 队列管理 │ 悬浮交互 │
├──────────────┴──────────────┴────────────────┤
│ AI智能问答(RAG) │
│ DeepSeek API + 知识库上下文注入 │
└──────────────────────────────────────────────┘
数据结构设计
知识文档
每篇文档是一个对象,包含标题、内容、科目、来源、关键词:
javascript
const CPA_DOCS = [
{
id: “doc_001”,
f: “长期股权投资.docx”, // 文件名
c: “注册会计师·会计·长期股权投资”, // 分类
sub: “会计”, // 科目
t: “东奥·张志凤”, // 来源
j: “基础阶段”, // 阶段
tl: 15200, // 字数
kw: [“长投”, “成本法”, “权益法”], // 关键词
ft: “长期股权投资是指…” // 全文内容
}
// … 173篇
];
记忆卡片
javascript
const CPA_CARDS = [
{ q: “长期股权投资成本法转权益法的步骤”, a: “1.按原持股比例确认投资收益…” },
{ q: “合并报表抵消分录的核心逻辑”, a: “母公司长期股权投资与子公司所有者权益抵消…” }
// … 87张
];
知识图谱节点与连线
javascript
var CONCEPT_NODES = [
{ id: 0, name: “长期股权投资”, category: “会计” },
{ id: 1, name: “合并报表”, category: “会计” },
{ id: 6, name: “审计报告”, category: “审计” },
{ id: 11, name: “增值税”, category: “税法” },
{ id: 16, name: “战略分析”, category: “战略” },
{ id: 21, name: “NPV”, category: “财管” },
{ id: 25, name: “公司法”, category: “经济法” }
// … 30个节点
];
var CONCEPT_LINKS = [
{ source: 0, target: 1, label: “会计” },
{ source: 1, target: 2, label: “会计->税法” }
];
模糊搜索:自定义评分算法
CPA知识点的搜索需求很特殊——用户可能输入"长股投"来搜"长期股权投资",输入"五力"来搜"波特五力模型"。标准 indexOf 搞不定,需要模糊匹配。
第一步:文本归一化
javascript
function norm(s) {
return (s || ‘’).toLowerCase().replace(/[\s-,.!()]/g, ‘’);
}
去掉空格、连字符、中文标点,全部转小写。“长 股 投” 和 “长股投” 归一化后一致。
第二步:模糊匹配
javascript
function fuzzy(text, q) {
if (!q) return true;
var t = norm(text), n = norm(q);
// 完全包含 → 直接命中
if (t.indexOf(n) >= 0) return true;
// 逐字检查:每个中文字都必须出现
for (var i = 0; i < n.length; i++) {
var c = n[i];
if (c >= ‘\u4e00’ && c <= ‘\u9fa5’ && t.indexOf© < 0) return false;
}
return true;
}
核心逻辑:如果 norm(text) 包含 norm(query) 则直接命中;否则对查询串逐字检查,每个中文字符都必须出现在目标文本中。“长股投” 能匹配 “长期股权投资”,因为"长"“股”"投"三个字都在。
第三步:评分排序
javascript
function score(d, q) {
var l = q.toLowerCase(), s = 0;
if (norm(d.f).indexOf(l) >= 0) s += 10; // 文件名命中 +10
if (norm(d.c).indexOf(l) >= 0) s += 8; // 分类命中 +8
if (d.kw) for (var i = 0; i < d.kw.length; i++) {
if (norm(d.kw[i]).indexOf(l) >= 0) { s += 6; break; } // 关键词命中 +6
}
if (norm(d.ft || ‘’).indexOf(l) >= 0) s += 2; // 全文命中 +2
return s;
}
不同字段命中得分不同:文件名 > 分类 > 关键词 > 全文。搜索结果按得分降序排列,最相关的排最前面。
记忆卡片:间隔复习队列
数据结构
javascript
var reviewQueue = []; // 待复习队列
var reviewState = { known: 0, maybe: 0, again: 0 };
核心流程
javascript
function initCards() {
// 复制全部卡片到队列
reviewQueue = CPA_CARDS.map(function© { return { q: c.q, a: c.a }; });
showCard();
}
function rateCard(t) {
var c = reviewQueue.shift(); // 取出当前卡片
if (t === ‘know’) {
reviewState.known++; // 记住了 → 移出队列
} else if (t === ‘again’) {
reviewQueue.push©; // 不认识 → 放回队尾
reviewState.again++;
} else {
reviewQueue.push©; // 模糊 → 也放回队尾
reviewState.maybe++;
}
showCard(); // 显示下一张
}
三档评分:
记住了(green)→ 卡片移出队列,不再出现
模糊(yellow)→ 卡片放回队尾,稍后再练
不认识(red)→ 卡片放回队尾,稍后再练
这是简化的 Anki 算法——不做时间间隔调度,只做队列循环。简单但有效:全部"记住"即完成复习。
知识图谱:Canvas 环形布局
节点环形排列
javascript
function drawGraph() {
var cvs = document.getElementById(‘graphCanvas’);
var ctx = cvs.getContext(‘2d’);
var dpr = window.devicePixelRatio || 1;
var rect = cvs.getBoundingClientRect();
cvs.width = rect.width * dpr;
cvs.height = rect.height * dpr;
ctx.scale(dpr, dpr);
var W = rect.width, H = rect.height;
// 环形布局:每个节点均匀分布在圆周上
var placed = CONCEPT_NODES.map(function(n, i) {
var a = (i / CONCEPT_NODES.length) * 2 * Math.PI - Math.PI / 2;
var r = Math.min(W, H) * 0.35;
return {
x: W / 2 + r * Math.cos(a),
y: H / 2 + r * Math.sin(a),
color: NC[n.category] || ‘#95a5a6’,
name: n.name,
category: n.category
};
});
30个概念节点均匀分布在圆周上,角度 = (索引/总数) × 2π - π/2,起始角度为正上方。r = min(W, H) * 0.35 确保节点不超出画布。
高DPI适配
javascript
var dpr = window.devicePixelRatio || 1;
cvs.width = rect.width * dpr;
cvs.height = rect.height * dpr;
ctx.scale(dpr, dpr);
Retina 屏幕下 devicePixelRatio = 2,如果不做适配,Canvas 会被放大模糊。设置物理像素 = CSS像素 × dpr,再 scale(dpr, dpr),绘制清晰。
悬浮高亮
javascript
cvs.addEventListener(‘mousemove’, function(e) {
var mx = e.clientX - rect.left, my = e.clientY - rect.top;
hov = -1;
for (var i = 0; i < placed.length; i++) {
if (Math.hypot(mx - placed[i].x, my - placed[i].y) < 16) {
hov = i;
break;
}
}
redraw(); // 重绘,悬浮节点放大
});
鼠标移动时,检测鼠标坐标与每个节点的距离(Math.hypot),小于16px则判定为悬浮。悬浮时节点半径从11变为16,外圈光晕也放大。
点击跳转搜索
javascript
cvs.addEventListener(‘click’, function() {
if (hov >= 0) {
document.getElementById(‘searchInput’).value = placed[hov].name;
switchTab(‘search’, document.getElementById(‘tab-search’));
doSearch();
}
});
点击图谱节点 → 自动切到搜索页 → 用节点名称搜索 → 显示相关文档。知识图谱不只是好看,还是导航工具。
AI问答:RAG 检索增强
上下文注入
javascript
async function sendChat() {
var msg = inp.value.trim();
if (!msg) return;
addMsg(‘user’, msg);
inp.value = ‘’;
addMsg(‘ai’, ‘正在思考…’);
// 从知识库检索相关文档
var relevant = CPA_DOCS.filter(function(d) {
return fuzzy(d.f + ’ ’ + (d.kw || []).join(’ ') + ’ ’ + (d.ft || ‘’), msg);
}).slice(0, 5);
// 构建上下文字符串
var ctxStr = relevant.map(function(d) {
return ‘[来源:’ + (d.t || ‘东奥’) + ‘]\n’ + (d.ft || ‘’).slice(0, 800);
}).join(‘\n\n’);
var sysPrompt = ‘你是CPA备考知识库助手。\n’ +
‘1.结合知识库内容回答,不要编造。\n’ +
‘2.回答后注明来源。\n’ +
‘3.控制在200字以内,条理清晰。\n\n’ +
‘知识库摘要:\n’ + ctxStr;
// 调用DeepSeek API
var resp = await fetch(‘https://api.deepseek.com/chat/completions’, {
method: ‘POST’,
headers: {
‘Authorization’: 'Bearer ’ + apiKey,
‘Content-Type’: ‘application/json’
},
body: JSON.stringify({
model: ‘deepseek-chat’,
messages: [
{ role: ‘system’, content: sysPrompt },
{ role: ‘user’, content: msg }
],
max_tokens: 600,
temperature: 0.3
})
});
核心思路:用户提问 → 先用模糊搜索从知识库检索 Top 5 相关文档 → 截取每篇前800字 → 拼接成上下文 → 注入到 system prompt → 发送给大模型。
temperature: 0.3 低温度保证回答稳定,max_tokens: 600 控制回答长度。API Key 存 localStorage,不上传任何服务器。
暗黑模式:CSS变量切换
css
:root {
–bg: #f0f2f5;
–card: #fff;
–text: #1a1a2e;
–primary: #4a6cf7;
}
[data-theme=“dark”] {
–bg: #0f0f23;
–card: #1a1a3e;
–text: #e0e0f0;
–primary: #6b8aff;
}
javascript
function toggleTheme() {
var d = document.documentElement;
var isDark = d.getAttribute(‘data-theme’) === ‘dark’;
d.setAttribute(‘data-theme’, isDark ? ‘’ : ‘dark’);
localStorage.setItem(‘cpa_theme’, isDark ? ‘light’ : ‘dark’);
}
所有颜色用 CSS 变量,切换>
CPA备考知识库大师
智能交互版 · 173篇文档 · 87张记忆卡 · 5科全覆盖
CPA核心概念知识图谱 · 点击节点搜索详情