PowerBuilder DataWindow 38个实战技巧:检索、更新、打印与性能优化
简介这份文档面向 PowerBuilder 开发者尤其是需要提升 DataWindow 实战能力的中级程序员系统整理了 38 个实用技巧覆盖数据展示、交互优化与性能调优等常见开发痛点。资源为单个 doc 文档压缩包约 49KB内容紧凑、便于随查随用。文档从列属性控制入手讲解如何让 DataWindow 列只能追加不能修改、实现列数据自动折行、利用内置定时器制作动画效果并针对选中行外观单调的问题给出单行与多行选择的背景色表达式方案。此外还涉及动态修改属性、条件格式化、隐藏列、自定义列宽、排序、计算字段、过滤数据、嵌套 DataWindow、分组汇总、打印导出、行状态判断、缓存优化与实时校验等技巧基本覆盖日常开发高频场景。已有 403 人学习适合希望快速查阅技巧、优化数据界面交互与提升应用质量的开发者参考。1. PB DataWindow 38个技巧非常实用的做 PowerBuilder 维护的同行大概都有同感一个跑了十几年的老系统界面土、逻辑绕但业务部门就是不让换因为里面沉淀了太多只有这套系统才跑得通的报表和录入逻辑。而撑起这些界面的核心控件几乎全是 DataWindow。很多人对它的印象还停留在“拖一个 dw 上去、配个 SQL 就能显示”可真到改需求的时候才发现同一张报表别人两小时收工你折腾两天还在跟检索参数较劲。差别不在语法而在对 DataWindow 那些“隐藏开关”的熟悉程度。这篇笔记把我在实际维护中反复用到的 38 个技巧按场景拆开讲覆盖数据检索、显示格式、更新控制、打印导出和性能排查新手能照着改熟手能对照检查自己漏了哪些参数。2. 数据检索与参数让 DataWindow 少跑冤枉路DataWindow 的性能问题八成出在检索环节。很多人习惯把 SQL 直接写进 dw 的 select 里运行时全量拉回来再在客户端过滤数据量小的时候看不出问题一旦表上了几十万行打开窗口就要等好几秒。这一章先把检索相关的技巧讲透后面再谈显示和更新。2.1 用 Retrieve 参数替代客户端过滤最常见的翻车场景窗口打开时dw_1.Retrieve()不带参数把整张表拉回来然后在代码里循环判断某列的值来决定显示哪些行。正确做法是把过滤条件下推到 SQL 的 where 子句通过 Retrieve 参数传入。// 在 DataWindow 画板中定义检索参数例如 as_dept_id // 脚本中调用时传入实际值 string ls_dept ls_dept sle_dept.text dw_1.SetTransObject(sqlca) dw_1.Retrieve(ls_dept)逻辑说明DataWindow 的 Retrieve 参数会替换 SQL 中对应的检索参数占位符数据库只返回匹配的行。参数说明SetTransObject必须在Retrieve之前调用否则事务对象为空会报数据库连接错误如果窗口用了共享事务对象注意SetTransObject和SetTrans的区别前者不自动提交后者会在检索后自动提交容易引发锁表。2.2 动态修改 SQL 的 Where 子句有些报表的查询条件是可选的用户勾了部门就按部门过滤没勾就查全部。这时候硬编码多个 dw 不现实可以用Modify动态改 where。string ls_where ls_where emp.dept_id :as_dept if sle_dept.text then ls_where 1 1 // 不加条件时用恒真表达式占位 end if dw_1.Modify(DataWindow.Table.Select ~SELECT emp_id, emp_name FROM employee WHERE ls_where ~) dw_1.Retrieve()逻辑说明Modify直接改 DataWindow 对象的 SQL 定义改完必须重新 Retrieve 才生效。参数说明字符串里的双引号要用~转义如果 where 里用了检索参数记得在 Modify 之后重新设置参数值否则会提示参数未赋值。2.3 检索行数限制与进度提示大表检索时用户不知道程序在干什么容易重复点击按钮。可以在检索前设置最大行数并配合进度条。dw_1.SetRedraw(false) // 检索期间关闭重绘减少闪烁 dw_1.Retrieve() dw_1.SetRedraw(true)逻辑说明SetRedraw(false)在批量操作时能明显提升响应速度但必须在操作完成后恢复为 true否则界面会一直不刷新。参数说明如果检索时间超过几秒建议在窗口上放一个静态文本显示“正在查询…”检索前 Visibletrue检索后 Visiblefalse。2.4 共享数据窗口减少重复查询同一个数据在多个 dw 中显示时不要每个都去查数据库。用ShareData让它们共享同一份结果集。dw_2.ShareData(dw_1) dw_2.ShareData(dw_1) // 再次调用可解除共享逻辑说明共享后两个 dw 的数据完全一致修改其中一个另一个也会变。参数说明共享要求两个 dw 的列结构完全一致否则运行时会报错解除共享用再次调用ShareData并传入原 dw 的方式具体行为取决于 PB 版本建议在共享前先确认业务是否真的需要联动。3. 显示格式与交互让报表看起来“专业”数据查出来了但显示得乱七八糟用户照样不买账。日期显示成 20240101、金额没有千分位、状态码显示 0 和 1这些细节直接影响使用体验。这一章讲显示格式和交互控制的技巧。3.1 用 DisplayFormat 统一日期和金额格式在 DataWindow 画板中选中列在 Format 标签页设置 DisplayFormat。日期常用yyyy-mm-dd金额常用#,##0.00。// 也可以在脚本中动态设置 dw_1.Modify(emp_salary.Format ~#,##0.00~)逻辑说明DisplayFormat 只影响显示不影响实际存储的值。参数说明如果列本身是 string 类型但存的是数字需要先用String函数转换再设置格式否则格式不生效。3.2 条件显示不同颜色根据某列的值改变文字颜色比如金额为负显示红色。// 在列的 Font 属性中设置表达式 // 表达式示例if(salary 0, rgb(255,0,0), rgb(0,0,0))逻辑说明DataWindow 的属性支持表达式运行时根据当前行的值动态计算。参数说明表达式里引用列名直接用列名即可不要加引号如果表达式复杂建议先在画板里测试避免运行时才发现语法错误。3.3 用 Code Table 替代硬编码下拉性别、状态这类固定选项不要在每个窗口里写 if-else用 Code Table 统一维护。// 在列的 Edit 风格中选择 DropDownListBox // 在 Code Table 中定义显示值和实际值 // 例如显示“男”对应值 M显示“女”对应值 F逻辑说明Code Table 把显示文本和实际存储值分离数据库里存 M/F界面上显示男女。参数说明Code Table 定义在 DataWindow 对象里如果多个 dw 共用同一套编码建议做成外部文件或数据库表避免改一处漏一处。3.4 行焦点与选中效果用户点击某一行时要有明显的视觉反馈否则不知道当前操作的是哪条记录。// 在 DataWindow 的 RowFocusChanged 事件中 dw_1.SelectRow(0, false) // 先清除所有选中 dw_1.SelectRow(currentrow, true) // 选中当前行逻辑说明SelectRow控制行的选中状态配合 dw 的选中背景色设置能实现整行高亮。参数说明currentrow是系统变量表示当前行号如果 dw 允许行内编辑注意选中行和编辑焦点的区别避免用户以为选中了就能直接改。4. 更新控制与并发别让多人同时改崩数据DataWindow 的更新机制是它最强大的地方也是最容易出问题的地方。默认情况下PB 会根据你设置的 Update 属性生成 insert/update/delete 语句但如果主键、更新列、并发检测没配对轻则更新失败重则覆盖别人的修改。4.1 正确设置 Update 属性在 DataWindow 画板的 Rows 菜单里选 Update Properties指定可更新表、主键列和可更新列。// 脚本中检查更新是否成功 if dw_1.Update() 1 then Commit; else Rollback; MessageBox(提示, 更新失败) end if逻辑说明Update()返回 1 表示成功-1 表示失败。参数说明如果 dw 涉及多表连接必须指定哪张表可更新否则 Update 会报错主键列必须勾选否则 PB 无法定位要更新的行。4.2 用 Update Where Clause 控制并发默认情况下PB 用主键作为 where 条件。如果两个人同时打开同一条记录A 改了保存B 再保存时会覆盖 A 的修改。可以在 Update Properties 里把 where 条件改成包含所有可更新列的原始值。// 在 Update Properties 中选择“Key and Modified Columns” // 或“Key and Updatable Columns”逻辑说明选择更严格的 where 条件后如果记录已被别人修改Update 会返回 0 行受影响PB 据此判断为并发冲突。参数说明这种方式会增加 where 子句的长度对性能有轻微影响但能有效防止覆盖如果业务允许后写覆盖用默认的主键条件即可。4.3 处理 Update 失败后的回滚更新失败时不能只弹个框就完事要把数据恢复到修改前的状态。if dw_1.Update() 1 then Commit; else Rollback; dw_1.Retrieve() // 重新检索丢弃未保存的修改 end if逻辑说明Rollback撤销数据库层面的事务Retrieve重新拉取数据把 dw 里的缓冲区也重置。参数说明如果用户输入了很多内容直接 Retrieve 会丢失建议先提示用户是否放弃修改更友好的做法是用dw_1.ResetUpdate()清除更新标志让用户继续编辑。4.4 批量更新时的提交策略循环更新多条记录时不要每改一条就 Commit 一次那样事务太碎性能差还容易锁表。// 循环修改数据 for ll_i 1 to dw_1.RowCount() dw_1.SetItem(ll_i, status, A) next // 统一更新 if dw_1.Update() 1 then Commit else Rollback逻辑说明所有修改先在客户端缓冲区完成最后一次性提交。参数说明如果数据量特别大比如上万行建议分批 Update每 500 行提交一次避免事务日志过大。5. 打印与导出把数据送到该去的地方DataWindow 的打印和导出功能很全但默认设置往往不符合实际需求。比如打印出来没有页眉页脚、导出 Excel 后格式全乱、PDF 里中文变方块这些问题都有对应的解法。5.1 用 Print 函数控制打印范围不要直接调Print()无参版本那样会打印全部数据。可以指定打印哪些页。dw_1.Print(false, 1, 3) // 打印第1到第3页逻辑说明第一个参数表示是否显示打印对话框后面两个参数是起始页和结束页。参数说明如果 dw 数据量很大建议先Preview()让用户确认再决定打印范围。5.2 导出 Excel 时保留格式用SaveAs导出 Excel 时默认只导出数据格式会丢。可以在导出前设置 dw 的导出属性。dw_1.SaveAs(c:\temp\report.xls, Excel!, true)逻辑说明第三个参数 true 表示包含列标题。参数说明Excel! 是 PB 内置的导出格式枚举不同版本可能支持 xlsx 需要额外配置如果导出后中文乱码检查系统区域设置和 PB 版本的兼容性。5.3 打印时自动分页与页眉页脚在 DataWindow 画板中设置页眉页脚区域放入页码、日期等表达式。// 在页眉中插入计算列 // 表达式Page() / PageCount()逻辑说明Page()返回当前页码PageCount()返回总页数。参数说明这两个函数只在打印预览和打印时有效在普通显示模式下返回 0如果页眉内容需要动态变化可以在 Print 前用 Modify 修改。5.4 导出 CSV 时的分隔符处理导出 CSV 时如果字段内容本身包含逗号会导致列错位。dw_1.SaveAs(c:\temp\data.csv, CSV!, true, ,)逻辑说明第四个参数指定分隔符PB 会自动对包含分隔符的字段加引号。参数说明如果目标系统要求用制表符分隔把最后一个参数改成~t导出前最好先检查数据里有没有换行符有的话需要额外处理。6. 避坑与排查那些年我们踩过的 DataWindow 坑这一章记录几个高频问题每个都按“现象 → 原因 → 解决”写方便对照排查。6.1 检索后数据不显示但 RowCount 有值现象dw_1.Retrieve()返回了行数但界面上看不到任何数据。原因dw 的 Visible 属性为 false或者列宽被设置为 0或者数据窗口的 Band 高度不够。解决检查 dw 的 Visible 属性检查每列的 Width 是否大于 0检查 Detail Band 的高度是否被意外拖成了 0。6.2 Update 返回 -1 但看不出哪里错现象dw_1.Update()返回 -1但错误信息很模糊。原因可能是主键未设置、可更新列未勾选、或者数据库约束冲突。解决在 Update 前调用dw_1.SetTransObject(sqlca)确保事务对象正确用dw_1.GetSQLPreview()查看实际生成的 SQL检查数据库端的触发器或约束是否拒绝了更新。6.3 共享数据窗口后修改不同步现象dw_2.ShareData(dw_1)后修改 dw_1 的数据dw_2 没变化。原因共享的是数据缓冲区但显示格式和排序是各自独立的。解决如果希望完全同步两个 dw 的对象结构必须完全一致如果只是数据同步共享后调用dw_2.SetRedraw(true)强制刷新。6.4 打印预览时中文显示为方块现象打印预览或导出 PDF 时中文变成方块或问号。原因打印机驱动或 PDF 虚拟打印机不支持当前字体。解决在 DataWindow 画板中把字体改成系统自带的中文字体如宋体或黑体如果导出 PDF检查 PDF 虚拟打印机的字体设置。6.5 动态 Modify 后 Retrieve 报参数错误现象用 Modify 改了 SQL 后Retrieve 提示“参数未赋值”。原因Modify 修改了 SQL 定义但检索参数列表没有同步更新。解决在 Modify 之后重新设置检索参数或者改用dw_1.SetSQLSelect()方法它会自动处理参数映射。7. 进阶技巧用 Describe 和 Modify 做动态报表前面讲的都是相对固定的用法真正让 DataWindow 灵活起来的是Describe和Modify这对组合。Describe读取对象的属性Modify修改属性两者配合可以在运行时动态调整报表的几乎任何部分。我一般用它们来做“用户自定义列”功能让用户勾选需要显示的列程序根据勾选结果动态设置列的 Visible 和 Width。// 读取当前列的可见性 string ls_visible ls_visible dw_1.Describe(emp_name.Visible) // 根据用户选择设置可见性 if cbx_name.checked then dw_1.Modify(emp_name.Visible 1) else dw_1.Modify(emp_name.Visible 0) end if逻辑说明Describe返回字符串形式的属性值Modify接受字符串形式的赋值语句。参数说明属性名大小写不敏感但列名必须和 dw 中定义的一致如果列名包含特殊字符需要用引号括起来。另一个实用技巧是用Describe获取 dw 的 SQL 语句方便调试。string ls_sql ls_sql dw_1.Describe(DataWindow.Table.Select) MessageBox(当前SQL, ls_sql)逻辑说明这能帮你确认运行时实际执行的 SQL 是什么尤其是在用了 Modify 动态改 SQL 之后。参数说明返回的 SQL 可能包含检索参数占位符实际执行时会被替换成具体值。还有一个我常用的技巧用Modify动态添加计算列。比如用户临时想按某个公式排序或显示不需要改 dw 对象直接在运行时加。dw_1.Modify(Create compute_1 as integer) dw_1.Modify(compute_1.Expression salary * 12)逻辑说明Create动态创建一个计算列Expression设置计算公式。参数说明计算列的类型要和表达式结果匹配创建后需要重新 Retrieve 或 SetRedraw 才能看到效果。这些技巧单独看都不复杂但组合起来能解决很多“看起来要改代码”的需求。我自己的习惯是遇到需要动态调整界面的需求先想能不能用 Describe/Modify 搞定实在不行再改 dw 对象。这样改出来的程序灵活用户也满意。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

U8G2中文字库裁剪与压缩:嵌入式点阵字库实战指南

U8G2中文字库裁剪与压缩:嵌入式点阵字库实战指南

简介:这份资源面向使用 Arduino 与 u8g2 库进行中文显示开发的嵌入式爱好者与工程师,解决 u8g2 默认字库不含中文字形、需要自行生成定制字体的实际问题。压缩包内包含 GUITool 字体取模工具与 u8g2-master 源码库两部分核心内容,前者用于将中…

2026/10/15 11:57:51 阅读更多 →
商业地产租赁合同关键条款拆解:租金、押金与装修期避坑指南

商业地产租赁合同关键条款拆解:租金、押金与装修期避坑指南

简介:房屋租赁合同书模板是一份面向出租方、承租方及法务行政人员的专业合同范本,适用于商场商铺、写字楼等房屋租赁场景。模板依据《中华人民共和国民法典》及相关法规拟定,对合同主体信息、租赁房屋详情、租赁用途、租赁期限、租金结算与支…

2026/10/15 11:57:51 阅读更多 →
PHPStan 2.0 升级全指南:从 1.x 平滑迁移的配置变更与扩展开发适配

PHPStan 2.0 升级全指南:从 1.x 平滑迁移的配置变更与扩展开发适配

开发工具后端 【免费下载链接】environment Provides functionality that helps writing PHP code that has runtime-specific execution paths 项目地址: https://gitcode.com/gh_mirrors/en/environment 点击查看 免费下载 PHPStan 2.0 是 PHP 静态分析工具的一次…

2026/10/15 11:57:51 阅读更多 →

最新新闻

LaTeX环境搭建:Texlive 2020与TeXstudio 4.5.3协同部署指南

LaTeX环境搭建:Texlive 2020与TeXstudio 4.5.3协同部署指南

1. 这不是“装个软件”那么简单:为什么LaTeX环境搭建值得花两小时认真对待你点开这个标题,大概率正被一篇论文、一份技术报告或毕业设计的排版问题卡在原地——Word里公式编号总对不齐,参考文献格式反复崩坏,图表跨页后标题消失&a…

2026/10/15 12:40:07 阅读更多 →
FM175XX + FreeRTOS 读卡工程详解:从移植避坑到业务链路落地

FM175XX + FreeRTOS 读卡工程详解:从移植避坑到业务链路落地

简介:面向嵌入式开发者的 APM32/STM32 RFID 读写方案,基于 FreeRTOS 集成 FM175XX 射频芯片,适合需要快速验证 TypeA 卡识别、评估识别距离优化效果的开发者。工程可直接编译,外接天线板采用双天线配置,已调通卡片 ID …

2026/10/15 12:40:07 阅读更多 →
系统辨识与建模40学时教案:从M序列设计到最小二乘的工程实践

系统辨识与建模40学时教案:从M序列设计到最小二乘的工程实践

简介:这份系统辨识教案面向自动化专业学生与相关工程技术人员,围绕从输入输出数据中建立系统模型这一核心问题,系统梳理辨识理论、方法与实验流程。内容涵盖实体与模型的概念区分、模型分类、Zadeh 辨识定义及数据、模型类、准则三要素&#…

2026/10/15 12:40:07 阅读更多 →
写博客坚持不下去?从知识复利到写作习惯的完整复盘

写博客坚持不下去?从知识复利到写作习惯的完整复盘

写了七八年的博客,中间无数次想放弃,又无数次因为某个读者的一条评论续了命。身边总有朋友问:现在短视频都把流量抢光了,谁还看长文?你怎么还能一篇篇写下去,而且没有烂尾?每次被问到这个问题&a…

2026/10/15 12:40:07 阅读更多 →
仓颉尝鲜第一步:VSCode 环境配置与 SDK 插件全流程

仓颉尝鲜第一步:VSCode 环境配置与 SDK 插件全流程

/* 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 12:40:07 阅读更多 →
CentOS 7 离线安装 PostgreSQL 9.5 完整实践指南

CentOS 7 离线安装 PostgreSQL 9.5 完整实践指南

1. 项目概述:为什么离线安装 PostgreSQL 9.5 在 CentOS 7 上仍值得深挖你手头有一台刚上架的生产服务器,物理隔离、无外网权限、防火墙策略严苛——这是某金融行业客户机房里最常见的“安全岛”环境;或者你正为某高校实验室的旧版教学集群做维…

2026/10/15 12:39:06 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战: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/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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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 阅读更多 →