1. 为什么我要自己动手写一个 MCP1.1 从一次崩溃的 Excel 处理经历说起上个月帮朋友处理一份销售数据三千多行需要按区域拆分、按品类汇总、再生成一份带图表的周报。我当时的做法很原始打开 Excel写几个公式手动筛选复制粘贴到新表再插入图表。整个过程花了将近两个小时中间还因为一次误操作把公式引用搞乱了不得不从头再来。那天晚上我就在想这种重复性极高、逻辑又相对固定的 Excel 处理任务能不能交给 AI 来做不是那种“帮我写个公式”的浅层辅助而是让 AI 真正理解我的意图自动完成读取、清洗、计算、输出这一整套流程。后来我接触到了 MCP 这个概念。MCP 全称是 Model Context Protocol翻译过来叫“模型上下文协议”。你可以把它理解成一座桥——桥的一头是 AI 大模型另一头是你的本地工具、数据源或者任何可执行的服务。有了这座桥AI 就不再只是一个“聊天机器人”而是能真正动手帮你干活的助手。市面上已经有不少现成的 MCP 工具比如 Playwright MCP 可以让 AI 操控浏览器Chrome DevTools MCP 能让 AI 调试网页还有各种数据库 MCP、文件系统 MCP。但 Excel 处理这个场景比较特殊——它既涉及文件读写又涉及数据计算还涉及格式控制现成的工具要么功能太泛要么不够灵活。于是我决定自己写一个。这篇文章就是记录我从零开始开发第一个 MCP 的完整过程。如果你也经常和 Excel 打交道或者想了解 MCP 到底怎么落地这篇内容应该能帮到你。1.2 这个 MCP 能做什么适合谁看先把这个 MCP 的能力边界说清楚。我给它取名叫ExcelMCP核心功能包括读取 Excel 文件自动识别表头和数据类型按指定列进行分组汇总、条件筛选、去重在同一列中统计包含特定关键词的数据并求和将处理结果写入新的 Excel 文件保留基本格式支持多表操作比如从多个 sheet 中提取数据合并适合阅读这篇文章的人有 Python 基础但没写过 MCP 的开发者经常处理 Excel 数据想用 AI 提效的职场人对 AI Agent 和工作流自动化感兴趣的技术爱好者想了解 MCP 协议实际怎么用的人如果你完全没写过 Python也不用慌。我会把每一步都拆开讲代码给全命令给全你跟着抄作业就行。2. MCP 的核心概念与整体设计思路2.1 MCP 到底是什么用生活化类比讲清楚很多人第一次听到 MCP 会懵——它是软件协议还是硬件协议其实都不是。MCP 是一个应用层协议专门用来规范 AI 模型和外部工具之间的通信方式。打个比方。你家里有一个很聪明的管家AI 模型他知道很多事情但你让他去开灯他做不到因为他没有手。MCP 就是给管家装的那双手——通过一套标准化的接口管家可以调用各种工具开灯、关窗、煮咖啡。技术一点说MCP 定义了一套 JSON-RPC 格式的通信规范。AI 模型作为客户端向 MCP Server 发送请求MCP Server 执行具体操作把结果返回给模型。整个过程对用户是透明的你只需要用自然语言说“帮我把这份 Excel 按区域汇总”AI 就会自动调用对应的 MCP 工具完成任务。MCP 的几个核心概念概念含义类比MCP Host运行 AI 模型的环境管家的脑子MCP Client发起请求的一方管家本人MCP Server提供工具服务的一方管家的手和工具Tool具体可执行的操作开灯、煮咖啡Resource可读取的数据源冰箱里的食材Prompt预定义的提示模板菜谱我这次要写的 ExcelMCP就是一个 MCP Server。它对外暴露几个 Tool比如read_excel、filter_data、group_sum、write_excelAI 模型根据需要自动调用。2.2 为什么选 Python 而不是其他语言MCP Server 可以用任何语言写官方提供了 Python、TypeScript、Java 等多种 SDK。我选 Python 的原因很实际第一Excel 处理生态成熟。openpyxl、pandas、xlrd、xlsxwriter这些库覆盖了几乎所有 Excel 操作场景社区活跃遇到问题容易找到答案。第二Python 的 MCP SDK 封装得好。官方mcp包提供了FastMCP类几行代码就能定义一个 Tool省去了大量样板代码。第三调试方便。Python 可以边写边跑配合print和日志排查问题比编译型语言快得多。第四和 AI 生态衔接顺畅。很多 AI 工具链、数据处理管道都是 Python 写的后续扩展成本低。当然如果你更熟悉 TypeScript用 Node.js 写也完全没问题。核心逻辑是一样的只是语法不同。2.3 整体架构设计三层结构我的 ExcelMCP 采用三层结构第一层协议层。基于mcp包实现 MCP Server负责和 AI 客户端通信。这一层不需要我写太多代码SDK 已经处理好了 JSON-RPC 的序列化和反序列化。第二层业务逻辑层。这是核心包含所有 Excel 处理函数。每个函数对应一个 Tool输入是参数输出是处理结果。这一层完全独立于 MCP即使不用 AI也可以直接调用。第三层数据层。负责实际的文件读写用pandas做数据处理用openpyxl做格式控制。这样分层的好处是业务逻辑和协议解耦方便单元测试数据层可以替换比如以后想支持 CSV 或数据库只需要改这一层。3. 环境准备与项目初始化3.1 Python 安装与环境配置如果你电脑上还没有 Python先去官网下载安装包。建议选 3.10 或以上版本因为 MCP SDK 要求 Python 3.10。安装时有一个关键选项勾选“Add Python to PATH”。这个选项如果不勾后面在命令行里敲python会提示找不到命令。我见过太多人卡在这一步。安装完成后打开终端验证python --version pip --version如果都能正常输出版本号说明安装成功。接下来配置虚拟环境。虚拟环境的作用是隔离项目依赖避免不同项目之间的包版本冲突。操作如下mkdir excel-mcp cd excel-mcp python -m venv venv激活虚拟环境Windowsvenv\Scripts\activatemacOS/Linuxsource venv/bin/activate激活后命令行前面会出现(venv)标识。3.2 安装核心依赖包在虚拟环境中安装以下包pip install mcp pandas openpyxl三个包的作用mcpMCP 协议 SDK提供 Server 和 Tool 的基础设施pandas数据处理核心库负责读取、筛选、分组、汇总openpyxlExcel 文件读写引擎pandas 底层调用它来操作.xlsx文件如果你还需要处理.xls老格式额外装一个xlrdpip install xlrd注意xlrd从 2.0 版本开始不再支持.xlsx只支持.xls。所以.xlsx文件还是靠openpyxl。3.3 项目目录结构我习惯把项目结构搭得清晰一点方便后续扩展excel-mcp/ ├── venv/ ├── server.py # MCP Server 入口 ├── excel_tools.py # Excel 处理业务逻辑 ├── requirements.txt # 依赖清单 └── test_data/ └── sample.xlsx # 测试数据server.py负责注册 Tool 和启动服务excel_tools.py放具体的处理函数。这样分开写代码可读性高也方便单独测试业务逻辑。生成requirements.txtpip freeze requirements.txt4. 核心功能实现从读取到写入的完整链路4.1 读取 Excel 并自动识别表头第一个 Tool 是read_excel。它的任务是读取指定 Excel 文件返回数据概览包括列名、行数、前几行数据。import pandas as pd def read_excel(file_path: str, sheet_name: str None) - dict: 读取 Excel 文件返回基本信息 try: df pd.read_excel(file_path, sheet_namesheet_name) result { columns: df.columns.tolist(), row_count: len(df), dtypes: {col: str(dtype) for col, dtype in df.dtypes.items()}, preview: df.head(5).to_dict(orientrecords) } return {status: success, data: result} except Exception as e: return {status: error, message: str(e)}这段代码有几个细节值得说sheet_nameNone时pandas 默认读取第一个 sheet。如果你想读取所有 sheet可以传sheet_nameNone但那样返回的是一个字典需要额外处理。df.head(5)只取前五行做预览避免返回数据量过大。MCP 的返回结果会传给 AI 模型如果一次返回几千行会消耗大量 token既慢又贵。dtypes返回每一列的数据类型。这个信息很重要因为 AI 需要知道哪些列是数字、哪些是文本才能正确选择汇总方式。4.2 条件筛选与关键词统计求和这是最实用的功能之一。热词里有人搜“excel同一列中统计含关键词对应数据求和”这个需求在销售分析、日志统计里非常常见。def filter_and_sum(file_path: str, keyword: str, text_column: str, value_column: str) - dict: 在 text_column 中查找包含 keyword 的行 对 value_column 求和 df pd.read_excel(file_path) mask df[text_column].astype(str).str.contains(keyword, naFalse) filtered df[mask] total filtered[value_column].sum() return { status: success, matched_rows: len(filtered), total_sum: float(total), details: filtered[[text_column, value_column]].head(10).to_dict(orientrecords) }关键点解释astype(str)是为了防止列里有非字符串类型导致报错。比如某列混了数字和文本直接调.str.contains()会出问题。naFalse表示遇到空值时当作不匹配处理而不是报错。这个参数在实际数据里非常必要因为真实 Excel 几乎不可能没有空单元格。float(total)是类型转换。pandas 的sum()返回的是numpy类型直接塞进 JSON 会序列化失败。转成 Python 原生float就没问题了。4.3 分组汇总与多表合并分组汇总是 Excel 里最常用的操作之一。比如按区域汇总销售额、按月份统计订单量。def group_sum(file_path: str, group_column: str, value_column: str, agg_func: str sum) - dict: 按 group_column 分组对 value_column 做聚合 agg_func 支持 sum/mean/count/max/min df pd.read_excel(file_path) agg_map { sum: sum, mean: mean, count: count, max: max, min: min } if agg_func not in agg_map: return {status: error, message: f不支持的聚合方式: {agg_func}} grouped df.groupby(group_column)[value_column].agg(agg_map[agg_func]) result grouped.reset_index() result.columns [group_column, f{value_column}_{agg_func}] return { status: success, group_count: len(result), data: result.to_dict(orientrecords) }多表合并的场景也很常见。比如一个文件里有“一月”“二月”“三月”三个 sheet需要合并成一张总表def merge_sheets(file_path: str, sheet_names: list) - dict: 合并多个 sheet 的数据 dfs [] for name in sheet_names: df pd.read_excel(file_path, sheet_namename) df[来源表] name dfs.append(df) merged pd.concat(dfs, ignore_indexTrue) return { status: success, total_rows: len(merged), columns: merged.columns.tolist() }ignore_indexTrue的作用是重置索引避免合并后索引重复。df[来源表] name是加一列标记数据来源方便后续追溯。4.4 写入 Excel 并保留格式处理完数据最后一步是写回 Excel。这里我用openpyxl做引擎因为它支持格式控制。def write_excel(data: list, output_path: str, sheet_name: str Result) - dict: 将数据写入 Excel 文件 try: df pd.DataFrame(data) with pd.ExcelWriter(output_path, engineopenpyxl) as writer: df.to_excel(writer, sheet_namesheet_name, indexFalse) worksheet writer.sheets[sheet_name] # 自动调整列宽 for column in worksheet.columns: max_length 0 column_letter column[0].column_letter for cell in column: if cell.value: max_length max(max_length, len(str(cell.value))) worksheet.column_dimensions[column_letter].width min(max_length 2, 50) return {status: success, output_path: output_path} except Exception as e: return {status: error, message: str(e)}自动调整列宽这段代码很实用。默认情况下Excel 列宽是固定的长文本会显示不全。这段逻辑遍历每一列找出最长的内容把列宽设为“最长内容长度 2”同时限制最大宽度为 50避免某一列特别长导致表格难看。5. 把业务逻辑包装成 MCP Server5.1 用 FastMCP 注册 Tool业务逻辑写好了接下来要把它暴露成 MCP Tool。官方 SDK 提供了FastMCP类用起来很简洁from mcp.server.fastmcp import FastMCP from excel_tools import read_excel, filter_and_sum, group_sum, write_excel mcp FastMCP(ExcelMCP) mcp.tool() def tool_read_excel(file_path: str, sheet_name: str None) - dict: 读取 Excel 文件返回列名、行数和前五行数据 return read_excel(file_path, sheet_name) mcp.tool() def tool_filter_and_sum(file_path: str, keyword: str, text_column: str, value_column: str) - dict: 在指定列中查找包含关键词的行并对目标列求和 return filter_and_sum(file_path, keyword, text_column, value_column) mcp.tool() def tool_group_sum(file_path: str, group_column: str, value_column: str, agg_func: str sum) - dict: 按指定列分组对目标列做聚合计算 return group_sum(file_path, group_column, value_column, agg_func) mcp.tool() def tool_write_excel(data: list, output_path: str, sheet_name: str Result) - dict: 将数据写入 Excel 文件自动调整列宽 return write_excel(data, output_path, sheet_name) if __name__ __main__: mcp.run()mcp.tool()装饰器是关键。它把普通 Python 函数注册成 MCP ToolSDK 会自动读取函数的类型注解和文档字符串生成对应的 JSON Schema。AI 模型就是根据这个 Schema 来理解每个 Tool 需要什么参数、能做什么事。文档字符串...非常重要它是 AI 选择 Tool 的主要依据。写的时候要简洁准确把功能、输入、输出说清楚。5.2 启动服务与连接测试启动 MCP Serverpython server.py默认情况下FastMCP 使用 stdio 传输方式也就是通过标准输入输出和客户端通信。这种方式适合本地集成比如在 IDE 或桌面 AI 助手里使用。如果你想用 HTTP 方式可以改成mcp.run(transportstreamable-http, host127.0.0.1, port8000)测试连接是否正常可以用 MCP 官方提供的 Inspector 工具npx modelcontextprotocol/inspector python server.pyInspector 会启动一个网页界面你可以在里面看到所有注册的 Tool手动传参数调用验证功能是否正常。这个工具在开发阶段非常有用强烈建议用起来。5.3 在 AI 客户端中配置使用以常见的桌面 AI 工具为例配置文件通常是一个 JSON 文件在里面加上{ mcpServers: { excel-mcp: { command: python, args: [/path/to/excel-mcp/server.py] } } }配置完成后重启客户端AI 就能识别到 ExcelMCP 提供的 Tool。之后你直接说“帮我读取 test_data/sample.xlsx按区域汇总销售额”AI 就会自动调用tool_read_excel和tool_group_sum完成任务。6. 实操中踩过的坑与排查技巧6.1 常见报错与解决方案速查表报错信息原因解决方法ModuleNotFoundError: No module named mcp没装 mcp 包或虚拟环境没激活激活 venv 后pip install mcpFileNotFoundError文件路径不对用绝对路径或确认工作目录JSON serialization error返回了 numpy 类型用float()、int()转换KeyError: xxx列名不存在先用read_excel确认列名openpyxl does not support .xls文件格式不匹配.xls用 xlrd.xlsx用 openpyxl中文乱码编码问题确保文件是 UTF-8或指定 encoding6.2 三个我实际踩过的坑第一个坑列名里有空格。有一次读取一个客户给的 Excel列名是“销售额 ”末尾有个空格我代码里写的是“销售额”结果一直报 KeyError。排查了半小时才发现。后来我养成了一个习惯读取后先print(df.columns.tolist())确认列名再写后续逻辑。第二个坑数字列被识别成文本。有些 Excel 里数字是文本格式存储的sum()会变成字符串拼接。解决方法是读取时加dtype参数强制指定类型或者用pd.to_numeric(df[col], errorscoerce)转换。第三个坑大文件内存溢出。有一次处理一个 50 万行的 Excel直接pd.read_excel把内存吃满了。后来改成read_excel(..., chunksize10000)分块读取或者先用openpyxl的只读模式过滤再加载。6.3 性能优化与安全建议性能方面几个实用建议读取大文件时指定usecols参数只加载需要的列用dtype参数预设类型避免 pandas 自动推断的开销写入时用xlsxwriter引擎比openpyxl快但不支持读取频繁操作同一文件时考虑先加载到内存处理完再统一写回安全方面MCP Server 能读写本地文件所以要特别注意限制可访问的目录范围不要让 AI 随意读取系统文件对输出路径做校验防止覆盖重要文件如果暴露成 HTTP 服务加上认证机制不要裸奔在公网提示开发阶段可以用print打日志但生产环境建议用logging模块把日志写到文件方便排查问题。7. 这个 MCP 还能怎么扩展7.1 接入更多数据源目前只支持 Excel但架构上数据层是独立的。你可以很容易地加上 CSV 支持def read_csv(file_path: str) - dict: df pd.read_csv(file_path, encodingutf-8) return {columns: df.columns.tolist(), row_count: len(df)}或者接入数据库用sqlalchemy做查询。核心思路是一样的读数据、处理、返回结果。7.2 结合工作流做自动化MCP 最大的价值在于和 AI 工作流结合。比如你可以搭一个这样的流程AI 收到一封带附件的邮件自动调用 ExcelMCP 读取附件按预设规则清洗和汇总生成报告并写回新文件把结果发给指定的人整个过程不需要人工干预。我试过用这种方式处理每周的销售周报原来要花一小时现在几分钟就搞定了。7.3 加入图表生成能力Excel 的图表功能很强但 pandas 本身不支持直接生成图表。可以结合xlsxwriter的图表功能import xlsxwriter workbook xlsxwriter.Workbook(output.xlsx) worksheet workbook.add_worksheet() chart workbook.add_chart({type: column}) chart.add_series({ categories: [Sheet1, 1, 0, 5, 0], values: [Sheet1, 1, 1, 5, 1] }) worksheet.insert_chart(D2, chart) workbook.close()这样 AI 就能根据数据自动生成柱状图、折线图、饼图直接输出一份完整的分析报告。7.4 错误处理与日志记录生产环境里错误处理和日志是必须的。我现在的做法是import logging logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(excel_mcp.log), logging.StreamHandler() ] ) logger logging.getLogger(__name__)每个 Tool 调用前后都打日志记录输入参数和返回结果。出问题时翻日志比在代码里到处加print高效得多。8. 一些个人体会写这个 MCP 的过程中我最大的感受是MCP 真正降低了 AI 落地的门槛。以前想让 AI 操作本地文件得自己写 API、处理认证、设计接口现在有了 MCP 协议只需要专注业务逻辑通信层 SDK 全包了。另一个体会是文档字符串比代码本身还重要。AI 是根据文档字符串来选择 Tool 的写得不清楚AI 就会选错工具或者传错参数。我一开始没重视这个结果 AI 经常把filter_and_sum和group_sum搞混。后来把文档写详细了准确率立刻上来了。最后分享一个小技巧开发 MCP 时先用 Inspector 把每个 Tool 单独测通再接入 AI 客户端。这样出问题时能快速定位是 Tool 本身的问题还是 AI 调用的问题。我一开始跳过这一步直接在客户端里测结果排查了半天才发现是参数类型不对。这个 ExcelMCP 目前还在持续迭代后面打算加上数据透视表、条件格式、多文件批量处理这些功能。如果你也在做类似的事情欢迎交流。
企业数字化 ERP 产品动态
相关推荐
AI工程化新信号:算力治理、Agent硬件与模型评测实战 1. 三条热搜背后的技术信号拆解今天这三条消息放在一起看,其实指向了同一个趋势:AI 正在从"能聊"往"能管"和"能干活"两个方向同时推进。安理会那场关于 AI "限速"的听证,讨论的是算力扩张的边界问题… · 2026/11/2 17:29:21
从Excel脚本到MCP Server:用Python让AI直接调用表格处理工具 1. 为什么我要自己写一个 MCP,而不是继续堆 Excel 脚本我做了七八年数据处理,Excel 一直是我手里最顺手的工具。但这两年有个很明显的感受:Excel 本身没变,变的是我们处理它的方式。以前写个 Python 脚本读 xlsx、做透视、再写回去… · 2026/11/2 17:29:21
LTX-Best-Face-ID提示词工程指南:如何编写让AI精准理解的身份描述 LTX-Best-Face-ID提示词工程指南:如何编写让AI精准理解的身份描述
LTX-Best-Face-ID是一款基于LTX-2.3模型的身份保留参考转视频LoRA工具,能够根据人物参考图片和文本提示生成保持身份特征的视频内容。本文将详细介绍如何通过提示词工程让AI精准理解身份… · 2026/11/3 9:19:10
SenseNova-U1-8B-MoT-LoRAs:革命性AI推理增强工具,解锁8B参数模型新能力 SenseNova-U1-8B-MoT-LoRAs:革命性AI推理增强工具,解锁8B参数模型新能力
SenseNova-U1-8B-MoT-LoRAs是一款革命性的AI推理增强工具,专为解锁8B参数模型的新能力而设计。它提供优化的LoRA(Low-Rank Adaptation)实现&am… · 2026/11/3 9:19:10
【人工智能基础05】决策树模型 /* 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 9:19:10
linux设备/驱动的注册 /* 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 9:19:10
Matrix-Game-3.0与Wan2.2-TI2V-5B对比:性能提升与技术创新详解 Matrix-Game-3.0与Wan2.2-TI2V-5B对比:性能提升与技术创新详解
在人工智能视频生成领域,Matrix-Game-3.0作为基于Wan2.2-TI2V-5B的升级版本,带来了显著的性能提升和技术创新。本文将深入解析这两个模型的核心差异,帮助新手和普通… · 2026/11/3 9:18:07
双极步进电机控制实战: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