SCORM课件演示与测试:从本地能播到平台能过的完整指南
简介这是一套遵循SCORM标准的交互式课件演示与测试资源面向E-Learning课程开发者、LMS平台测试人员及在线教学从业者用于验证课件在不同学习管理系统中的兼容性、跟踪与复用能力。压缩包共28个文件约12.07MB以swf动画、htm页面、xsd校验文件、js脚本和xml配置为主swf承载多媒体课程主体htm对应各学习单元xsd与xml负责元数据与结构定义js处理交互逻辑。资源将课程拆分为多个可独立使用的SCO学习单元配合清单文件描述课程结构、导航策略与学习对象引用便于平台解析、组织与呈现同时内置评估与反馈机制支持学习者自测与进度跟踪。已有579人学习适合作为SCORM标准落地、课件移植与平台适配的参考样例。1. SCORM 标准课件演示与测试从“能播”到“能过平台”的那道坎很多团队第一次做 SCORM 标准课件演示与测试都会卡在同一个地方本地浏览器里课件跑得好好的一上传到学习管理系统就出问题——要么进度不记录要么关掉再打开又从头开始要么干脆连启动都失败。这不是课件做得不好而是 SCORM 的“演示”和“测试”本来就是两件事演示是给人看的测试是给平台看的。SCORMSharable Content Object Reference Model本质上是一套课件与学习管理系统之间的通信约定课件通过前端脚本调用平台提供的运行时接口把学习进度、时长、完成状态写回去。你要做的不是“做一个好看的课件”而是“做一个平台认账的课件”。这篇文章面向正在做课件交付、课程平台对接、或者需要把现有内容包装成 SCORM 包的开发者从最小可运行结构讲到测试排错把演示和测试两条线都跑通。2. SCORM 包的骨架为什么你的课件上传后“失联”2.1 一个能启动的 SCORM 包最少需要哪些文件常见做法是做一个 1.2 版本的包因为兼容性最广。一个最小可运行的 SCORM 1.2 包结构如下course_package/ ├── imsmanifest.xml ├── index.html ├── scorm_api.js └── assets/ └── logo.pngimsmanifest.xml是包的“身份证”平台靠它识别资源、入口文件和组织结构。index.html是课件入口。scorm_api.js负责查找平台提供的 API 对象并封装调用。assets放静态资源。很多人翻车是因为把入口文件写成了index.html但 manifest 里写的却是别的路径平台找不到就直接报“无法启动”。下面是一个最小可用的imsmanifest.xml?xml version1.0 encodingUTF-8? manifest identifiercourse_pkg_001 version1.2 xmlnshttp://www.imsproject.org/xsd/imscp_rootv1p1p2 xmlns:adlcphttp://www.adlnet.org/xsd/adlcp_rootv1p2 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd metadata schemaADL SCORM/schema schemaversion1.2/schemaversion /metadata organizations defaultorg_001 organization identifierorg_001 title演示课程/title item identifieritem_001 identifierrefres_001 title第一章/title adlcp:masteryscore80/adlcp:masteryscore /item /organization /organizations resources resource identifierres_001 typewebcontent adlcp:scormtypesco hrefindex.html file hrefindex.html/ file hrefscorm_api.js/ file hrefassets/logo.png/ /resource /resources /manifest逻辑说明organizations定义课程结构resources定义实际文件。adlcp:scormtypesco表示这是一个可跟踪的学习对象。href必须和实际入口文件一致。masteryscore是及格分平台会根据它判断是否通过。参数说明identifier在同一包内必须唯一default指向默认组织typewebcontent是固定写法不要改。如果漏掉adlcp:scormtype平台可能把它当成普通资源不记录任何数据。2.2 入口文件里必须做的三件事index.html不是普通网页它需要在加载时找到 API、初始化、并在离开时提交数据。最小实现如下!DOCTYPE html html head meta charsetUTF-8 title演示课程/title script srcscorm_api.js/script /head body h1第一章/h1 button onclickfinishCourse()完成学习/button script // 页面加载时初始化 window.onload function () { SCORM.init(); SCORM.setValue(cmi.core.lesson_location, start); }; // 标记完成并提交 function finishCourse() { SCORM.setValue(cmi.core.lesson_status, completed); SCORM.setValue(cmi.core.score.raw, 90); SCORM.commit(); SCORM.finish(); } // 关闭页面时兜底提交 window.onbeforeunload function () { SCORM.commit(); SCORM.finish(); }; /script /body /html逻辑说明SCORM.init()负责在窗口层级里逐层查找平台注入的 API 对象。setValue写入数据commit提交到平台finish结束会话。onbeforeunload是后悔药防止用户直接关页面导致数据丢失。参数说明cmi.core.lesson_status的可选值包括not attempted、incomplete、completed、passed、failed。cmi.core.score.raw是原始分范围 0 到 100。cmi.core.lesson_location记录断点位置下次进入可以读取。2.3 scorm_api.js 里查找 API 的常见写法平台通常把 API 挂在window或父窗口上但不同平台的挂载方式不一样。下面是一个兼容性较好的查找函数var SCORM (function () { var api null; var initialized false; // 逐层向上查找 API function findAPI(win) { var attempts 0; while (win.API null win.parent ! null win.parent ! win) { attempts; if (attempts 10) return null; win win.parent; } return win.API; } return { init: function () { api findAPI(window); if (!api window.opener) { api findAPI(window.opener); } if (api) { initialized api.LMSInitialize(); } return initialized; }, setValue: function (key, value) { if (api initialized) { return api.LMSSetValue(key, String(value)); } return false; }, commit: function () { if (api initialized) { return api.LMSCommit(); } return false; }, finish: function () { if (api initialized) { return api.LMSFinish(); } return false; } }; })();逻辑说明findAPI从当前窗口向上遍历最多 10 层避免死循环。如果当前窗口找不到再尝试window.opener覆盖弹窗打开的场景。LMSInitialize、LMSSetValue、LMSCommit、LMSFinish是 SCORM 1.2 的四个核心方法。参数说明LMSInitialize()参数为空字符串是规范要求。LMSSetValue的第二个参数必须是字符串传数字可能被平台拒绝。LMSCommit()和LMSFinish()同理。3. 本地演示环境怎么搭不依赖平台也能跑通3.1 用本地模拟器验证 API 调用平台测试之前先在本地确认课件逻辑没问题。常见做法是写一个极简的模拟 API挂在window上把调用打印出来script // 本地模拟 API仅用于演示和调试 window.API { LMSInitialize: function () { console.log(init); return true; }, LMSSetValue: function (k, v) { console.log(set, k, v); return true; }, LMSCommit: function () { console.log(commit); return true; }, LMSFinish: function () { console.log(finish); return true; }, LMSGetValue: function (k) { console.log(get, k); return ; } }; /script把这个脚本放在scorm_api.js之前打开index.html控制台会输出完整的调用序列。如果init没打印说明查找逻辑有问题如果set打印了但值不对说明参数写错了。这一步能排掉大部分低级错误。参数说明模拟 API 的返回值必须是字符串true或false不要返回布尔值。LMSGetValue在真实平台里用于读取上次的学习位置本地模拟可以先返回空字符串。3.2 用浏览器开发者工具看通信过程打开开发者工具的 Network 面板筛选scorm或api关键字能看到每次commit触发的请求。如果没有任何请求说明 API 没找到或者initialized为 false。如果请求返回错误码常见的是401未授权或404路径不对前者是平台会话过期后者是 manifest 配置错误。我一般会先看 Console 里有没有报错再看 Network 里有没有请求最后看 Application 里有没有存储写入。三步走完基本能定位到是课件问题还是平台问题。3.3 本地文件协议的限制直接双击index.html用file://协议打开某些浏览器会限制跨窗口访问导致findAPI找不到父窗口。解决方法是起一个本地静态服务# 在课件目录下启动一个静态服务 python3 -m http.server 8080然后访问http://localhost:8080/index.html。这样窗口层级和跨域行为更接近真实平台环境。注意端口不要被占用8080 冲突就换 8081。参数说明http.server是 Python 标准库自带模块不需要额外安装。如果团队用 Node.js也可以用npx serve替代效果一样。4. 上传平台后的测试清单从“能播”到“能过”4.1 测试前必须确认的五个配置项检查项正确写法常见错误manifest 入口hrefindex.html写成绝对路径或大小写不一致scormtypeadlcp:scormtypesco漏写或写成 asset版本声明schemaversion 1.2写成 2004 但包结构是 1.2文件清单所有文件都在 resource 里列出漏掉 js 或图片字符编码UTF-8GBK 导致中文标题乱码这张表是血泪经验每一条都对应过真实的翻车现场。尤其是大小写Linux 服务器区分大小写本地 Windows 不区分上传后就找不到文件。4.2 用平台日志定位“进度不记录”平台一般会提供学习记录页面或日志查询。如果课件播放正常但进度不更新按以下顺序排查第一步确认LMSCommit是否被调用。在commit函数里加一行console.log看控制台有没有输出。没有输出说明代码没走到可能是按钮事件没绑定或者页面提前关闭。第二步确认cmi.core.lesson_status的值是否被平台接受。有些平台只认completed和passed写finished会被忽略。改成completed再试。第三步确认commit之后没有立即finish。某些平台在finish之后会关闭会话后续写入全部失效。正确顺序是先setValue再commit最后finish。第四步看平台日志里有没有LMSSetValue的报错。常见错误是 key 拼写错误比如把cmi.core.lesson_status写成cmi.lesson_status少了一层core。4.3 断点续播的数据读写验证断点续播依赖cmi.core.lesson_location。写入时用setValue读取时用LMSGetValue。测试方法是学到一半关掉重新进入看是否跳回上次位置。// 读取上次位置 var lastLocation SCORM.getValue(cmi.core.lesson_location); if (lastLocation lastLocation ! ) { // 跳转到对应章节 gotoChapter(lastLocation); }参数说明getValue在 SCORM 1.2 里对应LMSGetValue需要在scorm_api.js里补上这个方法。返回值是字符串空字符串表示没有记录。如果平台不支持断点返回值可能始终为空这时候不要强行跳转否则会报错。4.4 成绩回传的格式要求成绩通过cmi.core.score.raw回传范围 0 到 100。有些平台还要求同时写cmi.core.score.min和cmi.core.score.max否则成绩显示为空。稳妥做法是三个都写SCORM.setValue(cmi.core.score.min, 0); SCORM.setValue(cmi.core.score.max, 100); SCORM.setValue(cmi.core.score.raw, 90); SCORM.setValue(cmi.core.lesson_status, passed); SCORM.commit();逻辑说明先写范围再写分数最后写状态。passed表示通过平台会根据masteryscore判断。如果分数低于及格线但状态写passed部分平台会以状态为准部分平台会以分数为准测试时要确认平台行为。参数说明raw必须是整数字符串不要带小数点。如果分数是 89.5先取整再写入。5. 避坑与排查那些让课件“看起来正常”的暗坑5.1 现象本地正常上传后白屏原因manifest 里的href指向的文件名和实际文件名大小写不一致或者文件没有列在file标签里。平台解包后只保留清单里声明的文件没声明的直接丢弃。解决逐行核对 manifest 里的每个href确保和实际文件完全一致。建议全部用小写字母加下划线避免空格和特殊字符。5.2 现象进度条走到 100% 但状态还是 incomplete原因只写了cmi.core.lesson_location或自定义字段没有写cmi.core.lesson_status。平台判断完成与否只看lesson_status不看进度条。解决在完成逻辑里显式写入completed或passed并确保commit成功。可以在commit后读取一次LMSGetValue验证是否写入成功。5.3 现象第二次进入时数据被重置原因LMSInitialize之后没有先读取旧数据直接写入了初始值。或者平台不支持持久化每次进入都是新会话。解决初始化后先getValue读取lesson_location和lesson_status有值就恢复没值才写初始值。如果平台本身不支持持久化需要联系平台确认配置。5.4 现象控制台报“API not found”原因课件在 iframe 里打开但findAPI只查了当前窗口和父窗口没有查顶层窗口。或者平台把 API 挂在了window.top上而代码只查了window.parent。解决把查找范围扩大到window.top并增加尝试次数。下面是一个更完整的查找逻辑function findAPI(win) { var attempts 0; while (win.API null win.parent ! null win.parent ! win) { attempts; if (attempts 20) return null; win win.parent; } // 再查顶层 if (!win.API win.top win.top.API) { return win.top.API; } return win.API; }参数说明attempts上限从 10 调到 20覆盖更深的嵌套。win.top是顶层窗口某些平台把 API 挂在那里。5.5 现象中文标题在平台上显示乱码原因manifest 文件保存成了 GBK 编码但平台按 UTF-8 解析。或者 XML 声明里写了encodingGBK但实际文件是 UTF-8。解决统一用 UTF-8 保存XML 声明写encodingUTF-8。用编辑器检查文件编码不要靠肉眼判断。如果已经上传重新打包再传一次。6. 进阶技巧用一套代码兼容 SCORM 1.2 和 20046.1 两个版本的核心差异SCORM 1.2 和 2004 的 API 方法名不同1.2 用LMSInitialize、LMSSetValue、LMSCommit、LMSFinish2004 用Initialize、SetValue、Commit、Terminate。数据模型也不同1.2 的cmi.core.lesson_status在 2004 里拆成了cmi.completion_status和cmi.success_status。兼容做法是在scorm_api.js里做一层适配var SCORM (function () { var api null; var is2004 false; function findAPI(win) { var attempts 0; while (win.API null win.API_1484_11 null win.parent ! null win.parent ! win) { attempts; if (attempts 20) return null; win win.parent; } return win; } return { init: function () { var win findAPI(window); if (win.API_1484_11) { api win.API_1484_11; is2004 true; return api.Initialize(); } else if (win.API) { api win.API; is2004 false; return api.LMSInitialize(); } return false; }, setValue: function (key, value) { if (!api) return false; if (is2004) { // 1.2 到 2004 的 key 映射 if (key cmi.core.lesson_status) { if (value completed || value passed) { api.SetValue(cmi.completion_status, completed); api.SetValue(cmi.success_status, value passed ? passed : unknown); } else { api.SetValue(cmi.completion_status, value); } return true; } return api.SetValue(key, String(value)); } return api.LMSSetValue(key, String(value)); }, commit: function () { if (!api) return false; return is2004 ? api.Commit() : api.LMSCommit(); }, finish: function () { if (!api) return false; return is2004 ? api.Terminate() : api.LMSFinish(); } }; })();逻辑说明findAPI同时检查API和API_1484_11优先识别 2004。setValue里对lesson_status做映射把 1.2 的状态拆成 2004 的完成状态和成功状态。其他 key 直接透传。参数说明API_1484_11是 SCORM 2004 的 API 对象名1484来自规范编号。cmi.completion_status的可选值是completed、incomplete、not attempted。cmi.success_status的可选值是passed、failed、unknown。6.2 用一份 manifest 声明两个版本manifest 里可以通过schemaversion声明版本但一个包只能声明一个版本。如果要兼容两个版本常见做法是打两个包或者用平台支持的“多版本 manifest”。更稳妥的方式是只做 1.2 包因为 2004 平台通常向下兼容 1.2。如果平台明确要求 2004再单独打包。我一般会先问平台对接人支持哪个版本再决定打包策略。不要自己猜猜错了返工成本很高。6.3 测试时用同一套用例跑两个版本测试用例至少覆盖初始化、写入状态、写入分数、提交、结束、重新进入读取。每个用例在两个版本下各跑一遍记录返回值。下面是一个简单的测试记录表用例1.2 返回值2004 返回值是否一致初始化truetrue是写状态 completedtruetrue是写分数 90truetrue是提交truetrue是结束truetrue是重新读取状态completedcompleted是如果某个用例返回值不一致先看 key 映射有没有漏再看平台日志有没有报错。大部分兼容问题都出在 key 映射上。6.4 一个容易忽略的细节会话超时平台通常会给学习会话设置超时时间比如 30 分钟无操作就断开。如果课件长时间停留在同一页commit可能失败。解决方法是加一个定时器每隔几分钟自动commit一次setInterval(function () { SCORM.commit(); }, 5 * 60 * 1000); // 每 5 分钟提交一次参数说明间隔不要设得太短否则请求过多可能被平台限流。5 分钟是一个比较稳妥的值。如果平台超时时间更短按平台文档调整。这个技巧是我在某次交付后总结出来的当时课件在测试环境一切正常到了生产环境因为会话超时丢了一批数据后来加了定时提交就再没出现过。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

MySQL大表清空:TRUNCATE底层原理、权限与避坑实战

MySQL大表清空:TRUNCATE底层原理、权限与避坑实战

最近在帮一个老项目排查数据库慢查询时,发现一张千万级的日志表被一条清理任务拖得半死不活。对方一脸无奈地跟我说:“我明明用的DELETE,怎么数据越清越慢?”我让他把SQL贴出来看了一眼,果然又是那个场景:D…

2026/10/14 17:40:03 阅读更多 →
STM32F103ZET6库函数工程从零搭建实战指南

STM32F103ZET6库函数工程从零搭建实战指南

1. 为什么今天还要从零搭建一个“老”芯片的库函数工程?在2024年,当RISC-V开发板满天飞、Cortex-M33芯片已成中端主力、AIoT项目动辄集成Wi-FiBLERTOS的时候,我依然在Keil MDK里,为一块STM32F103ZET6——这颗2007年发布、主频72MH…

2026/10/14 17:39:02 阅读更多 →
Python入门必学:输入输出、流程控制与循环语句详解

Python入门必学:输入输出、流程控制与循环语句详解

1. 初见Python:为什么第一篇只讲这三件事如果你问一百个Python老手"入门的核心语法到底是什么",其中九十个会告诉你:先把输入输出、流程控制、循环语句这三样吃透,其他的都可以往后放。这看起来很简单,但恰恰…

2026/10/14 17:39:02 阅读更多 →

最新新闻

快速阅读不靠眼快:筛选与理解的实用框架

快速阅读不靠眼快:筛选与理解的实用框架

经常有人问我:“你读书怎么那么快?是不是一目十行、走马观花?”说实话,我刚开始试水快速阅读的时候也踩过不少坑,最典型的表现是字都看完了,合上书一问三不知。后来我花了很长时间去拆解“快速阅读”和“快…

2026/10/14 18:20:44 阅读更多 →
存量市场服务升级:低风险高回报的商业模式

存量市场服务升级:低风险高回报的商业模式

1. 为什么说"稳妥生意"比创业更值得关注最近三年,我身边有37位朋友尝试创业,最终存活下来的只有3家。这个残酷的数字让我开始思考:对于大多数普通人来说,是否存在着比创业风险更低、回报更稳定的商业机会?经…

2026/10/14 18:20:44 阅读更多 →
Decepticon 实战手册:EtherNet/IP + CIP 协议攻击全流程(44818/2222 端口下的 PLC 侦察、标签读写与模式切换)

Decepticon 实战手册:EtherNet/IP + CIP 协议攻击全流程(44818/2222 端口下的 PLC 侦察、标签读写与模式切换)

【免费下载链接】Decepticon Autonomous Hacking Agent for Red Team 项目地址: https://gitcode.com/gh_mirrors/de/Decepticon 点击查看 免费下载 本指南基于 Decepticon 仓库中 enip-cip 技能文档 编写,系统讲解针对北美工业控制系统(ICS…

2026/10/14 18:20:44 阅读更多 →
中点画圆算法详解:光栅化生成完美圆的数学原理与代码实现

中点画圆算法详解:光栅化生成完美圆的数学原理与代码实现

圆的生成算法,是计算机图形学课程里最容易被低估的一个实验。很多同学看到“画圆”两个字,第一反应是直接调用窗口库里的画圆函数,几行代码就结束。但实验真正要你做的,是把它当成一道光栅化算法题:输入圆心坐标、半径…

2026/10/14 18:20:44 阅读更多 →
把“学术导航仪”装进书匠策AI:一篇期刊论文的“造路”逻辑

把“学术导航仪”装进书匠策AI:一篇期刊论文的“造路”逻辑

官网直达:www.shujiangce.com 微信公众号搜一搜:书匠策AI 写期刊论文最折磨人的,从来不是“写”这个动作本身。 是你盯着空白文档,知道要做文献综述,却不知道从哪篇开始;知道要有理论框架,却拿…

2026/10/14 18:20:44 阅读更多 →
插件加载失败排查:从生命周期到激活机制,一篇搞定

插件加载失败排查:从生命周期到激活机制,一篇搞定

先说一句,我带过好几个插件化改造的项目,也修过无数跟插件相关的启动报错,所以看到 "failed to load plugins web boot: 2 entries did not activate" 这种日志时,第一反应不是翻框架文档,而是看插件的激活方…

2026/10/14 18:19:43 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →