【细胞工坊|05】HarmonyOS ArkTS 实验结果页实战:展示关键指标并支持重新实验
部分内容由AI辅助生成。本文面向 HarmonyOS 5.0 及以上版本基于细胞工坊项目真实源码展开源码根目录为D:\huawei\one14-9。本文重点复核entry/src/main/ets/views/experiment/ExperimentResultPage.ets并结合ExperimentFeedback.ets的表头和结论模型不虚构源码里不存在的数据上报、云端分析或 AI 诊断能力。实验模拟类应用的结果页不能只显示一个“完成”按钮。用户完成显微观察、DNA 提取、PCR 扩增或无菌操作后真正关心的是三个问题这次实验关键指标是什么为什么得到这个结论能不能带着这次反馈回去重新调整参数。结果页如果只做成静态总结卡片就会把实验过程和下一轮操作断开。细胞工坊的ExperimentResultPage做的是一个前端闭环从路由读取完成时传入的结果快照构建表格数据绘制反馈曲线调用反馈模型生成结论支持系统分享并通过返回按钮回到实验页重新操作。它没有在结果页重新运行实验也没有把结果上传到服务器所有展示都来自本地快照和页面内计算这符合一个离线教学实验应用的边界。本文会解决四个具体问题如何用路由参数把实验完成时的关键指标传入结果页。如何把successRate、activity、contamination等最终值转成可读表格和曲线。如何让结果页结论、分享文本和表格表头使用同一套反馈模型。如何通过返回实验按钮形成“查看结果 - 调整参数 - 重新实验”的闭环。一、结果页只消费完成快照不重新计算实验来源实际项目里结果页最容易犯的错误是“再算一次”。模拟页已经根据实验参数和进度算出了成功率、活性、污染指数和温度结果页如果又根据自己的逻辑重新计算页面、分享内容和历史记录就可能出现不一致。ExperimentResultPage的入口参数很明确interface ResultRouterParams { expId?: string expName?: string category?: string progress?: number successRate?: number activity?: number contamination?: number temperature?: number stage?: string paramSummary?: string }这些字段来自上一页完成实验时构造的结果快照。结果页并不关心模拟页内部用了什么公式也不反向读取实验页状态。它只需要知道这次实验是什么、完成到哪个阶段、关键指标是多少、参数摘要是什么。这个设计能防止两类问题问题如果结果页重算当前快照方案指标来源不一致页面显示和分享文本可能不一致所有展示读取同一组状态回退后参数变化结果页可能跟着旧组件状态变化结果页持有完成时快照新实验扩展每页都要知道实验公式结果页只处理通用展示结果页不是实验引擎它是完成结果的展示层。这个边界越清楚后续扩展越稳。二、aboutToAppear()是快照落地的第一道边界结果页在aboutToAppear()中读取路由参数并把合法字段写入本页状态aboutToAppear(): void { const params router.getParams() as ResultRouterParams | undefined if (params?.expId) this.expId params.expId if (params?.expName) this.expName params.expName if (params?.category) this.category params.category if (typeof params?.progress number) this.progress params.progress if (typeof params?.successRate number) this.successRate params.successRate if (typeof params?.activity number) this.activity params.activity if (typeof params?.contamination number) this.contamination params.contamination if (typeof params?.temperature number) this.temperature params.temperature if (params?.stage) this.stage params.stage if (params?.paramSummary) this.paramSummary params.paramSummary this.tableData this.buildTableData() }这里有两个细节值得保留。第一数值字段使用typeof params?.xxx number判断。这样可以避免字符串、空值或未传字段覆盖默认值。HarmonyOS 页面路由在实际项目中经常会遇到参数缺失结果页不应该因为某个字段缺失就直接白屏。第二状态落地后立即调用buildTableData()。这意味着表格和曲线基于同一组页面状态生成而不是等到渲染时临时拼凑。后续如果要增加加载态或异常态也能围绕这一处做守卫。三、关键指标用页面状态保存便于表格、曲线和分享复用源码中结果页为关键指标提供了默认值State expId: string microscope_observation State expName: string 显微镜观察洋葱表皮 State category: string 基础 State progress: number 100 State successRate: number 96 State activity: number 94 State contamination: number 20 State temperature: number 37 State stage: string 实验完成 State paramSummary: string 默认实验条件这些字段不是随便展示的文案而是结果页的核心数据源字段页面用途expId选择表头、结论模板和曲线形状expName页面标题、图表标题、分享标题category分享文本中的实验分类progress表格进度和结论进度successRate最终评分、结论判断activity曲线第一指标和实验表现contamination曲线第二指标和风险判断temperature结论和分享中的环境/实验条件paramSummary解释这次结果对应的参数组合结果页的核心不是卡片数量而是这些字段能否被一致地复用。页面标题、表格、曲线、结论和分享文本都读取这些状态用户看到的结果就不会分裂。四、表格数据不是直接写死而是根据最终值生成过程点结果页默认表格有 6 行从 0% 到 100%State tableData: number[][] [ [0, 62, 8, 74], [20, 70, 10, 80], [40, 78, 12, 86], [60, 84, 15, 89], [80, 90, 18, 93], [100, 94, 20, 96] ]但进入页面后tableData会被buildTableData()重新构建。这个函数读取最终activity、contamination、successRate按进度点生成过程数据private buildTableData(): number[][] { const result: number[][] [] const a this.activity const c this.contamination const s this.successRate for (let i 0; i 5; i) { const progress i * 20 const t progress / 100 let v1 a * (0.58 t * 0.42) let v2 c * (0.36 t * 0.64) let v3 s * (0.74 t * 0.26) result.push([ progress, Math.round(this.clamp(v1, 0, 100)), Math.round(this.clamp(v2, 0, 100)), Math.round(this.clamp(v3, 0, 100)) ]) } return result }这段默认逻辑的意义是即使某个实验没有专属曲线分支结果页也能展示一条基本可读的指标走势。它不是科学实验真实采样点而是教学模拟中的过程展示。更重要的是源码为不同expId加了曲线分支。显微观察、细菌培养、无菌操作、PCR、DNA 分层、细胞周期等实验有不同曲线形状这让结果页不会把所有实验都画成同一种线。五、曲线形状表达实验过程不只是装饰图结果页用shape()把 0-1 的进度映射成不同曲线private shape(kind: string, t: number): number { if (t 0) return 0 if (t 1) return 1 if (kind linear) return t if (kind sqrt) return Math.sqrt(t) if (kind square) return t * t if (kind cube) return t * t * t if (kind s) return 1 / (1 Math.exp(-(t - 0.5) * 8)) if (kind peak) return Math.sin(t * Math.PI) if (kind lateRise) return t 0.3 ? t * 0.4 : 0.12 (t - 0.3) / 0.7 * 0.88 if (kind platL) return 1 - Math.exp(-t * 3) if (kind invSquare) return 1 - (1 - t) * (1 - t) return t }这段逻辑让结果页的曲线具备业务含义。例如曲线类型代码含义典型实验sS 型增长细菌培养cube后期快速上升PCR 扩增、污染后期暴涨peak中段峰值有丝分裂、细胞周期lateRise滞后后拉升叶绿素层析platL快升后平台发酵、DNA 分层离心invSquare收敛下降混浊、误识别、杂光下降结果页并没有保存真实过程采样数组所以曲线不能被描述为真实实验数据。更准确的说法是它根据最终指标和实验类型生成教学模拟曲线。这个边界必须说清楚否则文章会夸大源码能力。六、Canvas 曲线绘制读取同一份tableData反馈曲线用 Canvas 绘制核心输入仍然是tableDataprivate drawChart(): void { if (this.chartW 0) return const ctx this.chartCtx const w this.chartW const h this.chartH const padL 46 const padR 20 const padT 34 const padB 38 const plotW w - padL - padR const plotH h - padT - padB ctx.clearRect(0, 0, w, h) ctx.fillStyle #111827 ctx.fillRect(0, 0, w, h) }随后它绘制坐标轴、横向网格、折线和节点ctx.strokeStyle AppColors.ACCENT_GREEN ctx.lineWidth 3 ctx.beginPath() for (let i 0; i this.tableData.length; i) { const row this.tableData[i] const x padL (row[0] / 100) * plotW const y padT plotH - (row[1] / 100) * plotH if (i 0) ctx.moveTo(x, y) else ctx.lineTo(x, y) } ctx.stroke()当前源码只绘制了row[1]这一条主曲线也就是第一业务指标。表格里仍然保留row[2]和row[3]用于展示风险指标和最终评分。这有一个现实取舍单条曲线更清晰不会在手机屏幕上把三条线挤在一起但如果后续要做专业数据分析可以增加图例并把row[2]、row[3]也画出来。七、数据表格和反馈曲线用 Tab 分流减少一屏信息压力结果页通过activeTab在“数据表格”和“反馈曲线”之间切换State activeTab: number 1UI 里两个文本按钮分别设置activeTabText(数据表格) .fontColor(this.activeTab 0 ? #07111F : AppColors.PRIMARY) .backgroundColor(this.activeTab 0 ? AppColors.ACCENT_GREEN : AppColors.CARD_BG) .onClick(() { this.activeTab 0 }) Text(反馈曲线) .fontColor(this.activeTab 1 ? #07111F : AppColors.PRIMARY) .backgroundColor(this.activeTab 1 ? AppColors.ACCENT_GREEN : AppColors.CARD_BG) .onClick(() { this.activeTab 1 })这是一个适合移动端的折中。表格适合复查具体数字曲线适合观察趋势。如果强行把表格、曲线、结论、分享、返回按钮都塞在同一个首屏结果页会变成信息堆叠。对 HarmonyOS 多设备适配来说后续可以在平板或 2in1 上把表格和曲线并排显示但手机端保持 Tab 更稳。八、表头来自反馈模型避免结果页写死列名结果页没有固定写“进度、活性、污染、评分”而是调用反馈模型private currentSchema(): FeedbackSchema { return getFeedbackSchema(this.expId) } private formatCell(value: number, unit: string): string { return ${value.toFixed(0)}${unit} }表格头部渲染ForEach(this.currentSchema().headers, (h: string) { Text(h) .fontSize(12) .fontWeight(AppFonts.WEIGHT_MEDIUM) .fontColor(AppColors.TEXT_SECONDARY) .layoutWeight(1) .textAlign(TextAlign.Center) })这让结果页可以复用到不同实验。显微观察显示“视野、清晰度、杂光、识别率”DNA 分层显示“转速段、分层度、混浊、纯度”PCR 显示“循环、扩增量、非特异、纯度”。如果新实验接入后表头不对问题通常不在结果页而在ExperimentFeedback.ets没有为该expId配置 schema。这就是职责分离的价值。九、实验结论复用反馈模型分享文本也复用同一结论结果页生成结论时会构造ConclusionContextprivate getConclusion(): string { const ctx: ConclusionContext { expName: this.expName, stage: this.stage, paramSummary: this.paramSummary, progress: this.progress, successRate: this.successRate, activity: this.activity, contamination: this.contamination, temperature: this.temperature } return getConclusion(this.expId, ctx) }分享文本也调用同一个getConclusion()private buildShareText(): string { return [ 细胞工坊 - ${this.expName}, 实验分类${this.category}, 实验参数${this.paramSummary}, 流程进度${this.progress}%, 成功率${this.successRate}%, 样本活性${this.activity}%, 污染指数${this.contamination}%, 温度${this.temperature}℃, 结果分析${this.getConclusion()} ].join(\n) }这点很重要。页面结论、分享描述和分享正文使用同一套结论来源用户不会看到“页面提示污染偏高分享文本却说实验稳定”的冲突。结果类页面经常被用户截图、分享或复制因此所有出口都应复用同一份结果解释。十、系统分享失败必须有用户提示和日志结果页使用 HarmonyOSShareKitconst record: systemShare.SharedRecord { utd: uniformTypeDescriptor.UniformDataType.PLAIN_TEXT, title: ${this.expName}结果分析, description: this.getConclusion(), content: this.buildShareText() } const data new systemShare.SharedData(record) const controller new systemShare.ShareController(data)如果宿主上下文为空或系统分享面板失败页面不会静默吞掉错误private showShareFailureToast(): void { try { this.getUIContext().getPromptAction().showToast({ message: 系统分享暂不可用请稍后再试 }) } catch (err) { const message err instanceof Error ? err.message : JSON.stringify(err) hilog.error(LOG_DOMAIN, LOG_TAG, Show share failure toast failed: %{public}s, message) } }这符合结果页的用户体验要求分享不是核心实验流程但失败时也要让用户知道发生了什么。日志使用hilog.error不会把错误直接暴露给用户。十一、重新实验靠返回链路不在结果页复制实验逻辑结果页底部有两个按钮Button(保存记录) .fontSize(14) .fontColor(AppColors.PRIMARY) .backgroundColor(AppColors.CARD_BG) Button(返回实验) .fontSize(14) .fontColor(#07111F) .backgroundColor(AppColors.ACCENT_GREEN) .onClick(() { router.back() })返回实验的实现非常克制只调用router.back()。它不在结果页重新创建实验不复制参数调整 UI也不试图重置模拟页。结果页的职责是告诉用户本次结果如果用户要重新实验就回到实验页重新操作。这能避免两类重复结果页不需要知道实验页的按钮、滑块、Canvas 和计时器状态。实验页仍然是唯一的参数调整和运行入口。需要注意的是源码里的保存记录按钮目前只有样式没有绑定保存动作。不能在文章中声称它已经保存结果当前真实可复核的是“按钮存在”保存记录逻辑实际在模拟页完成时已经通过persistRecord()触发。十二、结果页适配与可读性的检查点结果页由标题栏、Tab、Canvas 或 List、结论卡片、底部按钮组成。上线前至少检查这些点检查项操作期望结果参数缺失直接进入结果页或缺字段路由使用默认值不白屏表格 Tab点击数据表格四列表头与当前实验语义一致曲线 Tab点击反馈曲线Canvas 成功绘制主指标折线长实验名称使用较长expName标题居中且不遮挡分享按钮长结论污染偏高或 PCR 非特异场景结论卡片文字可读不压底部按钮分享失败模拟 host context 为空toast 显示“系统分享暂不可用请稍后再试”返回实验点击返回实验返回上一页不在结果页重建实验如果只做一次快速验证优先覆盖“完成实验 - 查看结果 - 切换表格/曲线 - 分享 - 返回实验”。这条路径覆盖了结果页的主要职责。十三、常见问题与修复建议现象首查位置原因修复结果页指标是默认值路由传参模拟页没有传入对应字段检查buildResultParams()和ResultRouterParams字段一致性表格列名不符合实验getFeedbackSchema()新实验没有专属 schema为expId配置表头和单位曲线没有显示chartW、onReady()Canvas 未 ready 或尺寸为 0保留if (this.chartW 0) return检查容器高度分享点击没反应shareResult()host context 为空或系统分享失败保留 toast 和hilog.error返回后状态异常路由栈结果页不是从实验页进入确认入口路径必要时提供显式重开实验路由保存记录按钮无效果当前按钮未绑定源码只有 UI 样式不在结果页声明已保存如要支持补具体事件排查时先确认结果快照。快照正确表格和结论才有意义快照错误页面样式再好也只是展示了错误结果。总结细胞工坊的实验结果页实现了一条清晰的 HarmonyOS ArkTS 前端链路从路由读取完成快照把关键指标落到状态生成表格数据使用 Canvas 绘制反馈曲线通过反馈模型输出结论支持 ShareKit 分享并通过router.back()返回实验页重新操作。这套实现的重点不是复杂而是边界清楚。结果页不重新运行实验不夸大数据来源不把“保存记录”按钮说成已经实现的业务动作。它只负责把已经完成的实验结果展示清楚并提供下一轮操作入口。对教学实验类 HarmonyOS 应用来说这比堆一个静态总结页更可靠也更容易维护。

相关新闻

【细胞工坊|03】HarmonyOS ArkTS DNA 提取流程实战:用步骤状态避免跳步和错误结果

【细胞工坊|03】HarmonyOS ArkTS DNA 提取流程实战:用步骤状态避免跳步和错误结果

部分内容由AI辅助生成。本文面向 HarmonyOS 5.0 及以上版本,基于 细胞工坊 项目真实源码展开,源码根目录为 D:\huawei\one14-9。文中示例围绕 entry/src/main/ets/views/experiment/ExperimentSimPage.ets、entry/src/main/ets/model/Experiment.ets 和 …

2026/10/12 20:11:12 阅读更多 →
重庆大学Green Chem. :赤泥高效变废为宝!电热冲击协同回收铁与稀贵金属

重庆大学Green Chem. :赤泥高效变废为宝!电热冲击协同回收铁与稀贵金属

通讯作者:王心月、陈志良通讯单位:重庆大学DOI:https://doi.org/10.1039/D6GC01682K核心导读:赤泥(拜耳法氧化铝生产副产物)年产 1.2–1.5 亿吨,累计堆存超 40 亿吨,富含铁、钪&…

2026/10/6 19:10:24 阅读更多 →
中国科学技术大学Nano Research:毫秒闪速焦耳热锚定原子Cu,TiO₂光催化CO₂还原效率提升10倍

中国科学技术大学Nano Research:毫秒闪速焦耳热锚定原子Cu,TiO₂光催化CO₂还原效率提升10倍

通讯作者:熊宇杰 、张超通讯单位:中国科学技术大学DOI:10.26599/NR.2026.94908854核心导读:原子级分散金属(ADMs)能最大化原子利用率、提供独特配位活性位点,是提升光催化CO₂还原效率的前沿策略…

2026/10/12 22:24:31 阅读更多 →

最新新闻

同花顺日线文件解析:从二进制乱码到可回测的OHLCV表

同花顺日线文件解析:从二进制乱码到可回测的OHLCV表

简介:这份资源面向金融数据分析初学者与量化爱好者,聚焦同花顺日线二进制文件(.day)的解析问题。压缩包仅4KB,内含2个文件:一个以600000为代码的日线样本文件,以及一份约20行的Python解析脚本&a…

2026/10/13 22:02:46 阅读更多 →
Axure RP 9 高效原型设计:动态面板、事件配置与母版管理实战指南

Axure RP 9 高效原型设计:动态面板、事件配置与母版管理实战指南

简介:《Axure RP 9 使用详解》docx文档面向产品经理、交互设计师及原型初学者,系统讲解从基础页面布局到动态面板、内联框架、函数等进阶交互功能,尤其针对项目投标时原型演示拿不出手的痛点,帮助读者做出专业、可演示的高保真原型…

2026/10/13 22:02:46 阅读更多 →
粒子群无功优化MATLAB实现:基于IEEE30节点的完整代码解析

粒子群无功优化MATLAB实现:基于IEEE30节点的完整代码解析

简介:针对电力系统网损偏高、电压质量不佳等运行优化需求,该资源提供基于粒子群算法的无功优化实现代码,采用IEEE30节点标准测试系统作为算例,适合电力专业研究生、科研人员与算法爱好者参考学习。代码完整覆盖粒子群寻优流程&…

2026/10/13 22:02:46 阅读更多 →
Spring Boot入门:自动配置与配置文件实战解析

Spring Boot入门:自动配置与配置文件实战解析

我最初接触 Spring Boot 时,最直观的感受就是"懒人福音"。这是 Spring 生态里少数能让我这种被 XML 配置折磨过的人,第一次体会到"开箱即用"的框架。如果你之前用传统 Spring 搭过项目,一定记得那些繁琐的配置&#xff1…

2026/10/13 22:02:46 阅读更多 →
FreeCAD中文翻译教程避坑指南:从参数化建模到动态数据驱动

FreeCAD中文翻译教程避坑指南:从参数化建模到动态数据驱动

简介:面向FreeCAD初学者的中文翻译版操作手册,它将官方英文文档wiki整理成更循序渐进的中文读本,帮助零基础用户快速理解参数化建模思路并掌握软件操作。FreeCAD是免费开源的跨平台3D建模软件,本手册涵盖安装、界面导航、3D视图、…

2026/10/13 22:02:46 阅读更多 →
基于SpringBoot的北京民宿推荐系统:内容算法与首都特色实践

基于SpringBoot的北京民宿推荐系统:内容算法与首都特色实践

1. 内容整体设计与思路拆解1.1 为什么是民宿推荐系统,而不是酒店预订系统先说个现象。很多人在做毕业设计时,一提到旅游住宿类系统,第一反应就是"酒店管理系统""民宿预订平台",但这类题目在答辩时很容易被打低…

2026/10/13 22:01:45 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 18:11:17 阅读更多 →