diff --git a/website/README.md b/website/README.md index 89a2e582..de89d4af 100644 --- a/website/README.md +++ b/website/README.md @@ -43,7 +43,7 @@ verify_updates.py 在临时副本中修改文章、替换配图、新增文章 页面模板保留手机分类导航、折叠目录、搜索和旧文章入口兼容;固定版本的浏览器依赖及许可证保存在 templates/vendor 中。 -学习站(定级、每日计划、间隔复习)的代码不在本仓库,网站工作流从私有仓库检出到 learn/;learn/ 存在时,build.py 调用 learn/build.py 生成到 dist/learn/,随网站一起发布,并在首页、侧边栏和三条学习路线的文章开头加入口;不存在时只构建笔记,没有入口。 +学习站(定级、每日计划、间隔复习)的代码不在本仓库,网站工作流从私有仓库检出到 learn/;learn/ 存在时,build.py 调用 learn/build.py 生成到 dist/learn/,随网站一起发布,并在首页、侧边栏和三条学习路线的文章开头加入口,学习站中有题的笔记在文章末尾提示“读完练一练”(search-paths.js 中的 CS_NOTES_LEARN_TOPICS 记录每篇笔记的题数);不存在时只构建笔记,没有入口。 ## 自动更新与发布 diff --git a/website/builder/build.py b/website/builder/build.py index 7d2268f6..10920159 100644 --- a/website/builder/build.py +++ b/website/builder/build.py @@ -245,6 +245,12 @@ def build(optimize=True): if learn_report['pending']: print(prefix + '学习站:内容与 learn/content.lock.json 不一致,请在本机运行 python3 learn/maintain.py 查看并写入:' + json.dumps(learn_report['pending'], ensure_ascii=False), file=sys.stderr) + learn_topics = {} + if learn_report is not None: + # 每篇笔记在学习站中有几道题(题目的原文链接指向这篇笔记),文章末尾据此提示“读完练一练” + for question in json.loads((dist / 'learn' / 'data.json').read_text())['questions'].values(): + for note in {link['note'] for link in question['links'] if link.get('note')}: + learn_topics[note] = learn_topics.get(note, 0) + 1 copy_referenced_assets(dist, ROOT) image_report = optimize_images(dist, CACHE) if optimize else {'converted': 0} index_name, search_report = write_search_index(dist, sorted((dist / 'notes').glob('*.md'))) @@ -254,7 +260,8 @@ def build(optimize=True): 'window.CS_NOTES_PATHS = ' + json.dumps(note_paths, ensure_ascii=False) + ';\n' 'window.CS_NOTES_LEGACY_ROUTES = ' + json.dumps(legacy, ensure_ascii=False, separators=(',', ':')) + ';\n' 'window.CS_NOTES_SEARCH_INDEX = "/' + index_name + '";\n' - 'window.CS_NOTES_ANIMATIONS = ' + json.dumps(animations, ensure_ascii=False, separators=(',', ':')) + ';\n') + 'window.CS_NOTES_ANIMATIONS = ' + json.dumps(animations, ensure_ascii=False, separators=(',', ':')) + ';\n' + 'window.CS_NOTES_LEARN_TOPICS = ' + json.dumps(learn_topics, ensure_ascii=False, separators=(',', ':')) + ';\n') generator = hashlib.sha256() learn = [*LEARN_BUILD.parent.glob('*.py'), *(LEARN_BUILD.parent / 'src').rglob('*')] if LEARN_BUILD.is_file() else [] for resource in sorted([*Path(__file__).parent.glob('*.py'), *SITE.rglob('*'), *learn]): diff --git a/website/templates/site.css b/website/templates/site.css index 0b2b0b63..5561f228 100644 --- a/website/templates/site.css +++ b/website/templates/site.css @@ -47,6 +47,7 @@ body { color: #34495e; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI .learn-banner-text span, .learn-callout span { display: block; margin-top: 2px; font-size: 14px; line-height: 1.6; opacity: .85; } .learn-banner-go { flex: none; padding: 7px 18px; border-radius: 8px; background: var(--theme-color); color: #fff; font-weight: 600; font-size: 15px; } .learn-callout { display: block; } +.learn-practice { margin-top: 36px; } @media (min-width: 769px) { .content { padding-top: 12px; } .article-toc { display: none; } } .page-toc-block { margin: 0 0 18px; padding: 0 0 14px 18px; border-bottom: 1px solid #e2e3dc; } .page-toc-title { margin: 0 0 6px; font-size: 14px; font-weight: 600; color: #34495e; } diff --git a/website/templates/site.js b/website/templates/site.js index c4c14f11..8da3a586 100644 --- a/website/templates/site.js +++ b/website/templates/site.js @@ -205,6 +205,19 @@ var heading = section.querySelector('h1'); (heading || section.firstChild).insertAdjacentElement(heading ? 'afterend' : 'beforebegin', box); } + // 学习站中有这篇笔记的题时,文章末尾提示读完练一练(只抽这篇笔记的题,还没开始学也可以练) + function practiceCallout(vm, section) { + var match = decode(vm.route.path).match(/^\/notes\/(.+)$/), note = match && match[1]; + var count = note && (window.CS_NOTES_LEARN_TOPICS || {})[note]; + if (!section || !count || count < 2 || section.querySelector('.learn-practice')) return; + var box = document.createElement('a'); + box.className = 'learn-callout learn-practice'; + box.href = '/learn/#/mock/' + encodeURIComponent(note); + box.innerHTML = '读完练一练学习站中有 ' + count + ' 道和这篇笔记有关的面试题:先自己答,再对照参考要点自评,自评过的题会按间隔提醒复习。'; + // 放在上一篇、下一篇之前 + var after = section.querySelector('.page-nav') || section.querySelector('.site-footer'); + if (after) after.insertAdjacentElement('beforebegin', box); else section.appendChild(box); + } window.$docsify = { name: 'CS-Notes', repo: 'https://github.com/CyC2018/CS-Notes', @@ -239,6 +252,7 @@ stepAnimations(document.querySelector('.markdown-section')); articleToc(vm); learnCallout(vm, document.querySelector('.markdown-section')); + practiceCallout(vm, document.querySelector('.markdown-section')); document.querySelectorAll('.article-toc').forEach(function (toc) { toc.open = window.matchMedia('(min-width: 769px)').matches; });