Serial Studio 操作员模式加固:只读仪表盘界面收口与 `--no-taskbar-search` 部署选项的实现拆解

发布时间:2026/11/10 12:16:11

Serial Studio 操作员模式加固:只读仪表盘界面收口与 `--no-taskbar-search` 部署选项的实现拆解 Serial Studio 操作员模式加固只读仪表盘界面收口与--no-taskbar-search部署选项的实现拆解【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio本文基于开源仓库中doc/claude/specs/0015-operator-mode-hardening/三份规格文档spec.md/plan.md/tasks.md及其落地源码完整拆解 Serial Studio 操作员模式Operator Mode加固这一 4 阶段规格驱动改造操作员部署应把仪表盘锁定为只读看板——冻结开关、工作区编辑、组件标题菜单等作者级控制不再外泄同时新增搜索栏这一部署期开关并由此引出--no-taskbar-search命令行标志。读完本文你将掌握四条泄漏面的 QML 收口手法、--no-taskbar-search从 CLI 解析到部署快捷方式生成再到运行时生效的完整调用链、每个任务的验证方式以及这套规格工作流中任务分解—验收标准—Definition of Done如何互相咬合。一、背景操作员部署为何需要只读加固Serial Studio 的操作员部署Operator Deployment是指通过New Deployment对话框生成的快捷方式作者负责设计项目操作员只负责观看实时数据。部署快捷方式以--runtime模式启动应用见 core/Ui/Misc/ShortcutGenerator.cppQML 侧通过CLI_RUNTIME_MODE上下文常量暴露运行态见 app/qml/main.qmlreadonly property bool runtimeMode: typeof CLI_RUNTIME_MODE ! undefined CLI_RUNTIME_MODE true在加固之前spec 0015 审计结论有三类作者级控制泄漏进了操作员模式仪表盘冻结freeze可被操作员切换任务栏冻结按钮与开始菜单的 Freeze Dashboard 在操作员模式下都可用。冻结状态本身存储在项目文件中spec 0007操作员一翻开关就静默覆盖了作者意图——作者精心发布的冻结 kiosk 布局操作员可以解冻、拖动组件、改变展示内容。当前工作区可被操作员编辑任务栏的编辑工作区按钮、任务栏按钮右键 Remove from Workspace 在操作员模式下对用户工作区id 1000仍然生效而开始菜单一侧早已在操作员模式下抑制了重命名/隐藏/删除与 New Workspace…行为既不一致又过度宽松。搜索栏不是部署期选择操作员部署中是否显示任务栏搜索框继承的是本机 Preferences 保存的作者偏好部署作者无法在生成快捷方式时决定这个 kiosk 没有搜索栏。审计中还发现第四块处于同一信任边界的表面维护者 2026-07-17 决策纳入范围组件标题菜单Rename Widget…、Freeze Title模式同样会修改项目且未在操作员模式下加门禁。二、需求骨架R1–R7 与两条改造主线完整需求定义在 spec.md 的 Requirements 一节tasks.md的任务清单就是把这些需求拆成可单独评审的单元编号需求对应任务R1操作员模式下仪表盘 UI 任何位置都不可达冻结开关冻结状态照项目文件原样生效T1、T2R2操作员模式下编辑工作区控件不可达右键任务栏组件按钮无 Remove from WorkspaceT1R3操作员模式下无其他路径可创建/重命名/隐藏/删除工作区既有拦截保持存量逻辑T1 保持R4New Deployment 对话框任务栏选项含搜索栏开关默认开启快捷方式记录该选择T4、T5、T6R5搜索关闭的部署永不显示搜索框、快捷键无效开启时不受宿主机保存偏好影响T4、T6R6仅当任务栏可出现shown/autohide时搜索栏选项才有意义hidden 部署两者皆无T6R7操作员模式下组件标题菜单不提供项目变异条目仅保留 Open in External WindowT3plan.md给出了两条相互独立的改造主线门禁主线Gating纯 QML 改动把已有的逐控件操作员门禁惯用法!app.runtimeMode/CLI_RUNTIME_MODE与 New Workspace…、pin 注册表同一套扩展到四条泄漏面所有被抑制控件在操作员模式下直接消失spec Q3而不是变灰零 C 改动。搜索栏主线Search-bar option新增否定式 CLI 标志--no-taskbar-search由部署生成器在作者关闭新 Search Bar 开关时写入快捷方式CLI::applyOperatorTaskbarSettings()每次操作员启动时据此推导TaskbarSettings::searchEnabled。三、任务分解约定每任务一变更、可独立验证tasks.md开头明确了 Phase 3 的编排公约这也是本规格工作流spec → plan → tasks → implement中可评审 diff的组织原则一任务一聚焦变更若一个任务触及超过 3 个文件或需要一段话来描述就拆分。Verify 是单元级确认手段通常是python scripts/code-verify.py --check files外加一项测试或回读确认。Deps 声明前置依赖任务按依赖顺序落地保证每步之后代码树概念上可编译。七个任务 T1–T7 全部标记为[x] done规格状态为approvedtasks.md头部元数据spec.md状态为done。四、门禁主线T1–T3 三条 QML 收口T1 — 任务栏冻结按钮 编辑工作区控件 右键菜单改动文件app/qml/MainWindow/Panes/Dashboard/Taskbar.qml。三处收口冻结按钮_freezeButton.visible追加 !app.runtimeModeTaskbar.qmlTrayButton { id: _freezeButton ... visible: Cpp_AppState.operationMode SerialStudio.ProjectFile !app.runtimeMode注意该按钮同时受freezeAllowed商业构建 许可证激活或试用门控opacity: freezeAllowed ? 1 : 0.5运行时直接整体不可见。编辑工作区按钮visible: !app.runtimeModeTaskbar.qml——隐藏而非禁用spec Q3与 New Workspace… 的既有消失方式一致。右键 Remove from Workspace 守卫组件按钮的TapHandler增加!app.runtimeMode条件使_tbContextMenu在操作员模式下永不弹出Taskbar.qmlTapHandler { acceptedButtons: Qt.RightButton onTapped: { if (taskBar taskBar.activeGroupId 1000 !Cpp_UI_Dashboard.frozen !app.runtimeMode) { root.tbRemoveWindowId button.model.windowId _tbContextMenu.popup() } } }_tbContextMenu仅含一个 Remove from Workspace 菜单项Taskbar.qml调用taskBar.removeWidgetFromActiveWorkspace(...)。这里还可以看到该文件的既有门禁惯用法——pin 注册表的pinVisible()同样以!app.runtimeMode过滤 settings/console/ai_assistant 等 pinTaskbar.qml且draggable: !app.runtimeMode禁用了操作员拖拽重排。任务的不变量要求只改可见性与守卫条件不新增或变更任何信号连接——_freezeButton.onClicked中的setFrozen/showLicenseDialog分支保持不变冻结状态仍由Cpp_UI_Dashboard.frozen驱动。验证python scripts/code-verify.py --check app/qml/MainWindow/Panes/Dashboard/Taskbar.qml并回读三个门禁点冻结按钮可见链、编辑按钮、TapHandler 守卫。T2 — 开始菜单冻结条目消失改动文件app/qml/MainWindow/Panes/Dashboard/StartMenu.qml。_freezeBt的visible追加 !app.runtimeModeStartMenu.qmlStartMenuToggle { id: _freezeBt commandId: dashboard.freeze }该条目通过命令注册表Commands.CommandModel上下文dashboard渲染。关键细节是分隔线不孤儿化_freezeBt上方的 Auto Layout 切换commandId: dashboard.autoLayout与其下的分隔线visible: _autoLayoutBt.visible || _freezeBt.visible逻辑互相关联StartMenu.qml冻结条目消失不会造成残留分隔线。另一个验证点是搜索面AC1 要求任务栏搜索 freeze 不应返回冻结动作。StartMenu.qml的searchableItems()StartMenu.qml直接把_menuModel.items(filter)转发给命令面板命令条目不可见即不可搜因此冻结命令隐藏后自然从搜索结果中消失。验证python scripts/code-verify.py --check app/qml/MainWindow/Panes/Dashboard/StartMenu.qml回读searchableItems()不再列出冻结动作。T3 — 组件标题菜单仅保留 Open in External Window改动文件app/qml/MainWindow/Panes/Dashboard/WidgetDelegate.qml。两处机制并用门禁属性折叠displayEditable追加 !app.runtimeModeWidgetDelegate.qmlreadonly property bool displayEditable: root.entityUniqueId 0 Cpp_AppState.operationMode SerialStudio.ProjectFile !app.runtimeModeRename Widget…enabled: root.displayEditableWidgetDelegate.qml与 Freeze Title 子菜单的三个模式项Title Bar / Painted Title / HiddenWidgetDelegate.qml全部以此门禁它们触发Cpp_JSON_ProjectModel.promptRenameWidget(...)与setFreezeTitleMode(...)——这正是操作员模式下必须不可达的项目变异调用点。运行时物理移除由于 QQC2 嵌套Menu设visible: false未必在所有 Qt 版本上真正收起其生成的菜单项plan.md 记录的风险任务采用Component.onCompleted时removeMenu()/removeItem()的兜底方案WidgetDelegate.qmlComponent.onCompleted: { if (app.runtimeMode) { widgetMenu.removeMenu(freezeTitleMenu) widgetMenu.removeItem(renameItem) widgetMenu.removeItem(captionSeparator) } }即Rename Widget…、嵌套 Freeze Title 子菜单及其MenuSeparatorcaptionSeparator在操作员模式下被显式移除菜单最终只剩 Open in External WindowWidgetDelegate.qml触发externalWindowClicked()是非变异条目按 R7 保留。removeMenu/removeItem的可用性在 T7 中对照 Qt 6.11 文档确认。关键不变量每组件冻结标题的显示路径必须保持工作——effectiveFreezeTitlebar/painted/hidden 三态解析WidgetDelegate.qml与refreshFreezeTitleMode()只读读取Cpp_JSON_ProjectModel.freezeTitleMode(...)不受此次改动影响被删除的只是变异入口。验证python scripts/code-verify.py --check app/qml/MainWindow/Panes/Dashboard/WidgetDelegate.qml回读运行时模式下无setFreezeTitleMode/promptRenameWidget可达调用点且读路径未被触碰。五、搜索栏主线T4–T6 从 CLI 标志到部署对话框T4 — CLI 标志--no-taskbar-search改动文件app/src/Misc/CLI.h、app/src/Misc/CLI.cpp。选项定义Pro 标记措辞与既有操作员选项一致CLI.hQCommandLineOption noTaskbarSearchOpt{ no-taskbar-search, Hide the taskbar search bar in operator runtime mode (Pro)};注册进解析器CLI.cpp并在applyOperatorTaskbarSettings()中推导搜索启用状态CLI.cppvoid CLI::applyOperatorTaskbarSettings() { static auto tbs UI::TaskbarSettings::instance(); if (m_parser.isSet(m_opts.taskbarModeOpt)) { const QString mode m_parser.value(m_opts.taskbarModeOpt).toLower(); tbs.setTaskbarHidden(mode QStringLiteral(hidden)); tbs.setAutohide(mode QStringLiteral(autohide)); } else { tbs.setTaskbarHidden(false); tbs.setAutohide(false); } ... tbs.setSearchEnabled(!m_parser.isSet(m_opts.noTaskbarSearchOpt)); }这里集中体现了两个关键设计否定式标志negative flag而非取值选项--no-taskbar-search缺席即搜索开启spec Q2 / AC4。这让向后兼容成为结构性规则——旧快捷方式无需任何迁移特判天然解析为搜索开启plan.md 对比了--taskbar-search on|off取值式与在--taskbar-buttons里塞伪 pin 的备选方案前者多一套解析而无表达力增益后者污染了 pin 语义。持久化关闭先于设置applyOperatorRuntimeSettings()中先执行taskbarSettings.setSettingsPersistent(false)CLI.cpp再调用applyOperatorTaskbarSettings()CLI.cpp保证作者的磁盘Taskbar/SearchEnabledQSettings 值永不被写入——操作员会话本就不落任何持久化状态。setSearchEnabled是单一事实源任务栏搜索框可见性Taskbar.qml与focusSearch()守卫Taskbar.qml都直接读Cpp_UI_TaskbarSettings.searchEnabled因此 R5 不需要任何 QML 二次改动。T5 — ShortcutGenerator 发射该标志改动文件core/Ui/Misc/ShortcutGenerator.h、core/Ui/Misc/ShortcutGenerator.cpp。generate(...)公有槽与私有buildArguments(...)各增加bool taskbarSearch参数紧邻既有 taskbar 参数放置ShortcutGenerator.h。参数生成逻辑ShortcutGenerator.cppif (!taskbarSearch) args QStringLiteral(--no-taskbar-search);字节级兼容保证只有当taskbarSearch false时才追加该参数搜索开启的快捷方式输出与改动前逐字节一致存量快捷方式无需迁移AC4。该函数同时负责生成--runtime、--fullscreen、--taskbar-mode、--taskbar-buttons、--theme等既有部署参数ShortcutGenerator.cpp可见--no-taskbar-search是任务栏系列旋钮的自然延续。调用方唯一性在 T5 验证中通过 grep 确认QML 通过public slots:调用Cpp_ShortcutGenerator.generate因此 C 侧无其他触点。T6 — New Deployment 对话框 Search Bar 开关改动文件app/qml/Dialogs/ShortcutGenerator.qml。在 Taskbar 标签页 Visibility 区段Mode 下拉之后新增一行LabelSwitchShortcutGenerator.qmlLabel { text: qsTr(Search Bar) color: Cpp_ThemeManager.colors[text] enabled: root.taskbarMode ! hidden opacity: enabled ? 1 : 0.5 } Switch { id: searchBar checked: true enabled: root.taskbarMode ! hidden ... }要点与源码对应关系默认开启checked: true对应 R4今天的有效默认R6 的不可应用呈现taskbarMode hidden时行被禁用并以 0.5 透明度变暗——与 Pinned Buttons 各行的处理完全一致同文件 0.5-opacity 惯用法可见于 ShortcutGenerator.qml 等多处hidden 模式的下方提示文案也会切换为隐藏任务栏会移除最小化/最大化/关闭按钮并强制自动布局每次打开对话框重置为开启onVisibleChanged中searchBar.checked trueShortcutGenerator.qml与其他 per-open 重置并列位置参数严格对齐 T5 签名文件保存回调中const hasSearch searchBar.checked随后按 T5 新增的位置槽位传给Cpp_ShortcutGenerator.generate(file, title, iconPath, projectPath, isFullscreen, hasActions, hasFileTransmission, hasCsv, hasMdf, hasSession, hasConsole, mode, buttons, hasSearch, theme)ShortcutGenerator.qml。T6 验证中专门回读 QML 实参顺序与 T5 签名完全一致——这是长位置参数列表最容易出错的地方。新增字符串是纯qsTr(Search Bar)无%n/占位符不会触发.arg()类翻译损坏翻译经常规 lupdate /sanitize-commit.py流程从 ts 文件重新生成app/translations/ts不手工编辑。六、T7 — 自审与交接检查最后一个任务是整体自审tasks.md范围反事实检查对照 spec 0015 范围重读完整 diff六个文件 规格产物无其他点名本 diff 最可能触犯的规则scope creep / 触碰无关文件及持有证据静态检查全部触及文件过仓库 linterC 增量过qt-cpp-review记录在案的评审结论qt-cpp-review0 条确认问题、1 条调查备注既有的 15 参数位置参数 QML 边界结论为 extend-consistently——即沿既有模式扩展Menu.removeItem/removeMenu的 Qt 6.11 可用性已确认提交前流水线的刻意推迟sanitize-commit.py有意未运行因为工作树中还有无关的 spec-0014 在途改动Plot*、Frame.、ProjectEditor、ci.yml 等仓库级流水线clang-format / code-verify --fix / lupdate会重写本次未编辑的文件待 0014/0015 分别暂存后在提交时执行。七、Definition of Done 与验收映射tasks.md末尾的 DoD 清单与spec.md的 AC1–AC5 一一对应验收标准验证方式维护者 in-app 检查落地依据AC1 无冻结开关、冻结被尊重冻结项目部署 → 任务栏/开始菜单无冻结控件、布局锁定未冻结项目 → 布局可交互仍无冻结入口搜 freeze 无结果T1、T2 searchableItems()AC2 无工作区编辑用户工作区id ≥ 1000部署 → 编辑按钮消失、右键任务栏按钮无移除动作、切换器仅切换同机作者模式三项全保留T1 StartMenu 存量 runtimeMode 拦截StartMenu.qmlAC3 部署期搜索选项对话框显示 Search Bar 默认开、hidden 模式变暗生成 search-on/search-off 两个部署在本机偏好相反的机器上验证字段与 CtrlF 跟随部署而非机器T4、T5、T6AC4 旧快捷方式兼容改动前的快捷方式照常启动且搜索栏可见与本机存储偏好无关否定式标志的缺席即开启语义AC5 标题菜单收口操作员部署 → 标题菜单仅 Open in External Window作者模式完整T3DoD 其余项code-verify.py --check全部触及文件干净热路径未触碰plan.md 声明无 FrameReader/CircularBuffer/FrameBuilder 代码、无 Dashboard 绘制路径、无缓存标志输入无需额外--benchmark-hotpath无 pytest 表面被改动diff 无越界文件spec.md状态置为done。八、架构与数据流小结两条主线都没有新增对象、信号或数据路径plan.md 明确声明搜索选项数据流作者在ShortcutGenerator.qml拨动Switch→Cpp_ShortcutGenerator.generate(..., taskbarSearch, ...)→buildArguments()将--no-taskbar-search写入快捷方式文件与--taskbar-mode/--taskbar-buttons同一机制→ 操作员启动时CLI::applyOperatorRuntimeSettings()仅运行时模式→applyOperatorTaskbarSettings()→TaskbarSettings::setSearchEnabled(...)持久化已先行关闭 →searchEnabledChanged驱动既有 QML 绑定搜索框可见性、focusSearch()守卫、CtrlF 快捷键R5 无需任何 QML 编辑。门禁数据流纯 QMLvisible:/守卫编辑消费既有的app.runtimeMode上下文属性由main.qml基于CLI_RUNTIME_MODE定义冻结状态仍按原路径从项目 JSON 经ProjectModel::frozen→UI::Dashboard::frozen流动spec 0007 既有接线本次只移除 UI 触发入口不改动接线。持久化影响为零无新增Keys::键、无项目 JSON 结构变更、无 schema 版本提升部署快捷方式文件只多一个可选参数缺席保持搜索开启的今日含义旧快捷方式零迁移。九、工程启示可评审任务分解的样板从这套规格流程中可以提炼出可复用的实践任务粒度规则一任务一聚焦变更3 文件或需一段话描述即拆分——T1–T6 每个都只动 1–2 个文件评审者可以隔离阅读每一个 diff。显式不变量T1不改信号连接、T3只删变异入口保留读路径、T5搜索开启时输出字节级不变都是防止顺手改动的锚点。验证与任务绑定每个任务自带code-verify.py --check命令与回读点静态检查 维护者 in-app 检查AC1–AC5各司其职——UI 与 CLI 启动类改动不硬套 pytest。风险有兜底QQC2 嵌套 Menu 隐藏怪癖用removeMenu()兜底菜单分隔线孤儿化靠邻项枚举规避范围蔓延靠审计中发现即上报批准而非悄悄塞入。如需继续深入可阅读同目录的 spec.md需求与验收标准全文与 plan.md技术设计与备选方案权衡表或直接查看各落地文件源码仓库级静态检查工具 scripts/code-verify.py 与提交前翻译重生成脚本 scripts/sanitize-commit.py 是这套工作流的验证基础设施。【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/11/7 23:51:06

火电机组协调控制Simulink高保真建模与工程落地

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

2026/11/7 5:23:46

2025毕业生必看:十大论文降重工具深度评测与实战技巧

1. 论文降重工具的核心价值与选择逻辑2025届毕业生正面临史上最严格的学术规范审查。某985高校研究生院最新数据显示,2023年答辩季因重复率超标被暂缓授予学位的案例同比激增47%。在这样的背景下,选择有效的降重工具已成为学术写作的刚需。我作为经历过三…

2026/11/10 12:13:58

电工证到哪里去报名多久拿证才靠谱

电工证到哪里去报名多久拿证才靠谱 你是不是也遇到过这种情况,网上一搜“电工证到哪里去”,全是各种培训机构的广告,看得人眼花缭乱,生怕一不小心被坑了钱还拿不到证?其实,想考电工证, 电工证到哪里去…

2026/11/10 12:13:58

肇庆中级电工证在哪考多久拿证实操考试心里没底怕挂科

肇庆中级电工证在哪考多久拿证实操考试心里没底怕挂科 实操考试心里没底怕挂科,是很多准备考肇庆中级电工证的朋友最真实的感受。尤其是在面对高压、高风险的电工作业时,很多考生对考试流程、评分标准、备考技巧都缺乏清晰的认识,导致在准备阶段就焦虑不安…

2026/11/10 12:13:58

电工证怎样查询被盗用 郑州报考避坑指南

电工证怎样查询被盗用 郑州报考避坑指南 你还在为找不到工作发愁?特种作业证是不少岗位的硬性门槛,尤其像电工证,几乎是建筑、电力、制造行业的“入场券”。但你有没有遇到过这种情况——辛辛苦苦考下来的电工证,突然发现被人盗用?这不仅影响你求职,还…

2026/11/10 12:13:58

坎墩哪里考电工证真实经历告诉你怎么考最稳妥

坎墩哪里考电工证真实经历告诉你怎么考最稳妥 你是不是也和我一样,学历不高,担心自己报不了名?去年我带着几个工友一起去考电工证,很多人就因为学历问题被拦在门外,结果耽误了项目进度。今天我就从 信阳劳务班组负责人 的角度,分享一下我 真实经历…

2026/11/10 12:08:57

黄江考电工证哪里报名多少钱才能上岗找工作

黄江考电工证哪里报名多少钱才能上岗找工作 你是不是正准备找电工相关的工作,却发现手里没有特种作业操作证?在黄江地区,考电工证哪里报名、多少钱、流程怎么走,成了很多人最关心的问题。别急,这文章就帮你从头理清楚,确保你一次拿证、顺利上岗。…

2026/11/10 8:12:38

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/10 0:01:28

学历不高怕报不上名?考电工证报名费+网上查询全攻略来了

学历不高怕报不上名?考电工证报名费+网上查询全攻略来了 你是不是也担心自己学历不高,考电工证报名费太贵,根本报不上名?别急,今天就来给你讲讲,怎么用最少的钱,把证拿到手,还能在工地上多挣点钱。 电工证报名费多少钱?网上查询是关键…

2026/11/10 0:01:29

绍兴考电工证培训机构怎么选?2026河南最新政策别错过

绍兴考电工证培训机构怎么选?2026河南最新政策别错过 怕考不过白交培训费?你不是一个人。2026河南最新政策出台,对电工证考试、报名、培训、复审都有明确规定。选择绍兴考电工证培训机构,别再踩坑,下面一步步教你避坑。…

2026/11/9 6:30:27

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/10 10:29:37

SSM电子书店源码全解析:结构、原理、部署与排错实战

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

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑