HarmonyOS应用实战-启示散页-50-发布截图别暴露用户题库:准备一套可复现的演示数据
HarmonyOS 应用实战 50发布截图别暴露用户题库把截图环境做成可回滚数据沙箱发布截图最怕一种“看起来很干净”的做法开发者先把自己的题库改名删掉几条提问历史再对结果页做几次手工打码然后拿这些图去交材料。单张图里可能只露出一个题库名连着首页、抽取页、结果页、收藏页一起看就能还原出用户最近问过什么、收藏过哪条答案、从哪个题库抽到过结果。答案之书是离线应用没有账号体系也没有服务端同步但这并不代表发布材料没有隐私边界。它的题库、收藏、提问历史都在本地Preferences页面展示时又会把答案正文、题库来源和最近问题带出来。第 50 篇要解决的不是“怎么打码”而是把发布截图前的本地数据切换做成可回滚的数据沙箱进入前备份真实数据截图时只展示演示数据退出后恢复原状并留下可复查的截图清单。这篇文章解决四件事从现有源码里找出发布截图会暴露哪些用户内容。说明为什么不能把截图准备逻辑塞进SeedLoader或页面回调。用现有 Repository 思路设计进入、安装、恢复三段流程。给出发布前复核表确认截图、日志和恢复路径都能解释清楚。先把泄露链路从源码里找出来第一个判断标准很简单页面上能出现的文字如果来自真实用户输入或真实本地题库就不能直接进入发布截图。答案之书当前已经有清晰的数据边界问题不在“找不到 owner”而在发布前没有把这些 owner 组合成一条临时数据沙箱链路。画面位置真实来源风险首页题库卡片DeckService.list()返回DeckSummary.name、count真实题库名和规模暴露抽取页参数路由里的question、deckId最近提问和当前题库暴露结果页答案AnswerPage.param.text答案正文直接入图结果页来源DeckService.get(this.param.deckId)题库来源暴露收藏页FavoriteService.list()收藏答案和来源题库暴露历史弹层QuestionHistoryService.list()用户问题原文暴露这张表比“发布前清空数据”更重要。清空只解决当次截图源码 owner 表能告诉我们每个页面为什么会出现真实内容以及应该从哪一层替换为演示数据。结果页已经把答案和题库名连在一起结果页是最典型的泄露点。它不仅展示答案正文还会异步读取题库名用户点击收藏时又把答案和来源题库一起写进收藏。Componentexportstruct AnswerPage{Stateprivatefavorited:booleanfalse;StateprivatedeckName:string;privateparam:AnswerParams{deckId:,answerId:,text:};privateasyncloadDeckName():Promisevoid{constdeckawaitDeckService.get(this.param.deckId);this.deckNamedeck?.name??;}privateonToggleFavorite():void{FavoriteService.add({text:this.param.text,fromDeckId:this.param.deckId,fromDeckName:this.deckName||undefined});}}这段代码本身没有错它是正常业务需要结果页应该知道答案来自哪个题库收藏也应该保留来源。但发布截图不能直接复用真实运行态。只要this.param.text来自真实抽取结果this.deckName来自真实题库名截图就不是演示材料而是用户内容的再展示。更稳的处理方式不是在AnswerPage里加一堆发布分支而是在进入截图环境前把页面会读取到的数据整体替换成受控演示数据。页面继续走原来的业务链路截图环境负责保证这条链路读到的是演示内容。历史和收藏必须一起处理很多发布图只检查首页和结果页忽略了底部弹层或二级页面。答案之书的提问历史和收藏都是真实 store一旦只替换题库收藏页和历史弹层仍会露出用户内容。classQuestionHistoryServiceImpl{asyncadd(text:string):Promisevoid{consttrimmed:stringtext.trim();if(!trimmed){return;}constall:string[]awaitQuestionHistoryRepository.loadAll();constfiltered:string[]all.filter((q:string):booleanq!trimmed);filtered.unshift(trimmed);constnextfiltered.lengthMAX_QUESTION_HISTORY?filtered.slice(0,MAX_QUESTION_HISTORY):filtered;awaitQuestionHistoryRepository.saveAll(next);AppStorage.setOrCreate(AppStorageKey.LastQuestionHistoryUpdateAt,Date.now());}}classFavoriteServiceImpl{asyncadd(payload:AddFavoritePayload):PromiseFavorite{constall:Favorite[]awaitFavoriteRepository.loadAll();constfav:Favorite{id:newId(fav),text:payload.text.trim(),fromDeckId:payload.fromDeckId,fromDeckName:payload.fromDeckName,createdAt:Date.now()};all.push(fav);awaitFavoriteRepository.saveAll(all);AppStorage.setOrCreate(AppStorageKey.LastFavoriteUpdateAt,fav.createdAt);returnfav;}}这两段源码说明了一个交付边界发布截图数据沙箱必须同时覆盖题库、收藏、提问历史和当前题库 id。只处理其中一项会制造“首页是演示数据收藏页是真实数据”的半干净状态。SeedLoader 不是发布截图入口当前项目已经有SeedLoader但它的目标是首次启动、默认题库缺失、默认题库损坏或种子版本升级时恢复默认题库。它不应该被改成发布截图入口。classSeedLoaderImpl{asyncrun(context:common.UIAbilityContext):Promisevoid{constids:string[]awaitDeckRepository.loadIndex();constdefaultDeck:Deck|nullawaitDeckRepository.loadDeck(DEFAULT_DECK_ID);constseededVersion:numberawaitPreferencesStore.getNumber(PrefStoreName.App,AppPrefKey.LastSeededVersion,0);constneedsSeed:booleanids.length0||defaultDecknull||defaultDeck.answers.lengthSEED_RESTORE_THRESHOLD||seededVersionDEFAULT_DECK_SEED_VERSION;if(!needsSeed){awaitthis.ensureCurrentDeck();return;}constdeck:Deckawaitthis.buildDefaultDeck(context);awaitDeckService.putRaw(deck);AppStorage.setOrCreate(AppStorageKey.LastDeckUpdateAt,deck.updatedAt);}}这里的 owner 是“默认题库自愈”。发布截图需要的是“真实数据备份、演示数据安装、截图完成恢复、证据留痕”。如果把两者混在一起冷启动时就很难解释当前题库为什么被替换也很难保证退出截图环境后真实收藏和历史已经恢复。演示场景要写成稳定合同演示数据不应该散落在页面里。它应该像一份发布素材合同本次截图使用哪个题库、哪个问题、哪些收藏、覆盖哪些页面。这样补图时才能复用同一套输入。interfaceReleaseScreenshotScenario{scenarioId:string;seedVersion:number;currentDeckId:string;decks:Deck[];favorites:Favorite[];questionHistory:string[];screenshotPages:Arrayhome|drawing|answer|favorites|settings;}constreleaseScenarioV1:ReleaseScreenshotScenario{scenarioId:release-screenshot-v1,seedVersion:1,currentDeckId:release-deck-daily,decks:[{id:release-deck-daily,name:发布演示题库,builtIn:false,colorKey:blue,answers:[{id:release-answer-1,text:先把问题写清楚再决定下一步。,createdAt:1760000000000},{id:release-answer-2,text:今天适合做一次小范围验证。,createdAt:1760000000000}],createdAt:1760000000000,updatedAt:1760000000000}],favorites:[],questionHistory:[今天要不要继续推进],screenshotPages:[home,drawing,answer,favorites,settings]};这里刻意使用固定 id 和固定时间。发布截图追求的是可复现不是模拟真实随机行为。随机答案、随机颜色、随机时间都会让补图和复查变得困难。备份用现有 Repository 粒度不要假装有整库导出当前PreferencesStore提供的是getString、setString、getJson、setJson、remove这些基础能力并没有一键导出或导入整个 store 的接口。因此文章里的落地方案不应该依赖当前项目不存在的整库能力。更贴近现有工程的做法是按业务 Repository 粒度备份题库全量、收藏全量、提问历史全量、当前题库 id。interfaceReleaseUserSnapshot{decks:Deck[];favorites:Favorite[];questionHistory:string[];currentDeckId:string;createdAt:number;}classReleaseSnapshotStore{asynccreateSnapshot():PromiseReleaseUserSnapshot{return{decks:awaitDeckRepository.loadAll(),favorites:awaitFavoriteRepository.loadAll(),questionHistory:awaitQuestionHistoryRepository.loadAll(),currentDeckId:awaitDeckService.getCurrentId(),createdAt:Date.now()};}}这个粒度更利于排障。恢复失败时可以分别判断是题库没有恢复、收藏没有恢复、历史没有恢复还是currentDeckId没有写回。整库黑盒导出看似简单出了问题反而难定位。题库恢复还要多一步因为当前DeckRepository只有loadAll()、saveDeck()、removeDeck()和saveIndex()没有一键替换全量题库的方法。发布截图能力可以在自己的边界里补一个很薄的批量写入器内部仍然走现有 Repository。classReleaseDeckSnapshotWriter{asyncreplaceAll(decks:Deck[]):Promisevoid{constoldIds:string[]awaitDeckRepository.loadIndex();for(constidofoldIds){awaitDeckRepository.removeDeck(id);}for(constdeckofdecks){awaitDeckRepository.saveDeck(deck);}awaitDeckRepository.saveIndex(decks.map((deck:Deck):stringdeck.id));}}这段代码不是为了绕过 Repository而是把“发布截图要临时替换题库全集”这个特殊动作收在发布准备层。页面和普通业务 Service 不需要知道它存在。安装演示数据时要刷新所有相关 live state演示数据安装不是把 JSON 写进去就结束。现有页面依赖AppStorageKey.LastDeckUpdateAt、LastFavoriteUpdateAt、LastQuestionHistoryUpdateAt这类时间戳触发刷新如果只写 Repository不发刷新信号页面可能仍显示旧数据。classReleaseScreenshotInstaller{asyncinstallScenario(scenario:ReleaseScreenshotScenario):Promisevoid{awaitnewReleaseDeckSnapshotWriter().replaceAll(scenario.decks);awaitFavoriteRepository.saveAll(scenario.favorites);awaitQuestionHistoryRepository.saveAll(scenario.questionHistory);awaitPreferencesStore.setString(PrefStoreName.App,AppPrefKey.CurrentDeckId,scenario.currentDeckId);constnowDate.now();AppStorage.setOrCreate(AppStorageKey.CurrentDeckId,scenario.currentDeckId);AppStorage.setOrCreate(AppStorageKey.LastDeckUpdateAt,now);AppStorage.setOrCreate(AppStorageKey.LastFavoriteUpdateAt,now);AppStorage.setOrCreate(AppStorageKey.LastQuestionHistoryUpdateAt,now);}}这一步要特别注意deck_index和deck:id的一致性。只写单个题库 JSON、不更新索引首页列表会读不到只更新索引、不清旧题库退出截图环境后又可能残留演示数据。退出时先恢复再宣布截图环境结束恢复顺序也要明确。先恢复真实题库、收藏和历史再刷新AppStorage最后记录本次截图场景已退出。顺序反了页面可能短暂读到空数据或演示数据。classReleaseScreenshotSession{privatesnapshot:ReleaseUserSnapshot|nullnull;asyncenter(scenario:ReleaseScreenshotScenario):Promisevoid{this.snapshotawaitnewReleaseSnapshotStore().createSnapshot();awaitnewReleaseScreenshotInstaller().installScenario(scenario);awaitReleaseChecklistRepository.markEntered({scenarioId:scenario.scenarioId,seedVersion:scenario.seedVersion,pages:scenario.screenshotPages});}asyncexit():Promisevoid{if(!this.snapshot){thrownewError(release screenshot snapshot missing);}awaitnewReleaseDeckSnapshotWriter().replaceAll(this.snapshot.decks);awaitFavoriteRepository.saveAll(this.snapshot.favorites);awaitQuestionHistoryRepository.saveAll(this.snapshot.questionHistory);awaitPreferencesStore.setString(PrefStoreName.App,AppPrefKey.CurrentDeckId,this.snapshot.currentDeckId);constnowDate.now();AppStorage.setOrCreate(AppStorageKey.CurrentDeckId,this.snapshot.currentDeckId);AppStorage.setOrCreate(AppStorageKey.LastDeckUpdateAt,now);AppStorage.setOrCreate(AppStorageKey.LastFavoriteUpdateAt,now);AppStorage.setOrCreate(AppStorageKey.LastQuestionHistoryUpdateAt,now);}}这段设计的核心是“页面不感知发布截图环境”。页面仍然走原来的DeckService、FavoriteService、QuestionHistoryService只是进入截图环境后这些 Service 读到的是演示数据。这样可以减少页面分支也更容易在发布后移除或关闭这段能力。日志复查不能只看有没有报错发布截图常常会配合日志排障。当前项目里的日志大多记录 id、count、错误信息例如结果页记录show answer id收藏服务记录总数。这种方向是对的发布材料不需要打印答案全文。// 结果页只记录 answerId避免把答案正文写入 hilog。hilog.info(DOMAIN,TAG,show answer id%{public}s,p.answerId);// 收藏服务只记录数量不记录收藏文本。hilog.info(DOMAIN,TAG,add favorite total%{public}d,all.length);// 提问历史只记录数量不记录问题原文。hilog.info(DOMAIN,TAG,add total%{public}d,next.length);发布前复查时要重点找反例question、answerText、deckName是否被直接写进日志分享、复制、收藏失败时是否把业务正文拼进错误信息。日志能帮助排障但不能成为第二条泄露渠道。发布前复核要覆盖五条路径发布截图完成前建议按五条路径复核。每条路径都对应一个真实 owner失败后能直接定位而不是只说“页面看起来不对”。路径操作通过标准失败时先看进入前备份进入截图环境前读取题库、收藏、历史、当前题库四类数据都有快照ReleaseSnapshotStore.createSnapshot()演示安装安装releaseScenarioV1首页、结果页、收藏页只显示演示内容ReleaseScreenshotInstaller.installScenario()页面刷新进入后重进首页、收藏页、历史弹层页面读取最新数据AppStorageKey.Last*UpdateAt日志复查搜索hilog附近的业务字段不出现问题原文和答案全文AnswerPage、FavoriteService、QuestionHistoryService退出恢复退出截图环境并冷启动原题库、收藏、历史、当前题库恢复Repository 批量恢复和CurrentDeckId如果某条路径当前还没有真机执行就在发布记录里写清楚它仍需实机复核。不要把静态源码复查、一次手动点击或文章示例说成完整运行证明。本地复查命令要围绕泄露字段写下面这些命令不是为了证明功能已经上线而是帮助写文章和做发布前复查时快速定位风险点。rg-nAnswerPage|param.text|deckName|FavoriteService.addD:\ProgramData\huawei\lesson\The_Book_of_Answers\libraryHSP\src\main\ets rg-nQuestionHistoryService|FavoriteRepository|QuestionHistoryRepositoryD:\ProgramData\huawei\lesson\The_Book_of_Answers rg-nhilog|question|answerText|fromDeckName|deckNameD:\ProgramData\huawei\lesson\The_Book_of_Answers\libraryHSP\src\main\ets rg-nPrefStoreName|AppPrefKey|AppStorageKeyD:\ProgramData\huawei\lesson\The_Book_of_Answers\libraryHAR\src\main\ets第一条找结果页泄露入口第二条找收藏和历史的持久边界第三条看日志有没有正文风险第四条确认恢复时要刷新哪些 live state。命令输出只能说明源码风险位置不能替代真机截图和退出恢复验证。常见问题与处理现象根因处理首页是演示题库结果页仍是真实答案只替换了题库列表没有替换当前抽取参数固定currentDeckId用演示题库完成抽取路径收藏页出现真实答案没有备份并替换favorite_list进入截图环境时用演示收藏列表替换退出时恢复历史弹层出现真实问题只清了页面状态没有写QuestionHistoryRepository用演示历史覆盖并刷新LastQuestionHistoryUpdateAt退出后当前题库丢失恢复了题库数据但没有恢复CurrentDeckId快照里保存当前题库 id退出后写回 Preferences 和 AppStorage补图时内容和上次不同演示场景用了随机 id 或当前时间固定scenarioId、答案 id、题库 id 和种子版本这些问题都不适合用 Toast 掩盖。Toast 只能提示当次操作不能证明发布截图来自干净数据也不能保证退出后真实数据恢复。小结第 50 篇真正要讲的是一个发布前数据沙箱现有源码已经证明题库、答案、收藏和历史都会进入页面发布截图前要把这几类数据统一换成演示场景截图后再恢复真实快照。这个方案的关键不是新增多少类而是把 owner 收清楚SeedLoader继续负责默认题库业务 Service 继续服务页面截图环境只负责备份、安装、恢复和清单。这样写出来的发布材料才可复现、可排障也不会把用户内容带进截图和日志。

相关新闻

Git版本控制完整教程

Git版本控制完整教程

1、Git简介与核心价值:1.1、Git在开发中的核心作用:Git 在开发中可以管理好我们的代码,包括代码的更改、删除、合并等,极大提升团队协作效率和开发效率。实际运用场景:在学校给的作业任务中,要我们敲一个记…

2026/10/16 13:29:44 阅读更多 →
InceptionTime震撼登场:时间序列分类领域的AlexNet革命

InceptionTime震撼登场:时间序列分类领域的AlexNet革命

InceptionTime震撼登场:时间序列分类领域的AlexNet革命 【免费下载链接】InceptionTime InceptionTime: Finding AlexNet for Time Series Classification 项目地址: https://gitcode.com/gh_mirrors/in/InceptionTime InceptionTime是GitHub加速计划中的明星…

2026/10/16 0:12:56 阅读更多 →
计算机毕业设计之基于SpringBoot的餐饮库存追踪系统设计与实现

计算机毕业设计之基于SpringBoot的餐饮库存追踪系统设计与实现

随着信息技术的飞速发展和互联网的普及,线上管理平台已成为当今社会经济发展的重要驱动力之一。本研究旨在设计并实现一个基于Java的餐饮库存追踪系统,在技术选择上,本项目采用了JAVA语言,MySQL数据库编程,使用springb…

2026/10/5 10:43:03 阅读更多 →

最新新闻

Java开发必备:Google Guava核心工具库实战解析

Java开发必备:Google Guava核心工具库实战解析

聊到Java生态里的工具库,Guava始终绕不过去。这个由Google开源并持续维护的核心库,几乎已经成为很多服务端项目的“标配依赖”——哪怕你不主动引,不少框架的底层也会带上它。我最早接触Google Guava是因为项目里要封装一个本地缓存&#xff…

2026/10/16 13:28:59 阅读更多 →
REA:面向开发者的零门槛运行时逆向分析工作流

REA:面向开发者的零门槛运行时逆向分析工作流

1. 项目概述:REA不是工具,而是一套“软件透视眼”工作流 三天11万星——这个数字在开源世界里不是热度,是地震波。当一个叫REA的项目突然冲上GitHub Trending榜首,评论区没人在问“怎么安装”,全在刷“我刚用它看穿了X…

2026/10/16 13:28:59 阅读更多 →
Nacos 2.2.3对接人大金仓8.6驱动配置全指南

Nacos 2.2.3对接人大金仓8.6驱动配置全指南

简介:本资源是一套面向Java微服务开发者与国产数据库集成实践者的Nacos人大金仓数据源配置实战方案,聚焦解决国产化信创环境下动态配置管理与高兼容性数据库接入难题。资源包共15个文件,含4个SQL脚本(含nacos-kingbase.sql等专用建…

2026/10/16 13:28:59 阅读更多 →
软考系统架构师案例题复盘:从瓶颈识别到架构决策

软考系统架构师案例题复盘:从瓶颈识别到架构决策

软考考完当天晚上,我就把2025年11月系统架构师案例题第二题的原题回忆了个大概。坐在考场里看到这道题的第一反应是:终于不考那些背熟了的固定套路了,题目把一套真实生产环境的架构问题摆到面前,让你在有限时间里当一次架构师。这…

2026/10/16 13:28:59 阅读更多 →
华为securec.zip嵌入式安全函数库集成与避坑指南

华为securec.zip嵌入式安全函数库集成与避坑指南

简介:本资源是面向C语言开发者与嵌入式安全工程师的华为安全C库函数实现集,严格遵循C11 Annex K标准中边界检查接口规范,重点解决传统C函数(如memcpy、strcpy)因缺乏长度校验导致的缓冲区溢出、内存越界等高危安全问题…

2026/10/16 13:28:59 阅读更多 →
Cocos Creator拖拽排序列表:ScrollView冲突与坐标换算实现

Cocos Creator拖拽排序列表:ScrollView冲突与坐标换算实现

简介:在Cocos Creator游戏与交互界面开发中,拖拽排序是常见需求。面向有一定引擎基础、希望实现可定制排序列表的开发者,这是一份可直接参考的Cocos Creator拖拽排序列表工程示例,覆盖场景搭建与脚本逻辑。压缩包共16个文件&#…

2026/10/16 13:27:56 阅读更多 →

日新闻

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

个人 AI Agent 平台接入 TaoToken:统一 Key 与 API 通道的配置大纲

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

2026/10/16 0:00:05 阅读更多 →
信号与系统频域分析作业解题指南

信号与系统频域分析作业解题指南

1. 作业背景与核心要求解析信号与系统作为电子信息类专业的核心基础课,其作业设计往往直接关联课程的核心知识框架。2026年春季第六次作业的布置,通常对应着课程进展到频域分析的关键阶段。根据常规教学进度推测,本次作业可能涉及以下核心内容…

2026/10/16 0:00:05 阅读更多 →
Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

Django MVT实战:从零构建图书借阅系统,彻底搞懂Model-View-Template分层

1. 从一个"四不像"的练手项目说起如果你已经看完了Django的官方教程前几章,跟着敲完了投票应用,大概会有一种"我好像会了,又好像什么都不会"的割裂感。教程里每个知识点都是拆开的——这一章讲模型,下一章讲视…

2026/10/16 0:00:05 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →