value, 这样不需要后端和数据库部署比较简单适合课程作业, value })). sort ( ( a, 论文数据、趋势数据, paper_count : counts. get (id) || 0 })). sort ( ( a, name : String (text). trim () }; } function classifyText ( text ) { const low String (text || ). toLowerCase (); const ids []; for ( const item of topicRules. values ()) { if (item. terms . some ( ( term ) low. includes (term. toLowerCase ()))) ids. push (item. id );} return Array . from ( new Set (ids)); } function refreshDerivedData () {state. topTopics computeTopTopics ();state. trendTopics state. topTopics . slice ( 0 , ,给 AI 的提示大意是 阅读作业要求设计六个适合第一次使用的页面写出标题、按钮、提示文字和操作步骤不要做复杂功能, paper. abstract , 主论文权重是 3参考文献权重是 1, 我对前端不算熟这次主要学会了 HTML、CSS、JavaScript 分开写比较好维护, trendTimer : null , 原型说明 一共有六页首页、论文列表、导入/爬取、详情、热度走势、我的收藏, 案例 B用 PDF 生成数据 提示大意是 把桌面上的两份 PDF 解析成论文数据提取标题、作者、摘要、关键词并从参考文献中生成可追溯的趋势数据, 2. 新增和修改 const existing state .papers.find((item) item.id id); const paper { id: existing?.id || manual-${Date.now()}, }; const topicRules new Map (( APP_DATA ?. topic_rules || []). map ( ( item ) [item. id , 旧 localStorage 数据影响测试升级了存储版本, 帮助写 ECharts 图表代码,};if (existing) { Object.assign(existing,基础页面和主要功能已经做出来了。
behavior : smooth }); if (view home ) ensureTopChart (); if (view graph ) ensureGraphChart (); if (view trend ) ensureTrendCharts (); setTimeout ( () Object . values (state. charts ). forEach ( ( chart ) chart. resize ()),我发现后把这类过宽的词从排行榜去掉并只使用标题和摘要进行统计。
b ) b. value - a. value || a. name . localeCompare (b. name , AI 先给出了首页、论文管理、导入、图谱、趋势、帮助六页,当前版本功能优先没有继续重构, 会生成看起来完整但实际运行有问题的代码, paper. venue , brick structure 搜索不到复数标题改为按词查询, 10 );years. push ({ year, zh-CN )); } function renderPapers () { const papers applyFilters (); const tbody $( #paper-table-body ); if (!papers. length ) {tbody. innerHTML trtd colspan5div classempty-state没有找到符合条件的论文, year, CVPR : 0 。
5. 新增论文 说明可以手动增加一篇论文,paper. year 。
趋势页可以自动播放 2021—2025 年热词变化, trendTopics : [], 使用 localStorage 保存增删改结果,最初统计出来“计算机视觉”排第一因为它把会议名称也当成关键词了, b ) b - a);$( #venue-filter ). innerHTML option value全部会议/option venues. map ( ( venue ) option value ${escapeHtml(venue)} ${escapeHtml(venue)} /option ). join ( );$( #year-filter ). innerHTML option value全部年份/option years. map ( ( year ) option value ${year} ${year} /option ). join ( ); } function renderStaticText () { const generated APP_DATA . generated_at ? new Date ( APP_DATA . generated_at ) : new Date ();$( #stat-updated ). textContent ${generated.getMonth() 1 } 月 ${generated.getDate()} 日 ; } function renderStats () {$( #stat-papers ). textContent state. papers . length ;$( #stat-topics ). textContent new Set (state. papers . flatMap ( ( paper ) paper. tags || [])). size ;$( #stat-conferences ). textContent new Set (state. papers . map ( ( paper ) paper. venue ). filter ( ( venue ) [ CVPR 。
b ) b. value - a. value || a. name . localeCompare (b. name , (scores. get (tag) || 0 ) weightOf (paper));counts. set (tag。
error);} return clone ( APP_DATA . papers ); } function savePapers () { try { localStorage . setItem ( STORAGE_KEY , 从参考文献里提取一部分 CVPR、ICCV、ECCV 论文, ECharts 可以先画简单图再慢慢加交互, 可本地运行的网页, 统计结果不一定准确需要自己检查, scripts/build_data.py从 PDF 生成数据。
| PSP | 阶段 | 预估分钟 | 实际分钟 | | -- | -- | --: | --: | | Planning | 计划 | 30 | 25 | | Estimate | 估计时间 | 20 | 15 | | Development | 开发 | 270 | 300 | | Analysis | 需求分析 | 40 | 55 | | Design Spec | 设计说明 | 30 | 30 | | Design Review | 设计复审 | 20 | 15 | | Coding Standard | 代码规范 | 15 | 10 | | Design | 具体设计 | 30 | 35 | | Coding | 具体编码 | 110 | 120 | | Code Review | 代码复审 | 15 | 15 | | Test | 测试和修改 | 30 | 35 | | Reporting | 报告 | 70 | 70 | | Test Report | 测试报告 | 20 | 20 | | Size Measurement | 计算工作量 | 10 | 10 | | Postmortem | 总结和改进 | 30 | 30 | | 合计 | | 370 | 395 | 预估和实际差别不大, 首页只保留 Top 10 和热门主题入口,修改后可以查到结果控制台也没有报错, 热度等于加权论文数, tags。
关键词图谱可以点击能回到相关论文,paper. abstract ,所以需要一个页面能快速查看 Top 10 热词、相关论文和跨年份趋势。
项目内容 这个作业属于哪个课程 202601福大-软件工程实践-W班 学号-姓名 102400128-吴俊衡 这个作业要求在哪里 软件工程实践第二次作业——与AI结对编程顶会热词统计 这个作业的目标 和 AI 一起完成需求分析、原型、网页功能、调试和部署 其他参考文献 《构建之法第4版》、ECharts 官方文档、OpenAlex 公共检索接口 目录 一、Git 仓库、代码规范和 AI 工具 二、PSP 表格 三、NABCD 需求分析 NNeed需求 AApproach做法 BBenefit好处 CCompetitors竞争和不同点 DDelivery交付 四、原型设计 五、成品展示 1. 首页 Top 10 2. 论文列表管理 3. 模糊查询 4. 论文详情弹窗 5. 新增论文 6. 批量导入 7. 单篇查找 8. 关键词图谱 9. 趋势初始页面 10. 趋势动画 11. 关于帮助 12. 墨刀原型 13. 六页线框原型 六、人机结对讨论过程 案例 A需求分析和原型 案例 B用 PDF 生成数据 案例 C查询 Bug 七、设计实现过程 八、代码说明 1. 模糊查询 2. 新增和修改 3. 年度动画 九、测试记录 十、心路历程和收获 十一、对 AI 结对伙伴的评价 十二、总结 一、Git 仓库、代码规范和 AI 工具 AI 编程助手Codex 桌面版2026-09-23 使用,这里只挑三个关键位置说明, BBenefit好处 页面简单第一次使用也能看懂, venue, 趋势 GIF,paper. venue , topTopics : [], JSON . stringify (state. papers )); return true ;} catch (error) { toast ( 保存失败浏览器存储空间不足或被禁用, 10. 趋势动画 说明点击播放后年份会自动切换,由于时间比较紧这个项目不算很复杂但基础要求都已经覆盖, yearlyTop : [], app.js查询、弹窗、图表和 localStorage, name : item. name }));state. trendRows computeTrendRows (state. trendTopics . map ( ( item ) item. id ));state. yearlyTop computeYearlyTop ();state. conferenceStats computeConferenceStats (); if (!state. selectedTrendTopic || !state. trendTopics . some ( ( item ) item. id state. selectedTrendTopic )) {state. selectedTrendTopic state. trendTopics [ 0 ]?. id || ;} } function weightOf ( paper ) { return paper. source_type main_dataset ? 3 : 1 ; } function computeTopTopics () { const scores new Map (); const counts new Map (); for ( const paper of state. papers ) { const tags Array . from ( new Set (paper. tags || [])); for ( const tag of tags) { if (tag computer-vision ) continue ;scores. set (tag, AI 的不足 有时会把需求做得太复杂,一开始想得很简单实际做的时候发现数据解析、查询和图表需要反复试。
项目内已经准备了 13 张截图,/p/article/main ; return ;}state. papers loadPapers (); refreshDerivedData (); populateFilters (); renderStaticText (); renderStats (); renderTopics (); renderPapers (); renderSourceInfo (); renderTrendTopicOptions (); bindEvents (); updateYearTrack (); ensureTopChart (); } function loadPapers () { try { const stored JSON . parse ( localStorage . getItem ( STORAGE_KEY ) || null ); if ( Array . isArray (stored) stored. length ) return stored;} catch (error) { console . warn ( 读取本地数据失败使用初始数据, lt; ). replaceAll ( , 代码中有一些写法还可以继续优化例如 app.js 比较长后续可以拆成多个文件, 支持新增、修改、删除和两种查询方式, 8. 关键词图谱 说明节点越大表示热度越高点击节点可以查看相关论文, DDelivery交付 交付内容包括 六页网页原型, ); return false ;} } function topicLabel ( id ) { if (topicRules. has (id)) return topicRules. get (id). name ; const custom state. papers . flatMap ( ( paper ) paper. keywords || []). find ( ( keyword ) keyword. id id); return custom?. name || id. replace ( /^custom:/ , 查询分成精确和模糊两种。
papers : [], ...(paper. keywords || []). map ( ( keyword ) keyword. name ),项目不算特别复杂但基本覆盖了这次作业的五项基础要求, zh-CN )). slice ( 0 , selectedTrendTopic : ,br可以尝试缩短关键词、切换为模糊查询或清除筛选, 趋势只统计 CVPR、ICCV、ECCV 2021—2025, ICCV : 0 , value] ) ({ id, b ) (b. year || 0 ) - (a. year || 0 ) || String (a. title ). localeCompare ( String (b. title ), 这个统计方法不算很专业但数据来源可以追溯也符合本次作业的基础要求, localStorage 可以保存少量数据不用一开始就上数据库, 案例 A需求分析和原型 我一开始只知道“做一个顶会热词网站”不知道页面应该有什么, gt; ). replaceAll ( , 可以快速从热词跳到相关论文, mode : fuzzy , 对云端部署和账号操作不能完全代替人, 数据来源和统计口径写在“关于帮助”页面,})[sourceType] || 本地数据 ; } function authorsText ( authors ) { if (! Array . isArray (authors) || !authors. length ) return 作者信息未录入 ; if (authors. length 3 ) return authors. join ( , 1. 模糊查询 if (state. filters . mode exact ) { return paper. title . toLowerCase () search;} const haystack [ paper. title , 给 AI 的提示是 输入 brick structure 搜不到 Brick Structures帮忙把模糊查询改得更符合正常使用。
...(paper. keywords || []). map ( ( keyword ) keyword. name )。
venue : , 3. 模糊查询 说明输入英文关键词的一部分也能筛选出相关论文,它可以帮我省时间但最后还是要自己确认, CCompetitors竞争和不同点 常见做法是直接展示论文列表或者只做一个关键词云, 7. 单篇查找 说明先在本地查找找不到时再访问 OpenAlex,/div/td/tr ;} else {tbody. innerHTML papers. map ( ( paper ) { const keywords (paper. keywords || []). slice ( 0 ,最后把整个数组存到 localStorage, 6. 批量导入 说明每行一篇可以填写标题、作者、会议和年份, name : item. name }; if (item. terms . some ( ( term ) low term. toLowerCase ())) return { id : item. id , conferenceStats : [], charts : {}, 案例 C查询 Bug 在测试时我输入 brick structure 想找那篇积木论文但是结果为空, key ) { if (!state. charts [key]) state. charts [key] echarts. init ( document . getElementById (id)); return state. charts [key]; } function ensureTopChart () { const instance chart ( top-chart , 找不到本地论文时可以通过 OpenAlex 公共接口查询, name : item. name };} return { id : custom: ${low} , 4. 论文详情弹窗 说明详情中显示作者、关键词、摘要和原文链接。
原因是标题里写的是 Brick Structures有复数原来的整句包含查询找不到, #039; ); const state { view : home 。
paper);} else { state .papers.unshift(paper);}localStorage. set Item(STORAGE_KEY, 四、原型设计 原型工具墨刀, 所以 AI 更像一个写得快、有时会犯错的同伴,。
八、代码说明 完整代码在仓库里, 这个案例让我明白AI 自动生成的统计结果不能直接相信需要自己检查, zh-CN )). slice ( 0 。
amp; ). replaceAll ( , ICCV : 0 , 9. 趋势初始页面 说明左侧是年度榜单右侧是跨会议折线图, , 辅助解析两份 PDF生成论文数据和热词统计,我主要检查和确认功能并用浏览器测试每个页面, paper. year 。
十二、总结 这个平台已经实现了论文列表、Top 10、关键词图谱、趋势动画和基础部署准备工作, authors, 页面跳转以顶部导航为主, 2. 论文列表管理 说明可以查看论文、会议、年份、关键词和数据来源, 排错时能给出几个可能原因, 如果以后继续改我会先把数据量扩大再把 app.js 拆开继续补充墨刀分享链接并优化页面细节, ); return ${authors.slice( 0 , 趋势页需要能自动播放不然只放折线图不符合要求, 3. 年度动画function playTrend() { state .trendTimer set Interval(() { if ( state .trendYearIndex state .yearlyTop.length - 1 ) { clearInterval( state .trendTimer); return; } state .trendYearIndex 1 ; renderTrendRaceYear(); }, year : , ICCV , 统计方法 先去停用词影响再用主题词表匹配标题和摘要, 九、测试记录 测试使用 Chrome 浏览器和本地 Python 静态服务器, keywords, 写重复代码比较快, styles.css颜色、布局和响应式样式, quot; ). replaceAll ( , ...(paper. authors || []), ECCV : 0 }; for ( const paper of state. papers ) { if (paper. year ! year || !paper. tags ?. includes (id)) continue ; const venue paper. venue ; if (row[id][venue] ! undefined ) row[id][venue] weightOf (paper);}}rows. push (row);} return rows; } function computeYearlyTop () { const years []; for ( let year 2021 ; year 2025 ; year 1 ) { const scores new Map (); for ( const paper of state. papers ) { if (paper. year ! year) continue ; for ( const tag of Array . from ( new Set (paper. tags || []))) { if (tag computer-vision ) continue ;scores. set (tag, root document ) Array . from (root. querySelectorAll (selector)); const clone ( value ) JSON . parse ( JSON . stringify (value)); const escapeHtml ( value ) String (value). replaceAll ( , items });} return years; } function computeConferenceStats () { const rows []; for ( let year 2021 ; year 2025 ; year 1 ) { const row { year, JSON.stringify( state .papers)); 思路如果找到了同 id 的论文就更新否则新增, )} 等 ; } function applyFilters () { const search state. filters . search . trim (). toLowerCase (); return state. papers . filter ( ( paper ) { if (state. filters . venue paper. venue ! state. filters . venue ) return false ; if (state. filters . year String (paper. year ) ! String (state. filters . year )) return false ; if (state. filters . source paper. source_type ! state. filters . source ) return false ; if (state. filters . tag !(paper. tags || []). includes (state. filters . tag )) return false ; if (!search) return true ; if (state. filters . mode exact ) return String (paper. title || ). toLowerCase () search; const haystack [paper. title ,]. join ( ). toLowerCase (); return search. split ( /\s/ ). filter ( Boolean ). every ( ( token ) haystack. includes (token));}). sort ( ( a。
我根据自己的理解做了修改 不加入注册登录避免增加难度, 截图 13 张。
README、代码规范、部署说明和博客, root document ) root. querySelector (selector); const $$ ( selector,后来先把六个页面写出来再一个一个做思路才清楚一点, 另外还有一张可直接使用的动图 六、人机结对讨论过程 这一部分按照作业要求挑 3 个比较有代表性的过程写, name : topicLabel (id), online_api : 联网获取 , AI 使用说明项目页面、部分数据和代码是在 AI 帮助下完成的, trendYearIndex : 0 , 13. 六页线框原型 说明用于快速查看六个页面的布局,主要多花时间在 PDF 数据处理和浏览器测试上。
1. 首页 Top 10 说明首页显示论文总数、热门主题数和 Top 10 柱状图, index ) button classtopic-card data-topic ${escapeHtml(topic.id)} span classtopic-rank ${index 1 } /spanspanstrong ${escapeHtml(topic.name)} /strongsmall ${topic.paper_count} 篇相关论文/small/spanspan classtopic-score ${topic.value} /span/button ). join ( ); } function sourceLabel ( sourceType ) { return ({ main_dataset : 主数据 , 不足的地方也很明显数据量不大代码没有完全拆分云服务器已经完成基础部署不过页面细节和访问速度还可以继续优化。
item])); function init () { if (! APP_DATA ) { document . body . innerHTML main classapp-mainarticle classcardh1数据加载失败/h1p请确认 data/data.js 与 index.html 位于同一个项目目录, 可以帮忙把零散内容整理成博客, 原型文件 prototype/modao/未命名原型.html prototype/wireframe/index.html 发布链接 %E6%9C%AA%E5%91%BD%E5%90%8D%E5%8E%9F%E5%9E%8B.html 五、成品展示 下面需要把本地截图上传到 CSDN, data/data.js网页直接使用的初始数据。
1350 );} 思路使用定时器改变当前年份然后重新设置 ECharts 的柱状图数据。
4 ). map ( ( keyword ) span classbadge ${escapeHtml(keyword.name || topicLabel(keyword.id))} /span ). join ( ); return trtddiv classpaper-title ${escapeHtml(paper.title || 未命名论文 )} /divdiv classpaper-meta ${escapeHtml(authorsText(paper.authors))} /div/tdtdstrong ${escapeHtml(paper.venue || 其他 )} /strongdiv classpaper-meta ${paper.year || 年份未知 } /div/tdtd ${keywords || span classpaper-meta暂无关键词/span } /tdtdspan classbadge source ${sourceLabel(paper.source_type)} /span ${paper.citation_count ! null ? div classpaper-meta引用 ${paper.citation_count} /div : } /tdtddiv classrow-actionsbutton classmini-btn data-actiondetail data-id ${escapeHtml(paper.id)} 详情/buttonbutton classmini-btn data-actionedit data-id ${escapeHtml(paper.id)} 编辑/buttonbutton classmini-btn danger data-actiondelete data-id ${escapeHtml(paper.id)} 删除/button/div/td/tr ;}). join ( );} const tagNote state. filters . tag ? 主题筛选 ${topicLabel(state.filters.tag)} : ;$( #paper-result-count ). textContent 共 ${papers.length} 条结果 ${tagNote} ; } function renderSourceInfo () { const sources APP_DATA . sources || [];$( #data-source-list ). innerHTML sources. map ( ( source ) div classsource-itemstrong ${escapeHtml(source.title)} /strongsmall ${source.pages} 页 · ${(source.bytes / 1024 / 1024 ).toFixed( 1 )} MB · SHA-256 ${escapeHtml(source.sha256.slice( 0 , AI 给的结果一定要测试尤其是查询和数据统计, 10 ); } function computeTrendRows ( topicIds ) { const rows []; for ( let year 2021 ; year 2025 ; year 1 ) { const row { year }; for ( const id of topicIds) {row[id] { CVPR : 0 , 三、NABCD 需求分析NNeed需求 小刚想了解计算机视觉领域最近有哪些热门方向, | 测试内容 | 结果 | | -- | -- | | 首页能否显示 | 通过 | | Top 10 是否显示 | 通过 | | 论文列表是否显示 | 通过 | | 模糊查询 | 修改后通过 | | 精确查询 | 通过 | | 新增、编辑、删除 | 通过 | | 本地单篇查找 | 通过 | | 联网查找 | 已写代码受网络影响 | | 关键词图谱 | 通过 | | 趋势动画 | 通过 | | GIF 是否生成 | 通过 | | 手机宽度布局 | 基本通过 | 发现的错误 最开始把会议名称误当成热词后来去掉, 3 ).join( ,因为 ECharts 自带过渡动画看起来就是年度榜单在变化, ; } function renderTrendTopicOptions () { const select $( #trend-topic-select );select. innerHTML state. trendTopics . map ( ( topic ) option value ${escapeHtml(topic.id)} ${escapeHtml(topic.name)} /option ). join ( );select. value state. selectedTrendTopic ; } function switchView ( view ) {state. view view;$$( .view ). forEach ( ( element ) element. classList . toggle ( active , ); } function topicFromText ( text ) { if (!text) return null ; const low String (text). toLowerCase (). trim (); for ( const item of topicRules. values ()) { if (low item. id . toLowerCase () || low item. name . toLowerCase ()) return { id : item. id , top ); 思路精确查询只比较标题模糊查询把标题、摘要、会议、作者和关键词拼成一个字符串再检查每个查询词是否出现, (counts. get (tag) || 0 ) 1 );}} return Array . from (scores. entries ()). map ( ( [id, 后来改成先按空格拆分查询词再判断每个词是否出现在论文信息里,]. join ( ). toLowerCase (); return search. split ( /\s/ ). every ( ( token ) haystack. includes (token));( () { use strict ; const APP_DATA window . APP_DATA ; const STORAGE_KEY cvhotspot_papers_v3 ; const $ ( selector,...(paper. authors || []), ECCV : 0 }; for ( const paper of state. papers ) { if (paper. year year row[paper. venue ] ! undefined ) row[paper. venue ] 1 ;}rows. push (row);} return rows; } function populateFilters () { const venues Array . from ( new Set (state. papers . map ( ( paper ) paper. venue ). filter ( Boolean ))). sort (); const years Array . from ( new Set (state. papers . map ( ( paper ) paper. year ). filter ( Boolean ))). sort ( ( a,本项目的区别是 不只展示论文还统计热门主题, 出现查询问题、环境问题时帮我一起排查, 七、设计实现过程 功能不复杂整体结构可以理解为 用户打开网页 ↓读取 data .js 中的论文数据 ↓首页统计 Top 10 ↓论文页进行增删改查 ↓图谱页点击热词查看相关论文 ↓趋势页播放年度动画、比较三个会议 主要文件 index.html六个页面的结构, 一开始我以为直接让 AI 生成一个网页就行结果发现连“首页要放什么”“查询怎么算精确”“趋势数据从哪里来”都不清楚, 二、PSP 表格 下面的实际时间按本次完成过程大致记录不是非常精确,CVPR、ICCV、ECCV 论文数量很多一篇一篇看效率太低。
查询、弹窗和图表都放在当前页面中不来回跳转。
graphTopic : , 使用关键词规则统计热门主题, 60 ); } function chart ( id, filters : { search : , 两条参考文献标题不完整后来过滤, 使用 ECharts 画柱状图、关系图和折线图,/p ; return ;}container. innerHTML state. topTopics . map ( ( topic, 主要用途 帮助我整理作业需求确定六个页面, element. dataset . page view));$$( .nav-item ). forEach ( ( button ) button. classList . toggle ( active , AApproach做法 采用纯前端方案 使用两份 ICCV 2025 PDF 作为主要数据, 8 ). map ( ( item ) ({ id : item. id 。
name : topicLabel (id), reference_dataset : 参考文献 , button. dataset . view view)); window . scrollTo ({ top : 0 , 12. 墨刀原型 说明墨刀导出的原型页面。
value] ) ({ id, 11. 关于帮助 说明这里写数据来源、统计方式和常见问题, 这些问题的处理过程也在 docs/AI协作记录.md 中记录, abstract, 有现成 GIF方便写博客, tag : }, AI 使用 pypdf 提取正文再根据关键词规则匹配主题, scripts/make_trend_gif.py生成趋势 GIF。
配色以蓝色和白色为主按钮统一使用动作词, ECCV ]. includes (venue))). size || 3 ; } function renderTopics () { const container $( #topic-grid ); if (!state. topTopics . length ) {container. innerHTML p classempty-state暂无热词请先导入论文, 这个案例中AI 帮我把需求拆成页面我负责删掉不必要的内容, 十一、对 AI 结对伙伴的评价 AI 的优点 能从零给出一个完整方向, 现在的不足是数据量不大只有两份主 PDF 和部分参考文献不能代替真正的 DBLP 大数据分析, 16 ))} …/small/div ). join ( ); const method APP_DATA . methodology || {};$( #methodology-box ). innerHTML strong统计口径/strongbr ${escapeHtml(method.keyword_method || )} br ${escapeHtml(method.heat_formula || )} br趋势范围 ${escapeHtml(method.trend_window || 2021—2025 )} 会议CVPR、ICCV、ECCV, manual : 手动新增 , 十、心路历程和收获 这次作业最大的感受是让 AI 写东西很快但检查很花时间, title, trendRows : [], 趋势页把三个会议放在一起比较, (scores. get (tag) || 0 ) weightOf (paper));}} const items Array . from (scores. entries ()). map ( ( [id, source : 。
