简介本资源是面向Access数据库初学者的VBA编程入门指南系统讲解如何利用Visual Basic for Applications实现数据自动化处理、窗体交互增强与用户界面定制解决办公场景中重复操作多、手工处理效率低等实际问题。全书以零基础为起点覆盖VBA编辑器环境搭建、变量与流程控制语法、Access对象模型与事件驱动机制、控件属性操作、Sub/Function过程编写、On Error错误处理、Recordset记录集操作、SQL语句嵌入调用、动态表单设计及调试技巧等12个核心模块内容结构清晰、实例导向明确。资源为单文件PDF格式共1个文件大小1.76MB轻量易读适合作为随身学习手册或课前预习材料。已有382人下载学习作者Bingo_Excel同步提供Excel函数、PowerBI、Python、MySQL等系列课程链接便于拓展Office自动化与数据分析技术栈。1. Access VBA不是Excel VBA的“简化版”而是数据库上下文里的一套独立运行时契约你写完一个Excel VBA宏双击按钮就能跑但把同样逻辑的代码原封不动挪到Access窗体里——大概率报错Object required、Invalid use of Null、甚至根本触发不了事件。这不是你手抖漏了句点而是Access VBA从底层就拒绝“照搬”。它不运行在工作表单元格的松散沙盒里而嵌在Jet/ACE数据库引擎的事务边界内所有对象Form、Report、Recordset都绑定着隐式连接、游标状态和行级锁语义。一个Me!txtName.Value 张三背后Access不是简单赋值而是先校验字段约束是否允许Null长度是否超限是否触发BeforeUpdate事件再检查当前记录是否处于可编辑状态Me.Dirty为True最后才走ACE引擎的缓冲区写入路径。这解释了为什么孙兴华在视频中反复强调“Access窗体控件的.Value属性本质是Recordset当前行对应字段的实时镜像不是内存变量”——你改的不是UI是在动数据库的活页。初学者常以为“VBA语法一样就能通吃Office”结果在Access里连DoCmd.OpenForm都卡在参数WhereCondition的引号嵌套上翻车。这份《Access VBA教程初级篇.pdf》的价值正在于它没把VBA当通用脚本语言教而是用200页纸拆解Access如何用VBA语法糖封装数据库引擎的硬性契约。搞懂这个你写的就不是“能跑的代码”而是“不会崩的契约执行器”。2. 对象模型Access VBA的根对象不是Application而是CurrentDb()背后的ACE引擎实例Access VBA的对象模型表面看和Excel相似有Application、Forms、Reports集合但它的生命线锚定在数据库引擎层。当你调用CurrentDb()返回的不是抽象的“当前数据库对象”而是Jet/ACE引擎的一个活动会话句柄Session Handle它持有连接池、事务日志指针、以及所有打开的Recordset的游标管理器。这才是Access VBA真正的工作母机。2.1 CurrentDb() vs OpenDatabase()共享会话与独占会话的本质区别 ✅ 推荐使用CurrentDb()获取当前数据库会话 Dim db As DAO.Database Set db CurrentDb() 返回当前.mdb/.accdb文件的共享会话 后续所有Recordset操作都复用此会话的连接和缓存 ❌ 风险OpenDatabase()创建新会话绕过Access事务管理 Dim db2 As DAO.Database Set db2 DBEngine.OpenDatabase(C:\data.accdb) 新建独立会话 此会话不参与CurrentDb()的事务CommitTrans/rollback无效CurrentDb()返回的是Access UI层已建立的数据库会话它与窗体、报表、查询共享同一套连接状态和事务上下文。而OpenDatabase()创建的是DAO层的独立会话它有自己的缓存、自己的游标、自己的未提交变更队列。这意味着如果你用OpenDatabase()打开一个表并修改数据再用DoCmd.RunSQL UPDATE ...执行相同SQL这两处修改互不可见——因为它们在不同的会话隔离级别里。孙兴华在PDF第37页特别标注“除非你要操作外部数据库否则永远用CurrentDb()”。这是Access VBA最易被忽略的底层契约所有VBA代码默认运行在CurrentDb()会话的影子中脱离它等于自建黑匣子。2.2 Recordset不是数据快照而是带状态的游标控制器Access中的Recordset对象远比Excel Range复杂。它不是静态数据容器而是一个指向ACE引擎内部游标的活体指针Dim rs As DAO.Recordset Set rs CurrentDb().OpenRecordset(SELECT * FROM 客户表, dbOpenDynaset) dbOpenDynaset是关键它启用实时更新、支持书签、响应其他用户变更 现在rs.Bookmark指向第一行但注意 Debug.Print rs.AbsolutePosition 可能是-1因为游标尚未定位 rs.MoveFirst 必须显式移动否则AbsolutePosition无效 Debug.Print rs.AbsolutePosition 此时为0 修改当前行字段 rs.Edit rs!姓名 李四 rs.Update 此刻才真正写入ACE缓存并触发BeforeUpdate/AfterUpdate事件dbOpenDynaset模式下Recordset与底层表保持双向绑定其他用户修改同一行你调用rs.Requery会立即看到变更你修改后未Update其他用户查不到rs.Delete会直接标记该行删除位而非物理删除。这种设计让Access VBA天然支持多用户协作但也带来陷阱——比如rs.MoveNext遇到已删除行时抛出错误必须用On Error Resume Next捕获。PDF第52页用流程图说明Recordset的Edit→Update→Requery三步闭环本质是向ACE引擎提交一个“行级事务段”而非内存操作。2.3 Form对象UI控件与Recordset的隐式桥接协议Access窗体Form的魔力在于它自动完成UI与数据的双向绑定但这背后是严格的协议 窗体加载时Access自动执行你无需写 Me.RecordSource 客户表 绑定数据源 Me.AllowEdits True 启用编辑 Me.Controls(txt姓名).ControlSource 姓名 将文本框绑定到字段 此时你对txt姓名的输入实时反映在Recordset当前行 Debug.Print Me.Recordset!姓名 输出与txt姓名内容一致 Debug.Print Me!txt姓名.Value 输出与Recordset字段一致 但注意如果手动修改Recordset Me.Recordset.Edit Me.Recordset!姓名 王五 Me.Recordset.Update txt姓名控件会自动刷新因为Form监听了Recordset的FieldChange事件这种自动同步不是魔法而是Access在窗体对象内部维护了一个“控件-字段映射表”并注册了Recordset的FieldChange、Update等事件回调。一旦你绕过这个协议比如用Me.txt姓名 王五直接赋值而不走Recordset就会破坏一致性——下次Requery时控件显示旧值而数据库已是新值。PDF第68页用对比表格强调“窗体控件的.Value属性本质是Recordset.Fields(控件.ControlSource).Value的代理而非独立变量”。3. 事件驱动Access VBA的事件链不是线性触发而是分阶段、可拦截的数据库事务钩子Access窗体事件看似和Excel类似Click、Load、Unload但它的执行时机深度耦合数据库事务生命周期。一个CommandButton_Click事件实际触发的是跨越UI层、窗体层、Recordset层、引擎层的四级钩子链每一级都可被拦截或取消。3.1 BeforeUpdate/AfterUpdate行级事务的守门人与见证者Private Sub 客户名称_BeforeUpdate(Cancel As Integer) 此事件在用户离开该控件、且数据将要写入Recordset前触发 Cancel1可阻止写入实现业务校验 If Len(Me!客户名称) 2 Then MsgBox 客户名称至少2个字 Cancel 1 ⚠️ 关键阻止后续所有更新 Exit Sub End If 注意此处不能调用Me.DirtyTrue因为Recordset尚未接受变更 End Sub Private Sub 客户名称_AfterUpdate() 此事件在Recordset.Update成功后触发此时数据已落库内存缓存 可安全执行关联操作如更新日志表 CurrentDb.Execute INSERT INTO 操作日志(操作,时间) VALUES(修改客户,Now()) 但注意此时若发生错误无法回滚Recordset.Update 因为AfterUpdate发生在事务提交之后 End SubBeforeUpdate是事务的“守门人”它运行在Recordset的Edit→Update流程中Cancel1会中断整个更新链连AfterUpdate都不会触发。而AfterUpdate是“见证者”它确认变更已进入ACE引擎缓存但此时事务尚未提交到磁盘dbEngine.CommitTrans才真正落盘。PDF第89页用时序图揭示BeforeUpdate→Recordset.Update→AfterUpdate→Form_Current构成完整行变更周期其中BeforeUpdate是唯一能阻止持久化的环节。3.2 Form_Current不是“切换记录”而是游标重定位的信号Private Sub Form_Current() 当前记录切换时触发包括MoveFirst/MoveNext/Find等所有游标移动 但注意此事件在Recordset游标定位完成后触发此时Me!字段已更新 常见误用在此事件中修改当前记录字段 Me!状态 已查看 ❌ 危险可能触发无限递归 Me.Dirty True 强制标记为脏但会再次触发BeforeUpdate 正确做法用临时变量暂存状态避免循环 Static lastID As Long If Me!ID lastID Then lastID Me!ID Me!状态 已查看 Me.Dirty True End If End SubForm_Current事件名极具误导性——它并非“当前记录”概念的被动通知而是游标重定位完成后的主动信号。每次MoveNext、FindFirst、甚至用户滚动记录导航条都会触发它。问题在于如果你在Form_Current里修改字段并设Me.DirtyTrue会立即触发BeforeUpdate而BeforeUpdate里若再调用MoveNext又回到Form_Current……形成死循环。PDF第95页给出血泪经验“Form_Current只做读取和UI状态同步所有写操作必须加防重入锁如Static变量标记”。3.3 Unload vs Close窗体销毁的两种语义决定事务是否回滚Private Sub Form_Unload(Cancel As Integer) 窗体即将销毁前触发此时Recordset仍有效 可用于保存临时状态但不能阻止销毁 If Me.Dirty Then 用户有未保存修改询问是否放弃 If MsgBox(有未保存修改确定要关闭, vbYesNo) vbNo Then Cancel 1 ⚠️ 关键Cancel1可取消Unload窗体不销毁 End If End If End Sub Private Sub Form_Close() 窗体已销毁Recordset可能已被释放 此处只能做清理不能访问Me!字段 Set g_rsTemp Nothing 清理全局Recordset引用 End SubUnload是销毁前的“最后通牒”Cancel1能强行挽留窗体而Close是销毁后的“葬礼致辞”此时窗体对象已析构Me不再有效。更关键的是事务语义Unload中若Cancel1当前Recordset的未提交变更Me.DirtyTrue依然存在但若用户点击关闭按钮导致Unload未被取消则Access会自动调用Recordset.CancelUpdate回滚所有未提交变更。PDF第102页用加粗警告“永远在Unload中处理未保存提示Close里只剩灰烬”。4. 避坑Access VBA开发中5个高频翻车现场及根因修复Access VBA的坑往往藏在“看起来应该能跑”的代码里。以下是基于PDF实战案例和社区高频问题提炼的5个真实踩坑记录每条都按“现象→原因→解决”结构还原4.1 现象DoCmd.OpenForm打开窗体后Me.RecordsetClone返回空记录集原因窗体RecordSource为SQL字符串如SELECT * FROM 表 WHERE ID Me!ID但RecordsetClone只克隆原始表/查询不解析SQL中的参数。Me!ID在窗体加载时尚未初始化导致SQL语法错误RecordsetClone静默失败。解决改用Me.Recordset它已绑定正确数据或确保RecordSource是静态对象名如客户表动态条件用WhereCondition参数传入DoCmd.OpenForm 客户窗体, , , ID Me!ID ✅ WhereCondition由Access解析4.2 现象DLookup(姓名, 客户表, ID Me!ID)在子窗体中返回Null原因DLookup在子窗体上下文中执行时Me!ID引用的是子窗体自身的ID控件而非主窗体的ID。子窗体ID可能为空或类型不匹配如文本型ID用数字比较。解决显式指定主窗体引用并强制类型转换DLookup(姓名, 客户表, ID CLng(Forms!主窗体!ID)) ✅ 显式引用类型转换4.3 现象For Each ctl In Me.Controls遍历控件时ctl.ControlType acTextBox始终为False原因Controls集合包含所有控件标签、按钮、分页符等但acTextBox常量需引用AcControlType枚举若未引用DAO库或拼写错误如acTextbox少字母比较恒为False。解决添加DAO引用工具→引用→勾选“Microsoft DAO 3.6 Object Library”并用TypeName(ctl)调试For Each ctl In Me.Controls Debug.Print ctl.Name : TypeName(ctl) 查看真实类型名 If TypeName(ctl) TextBox Then ... ✅ 用字符串比较更可靠 Next4.4 现象Me.Recordset.FindFirst 姓名 Me!姓名 执行后NoMatch为True但数据明明存在原因FindFirst的搜索字符串中Me!姓名值含单引号如张三导致SQL语法错误张三变成两个字符串。Access不自动转义静默失败。解决用Replace函数双重转义单引号Dim searchStr As String searchStr Replace(Me!姓名, , ) 将替换为 Me.Recordset.FindFirst 姓名 searchStr 4.5 现象CurrentDb.Execute UPDATE 表 SET 字段?用参数化查询时报错“Too few parameters”原因Execute方法不支持ADO风格的?参数占位符它只接受字符串拼接或DAOQueryDef对象。直接拼接?会被当作未声明的参数。解决改用QueryDef对象实现安全参数化Dim qdf As DAO.QueryDef Set qdf CurrentDb.CreateQueryDef(, UPDATE 表 SET 字段? WHERE ID?) qdf.Parameters(0) 新值 qdf.Parameters(1) 123 qdf.Execute dbFailOnError5. SQL方言Access VBA中SQL不是标准SQL而是ACE引擎的编译指令集Access的SQL执行层不是通用解析器而是ACE数据库引擎的专用指令编译器。它把SQL语句翻译成底层B树索引操作、页面锁指令、日志写入序列。因此同样的SQL在MySQL或SQL Server能跑在Access里可能直接语法报错——这不是兼容性问题而是指令集差异。5.1 TOP N不是语法糖而是游标截断指令-- ✅ Access有效TOP 10 是ACE引擎的游标截断指令 SELECT TOP 10 * FROM 订单表 ORDER BY 日期 DESC -- ❌ MySQL有效Access报错Access不支持LIMIT SELECT * FROM 订单表 ORDER BY 日期 DESC LIMIT 10 -- ⚠️ 注意TOP 10 在Access中不保证稳定排序 -- 若ORDER BY字段有重复值每次执行结果顺序可能不同 -- 解决添加唯一字段到ORDER BY如ORDER BY 日期 DESC, ID DESCTOP N在Access中不是LIMIT的同义词而是ACE引擎的游标控制指令它告诉引擎“打开游标后只取前N行然后关闭游标”。这导致两个特性1TOP必须配合ORDER BY否则无意义2TOP作用于游标结果集而非物理表扫描。PDF第124页用执行计划图说明TOP 10指令插入在“索引扫描→排序→游标截断”流水线末端而LIMIT在MySQL中是“结果集切片”操作位置不同。5.2 IIF()不是函数而是条件表达式编译器 ✅ Access有效IIF是ACE引擎内置的三元表达式编译器 SELECT 姓名, IIF(年龄18,成人,未成年) AS 类别 FROM 客户表 ❌ 标准SQL无效CASE WHEN在Access中仅部分支持2007 SELECT 姓名, CASE WHEN 年龄18 THEN 成人 ELSE 未成年 END AS 类别 FROM 客户表 ⚠️ IIF嵌套深度限制Access最多支持50层嵌套超限报错 替代方案用VBA函数封装复杂逻辑再在SQL中调用IIF(condition, true_part, false_part)在Access中不是函数调用而是SQL解析器的语法节点。它被直接编译成ACE引擎的条件跳转指令效率远高于CASE WHEN后者在旧版Access中需通过ODBC桥接。PDF第131页指出“IIF的true_part和false_part必须返回相同数据类型否则编译失败——这是编译期类型检查非运行时”。5.3 日期字面量不是字符串而是OLE日期常量-- ✅ Access有效#2023-01-01# 是OLE日期常量编译为8字节浮点数 SELECT * FROM 订单表 WHERE 日期 #2023-01-01# -- ❌ 标准SQL无效2023-01-01在Access中被当作文本类型不匹配 SELECT * FROM 订单表 WHERE 日期 2023-01-01 -- ⚠️ 区域格式陷阱#01/01/2023# 在中文系统解析为2023年1月1日 -- 但在英文系统可能解析为2023年1月1日相同但#01-01-2023#在英文系统是1月1日中文系统是1月1日无歧义 -- 最佳实践始终用#YYYY-MM-DD#格式如#2023-01-01#Access的#...#日期字面量是编译器识别的特殊标记它被直接转换为OLE Automation日期格式自1899年12月30日起的天数小数部分。而字符串2023-01-01需经CDate()函数转换若字段类型为Date隐式转换失败即报错。PDF第138页强调“所有日期比较必须用#包裹这是ACE引擎的硬性语法要求非约定俗成”。6. 设计取舍为什么Access VBA坚持用DAO而非ADO以及我如何重构遗留代码Access VBA选择DAOData Access Objects而非更通用的ADOActiveX Data Objects绝非技术落后而是对“办公数据库”场景的精准取舍。DAO直接封装Jet/ACE引擎的C接口零拷贝传递Recordset游标、原生支持.Bookmark、无缝集成窗体绑定——这些在ADO中需多层包装性能损耗达30%以上PDF附录B实测数据。但代价是DAO只适配Access跨数据库迁移成本高。当某公司从Access迁移到SQL Server时我接手的3000行VBA模块核心矛盾不是语法转换而是DAO的“会话即事务”模型与SQL Server的显式连接池冲突。我重构时做了三件事抽象数据访问层用Class Module封装DAO操作对外提供GetRecordset(sql)、ExecuteSQL(sql)接口内部根据CurrentProject.Connection判断引擎类型重写事件链将BeforeUpdate校验逻辑抽离为独立函数用CallByName动态调用避免硬编码Me!字段SQL方言适配器用正则替换TOP \d为ROWNUM NOracle或LIMIT NPostgreSQL#date#转为date::date。最关键的教训是Access VBA的“简单”是假象它用语法糖掩盖了数据库引擎的复杂性而真正的简单是承认这种复杂性并设计隔离层。现在我每次新建Access项目第一件事就是创建clsDBHelper类强制所有SQL走ExecuteSQL方法——哪怕只是SELECT 1。这看似多此一举但它让未来任何迁移都只需改一个类而非grep全项目。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
单链表双指针技巧:高效定位倒数第k个节点 1. 项目概述这道2009年408考研真题考察的是单链表操作与双指针技巧的结合应用,属于数据结构中的经典题型。题目要求设计一个算法,在单链表结构中高效定位特定位置的节点。这类问题在实际开发中非常常见,比如音乐播放器的"最近播放"… · 2026/11/3 11:38:18
Vercel部署RSSHub实战:自建RSS订阅服务,摆脱算法推荐 1. 为什么我决定自己部署一个RSSHub 信息获取这件事,这几年变得越来越割裂。我每天要看的几个独立博客、两个视频频道的更新、一个技术论坛的精华帖、还有几个垂直站点的专栏文章,分散在五六个不同的入口里。平台算法推什么我就看什么,久而久… · 2026/11/3 11:38:18
SINAMICS DCM 6RA80固件升级全流程避坑指南 简介:本资源是一份面向工业自动化领域技术人员与维护工程师的SINAMICS DCM(6RA80)驱动系统升级实操指南,聚焦低版本(如CV1.1)向高版本(如V1.2及以上)的完整固件与软件升级流程&#… · 2026/11/3 11:38:18
强制门户技术详解:原理、实现与优化 1. 强制门户技术概述强制门户(Captive Portal)是一种常见的网络访问控制技术,广泛应用于酒店、机场、咖啡馆等公共场所的Wi-Fi网络。当用户首次连接网络时,会被自动重定向到一个特定的网页,要求完成某种形式的认证或同… · 2026/11/3 16:09:19
Python中栈与队列的高效实现与性能优化 1. 栈与队列:Python中的高效实现之道 作为一名长期使用Python进行算法开发的工程师,我经常需要处理各种数据结构的选择问题。栈和队列作为最基础的两种线性数据结构,它们的实现方式直接影响程序性能。今天我就来分享一下在Python中实现栈和队… · 2026/11/3 16:09:19
AD9834 DDS芯片详解:高频低噪数字信号发生器核心 简介:本资源是一套基于AD9834芯片的高速DDS信号源发生器模块完整硬件设计资料,面向嵌入式硬件工程师、电子竞赛学生及高频信号发生器开发人员,解决高频可编程波形生成模块的原理理解、电路实现与PCB工程落地难题。资源包含1个741KB PDF文件&a… · 2026/11/3 16:09:19
大模型推理为何疯狂写SSD?一天256GiB的元凶与优化指南 Strix Halo这台小主机跑 qwen3.8 flash next,一天下来直接把我 SSD 写掉 256GiB——这是我玩本地大模型以来踩过最深的坑,没有之一。最开始我还不信,以为 smartctl 的数据有问题,反复核对了两次,才确认就是昨天一天之内… · 2026/11/3 16:09:19
平头哥开放RISC-V底层软件栈:从650家客户考验到生态突围 芯片行业里有一个很“魔幻”的段子:三年前你跟人聊RISC-V,对方多半会礼貌点头,然后转头继续看ARM的选型表;今天你再聊RISC-V,问的问题已经变成“你们工具链行不行”“Linux跑得利索不利索”“AI算子库支持到哪一步了”… · 2026/11/3 16:09:19
AI编程时代,为什么扎实编程基础仍是不可替代的核心能力 1. 这不是“替代”,而是“新编程范式”的入场券最近在技术圈刷屏的那句“Elvis Saravia:用 Claude Code 不等于不用学编程”,我第一次看到时就在咖啡机旁停了三秒——不是因为话多,而是因为它精准戳破了一个正在快速膨胀的认知泡沫… · 2026/11/3 16:08:18
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46