山东大学软件学院项目实训进展记录6

山东大学软件学院项目实训进展记录6 一、本周工作概述本周我的核心任务是完成亮点分析模块和个性化学习页面的基础实现。核心思路是复用已有组件降低维护成本通过状态管理统一数据流向优先保证核心业务逻辑的正确性。没有盲目追求界面效果而是先把数据从后端到前端的完整链路打通再逐步优化交互体验。目前亮点分析模块已实现原文标注 详细赏析个性化学习页面的两个核心子页面均已完成基础功能开发并对接接口。二、核心工作进展1. 亮点分析模块亮点分析模块的核心需求是识别作文中的高级词汇和优秀表达在原文中标注并生成 亮点类型 亮点定位 赏析解释 的完整说明。我没有单独开发一套亮点标注系统而是选择复用已经开发的文本高亮架构这样做的好处是避免重复开发文本解析和位置定位逻辑统一错误和亮点的展示风格后续维护只需要修改一套代码。1数据层设计亮点数据和错误数据结构高度相似都包含位置偏移量、类型、文本和解释字段。后端返回的亮点数据会存储在assessmentStore.ts中所有页面共享同一份数据。2原文亮点标注实现在EssayViewer.vue中我扩展了原有的错误高亮逻辑增加了亮点的样式区分和点击事件。核心思路是通过后端返回的startOffset和endOffset字符偏移量使用浏览器原生的Range API精确找到对应的文本节点然后包裹高亮标签。亮点使用绿色系样式错误使用红色系样式通过 CSS 类名区分避免了样式冲突。function highlightText(container: HTMLElement, highlights: Highlight[]) { // 先清除所有已有高亮 clearAllHighlights(container); const textNodes getTextNodes(container); let currentOffset 0; highlights.forEach(highlight { const range document.createRange(); let found false; for (const node of textNodes) { const nodeLength node.textContent?.length || 0; if (currentOffset nodeLength highlight.startOffset) { // 找到包含亮点的文本节点 range.setStart(node, highlight.startOffset - currentOffset); range.setEnd(node, Math.min(highlight.endOffset - currentOffset, nodeLength)); const span document.createElement(span); span.className highlight highlight--${highlight.type}; span.dataset.highlightId highlight.id; span.title highlight.explanation; range.surroundContents(span); found true; break; } currentOffset nodeLength; } }); }3亮点详情与词云可视化在ResultPage.vue中我添加了独立的 亮点展示区遍历assessmentStore.highlights数组逐条展示亮点类型、原文片段和详细赏析词汇类亮点通过生成词云图直观展示作文中的高频高级词汇。词云使用纯 SVG 实现没有引入第三方库减少了包体积2. 个性化学习页面个性化学习页面包含错误画像展示、错题收集与展示、个性化建议展示、历史评估记录四个核心功能。我将其拆分为两个独立页面ProfilePage.vue个人学习画像展示整体学习趋势和长期错误分布NotebookPage.vue错题本专注于错题的筛选、查看和复习1个人画像页面个人画像页面的核心是让用户清晰看到自己的进步和短板。我主要实现了以下功能核心指标展示累计作文数、当前平均分、主导错误类型、下一步建议平均分趋势图使用BaseEChart.vue组件绘制折线图展示最近 10 次作文的分数变化长期错误分布使用SvgMeterList.vue组件展示四大类错误的占比本周学习路径展示后端生成的个性化学习建议其中平均分趋势图的配置逻辑如下const trendOption computedEChartsOption(() { const scores profile.value?.recentScores ?? []; return { animation: false, grid: { top: 16, left: 30, right: 16, bottom: 22 }, xAxis: { type: category, data: scores.map((_, index) D${index 1}), axisLine: { lineStyle: { color: #ebebeb } }, axisLabel: { color: #666666, fontSize: 11 } }, yAxis: { type: value, min: 0, max: 9, splitNumber: 3, axisLabel: { color: #666666, fontSize: 11 } }, series: [ { type: line, smooth: false, // 折线不做平滑处理更准确反映分数波动 data: scores, symbol: circle, symbolSize: 7, lineStyle: { color: #171717, width: 2 } } ] }; });关闭了 ECharts 的默认动画因为对于学习数据来说准确性比视觉效果更重要。同时将 Y 轴固定为 0-9 分符合雅思评分标准避免图表自动缩放导致的视觉误导。2错题本页面错题本是个性化学习中最核心的功能我采用了左右分栏布局左侧是错题列表右侧是当前选中错题的详情。这种布局符合用户 先筛选、再查看、后复习 的操作流程。核心功能实现多维度筛选支持按错误类型语法 / 用词 / 句法 / 逻辑、作文题型、复习状态待复习 / 已复习筛选错题错题详情展示展示原句、问题解释、推荐改写、重写句子标记已复习点击按钮将错题标记为已复习状态会同步到notebookStore其中标记已复习的状态管理逻辑在notebookStore.ts中实现async markReviewed(id: string) { this.reviewActionState submitting; this.errorMessage ; try { await http.post(/api/notebook/items/${id}/review); // 本地更新状态不需要重新请求接口 this.items updateReviewedItem(this.items, id); this.summary buildSummary(this.items, this.summary); this.reviewActionState success; } catch (error) { this.reviewActionState error; this.errorMessage normalizeHttpError(error, 标记复习失败); } }这里我做了一个优化后端返回成功后直接在本地更新错题状态和统计数据而不是重新请求整个错题列表大大提升了响应速度。三、遇到的问题与解决方案1. 状态同步不及时问题问题标记错题已复习后页面顶部的 复习率 统计没有自动更新。解决方案在notebookStore.ts的markReviewed action 中添加了buildSummary函数每次更新错题状态后重新计算统计数据总题数、已复习数、待复习数。这样所有依赖summary的计算属性都会自动更新保证了状态的一致性。2. 图表自适应问题问题BaseEChart.vue组件在窗口缩放或侧边栏收起时图表不会自动调整大小会出现溢出或留白。解决方案在BaseEChart.vue中添加ResizeObserver监听容器尺寸变化当容器大小改变时调用 ECharts 的resize()方法重新渲染图表。onMounted(() { renderChart(); if (containerRef.value) { resizeObserver new ResizeObserver(() { chart?.resize(); }); resizeObserver.observe(containerRef.value); } });四、本周工作成果亮点分析模块原文亮点标注、亮点详情展示、词汇词云生成个人画像页面核心指标展示、平均分趋势图、长期错误分布、学习路径展示错题本页面错题列表、多维度筛选、错题详情、标记已复习接口对接全部使用后端真实数据移除了所有静态假数据五、本周感悟本周最大的收获是理解了 状态管理是前端项目的核心 这句话。一开始我把错题数据存在NotebookPage.vue的组件内部状态里后来发现ProfilePage.vue也需要用到错题统计数据不得不把数据提升到全局状态。这让我意识到在项目开始前就应该规划好哪些数据是全局共享的哪些是组件内部的否则后期重构会非常麻烦。另外复用组件能大大提高开发效率。亮点分析模块如果从零开始开发至少需要三天时间但通过复用 EssayViewer 的高亮架构我只用了一天就完成了核心功能。这也让我明白了在写代码的时候要多思考尽量写出可复用的代码。