iApp轻量PHP后台源码:12文件可运行服务端骨架
简介这是一套面向移动应用开发者与iApp初学者的全开源后台管理系统源码聚焦于iApp生态下的服务端逻辑实现与前后端协同开发场景。资源包含完整的PHP后端架构、配套iApp前端页面及基础接口层适用于快速搭建轻量级移动应用后台、学习服务端数据交互原理或开展二次开发实践。压缩包共419个文件主体为278个PHP脚本涵盖用户登录、支付对接、IP校验、随机生成、祝福语管理等核心模块辅以49个iApp页面文件、37个PNG图标资源及HTML入口页如register.html、login.html、wxpay.html等整体体积仅5.27MB结构紧凑、即下即用。目前已有217人学习下载读者可直接获取完整目录结构、标准化接口定义、常见业务模块如API对接、IP风控、支付跳转、合作接入的可运行示例代码以及配套的JS交互逻辑与静态资源组织方式是理解iAppPHP轻量后台架构的实用入门材料。1. iApp后台带PHP文件源码全开源不是“套壳网页”而是可落地的轻量级移动应用服务端骨架你手头有个iApp前端项目界面搭好了按钮点下去却连不上后端——调试控制台满屏500 Internal Server ErrorPOST /api/login返回空白页config.php里数据库密码改了三遍还是连不上。这不是前端问题是缺一个真正能跑起来、能改、能查、能扩的PHP服务端基座。这份「iApp后台带PHP文件源码全开源」资源就是为这类场景准备的它不是WordPress或ThinkPHP那种重型框架打包而是一套精简到12个核心PHP文件含index.php、api.php、db.php、auth.php等 1个SQL建表语句 完整目录结构的轻量服务端实现。它默认适配iApp标准HTTP请求格式如datajsonPOST体、tokenHeader校验支持用户注册/登录/数据增删改查四类基础API且所有逻辑无混淆、无加密、无域名绑定限制。适合刚从Android原生转iApp开发的移动端同学快速联调也适合某高校课程设计中需要“前后端分离但又不想花三天搭Laravel环境”的学生直接填库名密码就跑通。它解决的不是“高并发”或“微服务治理”而是“今晚十点前让iApp能真正读到数据库里的用户列表”这个具体、急迫、反复踩坑的问题。2. 搭建与运行从零部署到首条API返回成功2.1 环境准备PHP版本、扩展与Web服务器选型依据iApp后台PHP源码对运行环境有明确边界最低要求PHP 7.2推荐PHP 8.0必须启用mysqli和json扩展不依赖pdo_mysql或opcache。为什么因为源码中数据库操作全部基于原生mysqli_connect()封装db.php里没有一句PDO语法所有接口响应强制header(Content-Type: application/json; charsetutf-8)json_encode()调用频次高PHP 7.2以下版本在中文字段序列化时易出现乱码或null。常见误判是看到“PHP”就装XAMPP——XAMPP默认带pdo但可能禁用mysqli小皮面板phpstudy则需手动勾选mysqli扩展。Nginx用户注意.htaccess重写规则不存在该源码不依赖Apache模块Nginx需配置try_files $uri $uri/ /index.php?$query_string;以支持iApp的RESTful路径如/api/user/123。我一般会用php -v php -m | grep -E mysqli|json两行命令快速验证比打开phpinfo页面快得多。2.2 目录结构解析与关键文件职责划分源码解压后呈现极简结构共1个根目录4个子目录无vendor或node_modulesiapp-backend/ ├── index.php # 入口文件路由分发中枢根据REQUEST_URI匹配/api/前缀并include对应模块 ├── api.php # API统一入口接收所有/api/*请求校验token后调用具体控制器 ├── db.php # 数据库单例封装含connect()、query()、fetch()三方法屏蔽底层连接细节 ├── auth.php # 认证核心含login()生成token、checkToken()验证时效与签名、getUserIdByToken()提取UID ├── config.php # 配置中心数据库地址/名/用户/密码、JWT密钥、token过期时间单位秒 ├── sql/ │ └── init.sql # 建表语句users表id, username, password_hash, token, created_at、logs表id, action, ip, created_at ├── controllers/ │ ├── user.php # 用户控制器handleRegister()、handleLogin()、handleGetProfile()等业务逻辑 │ └── data.php # 数据控制器handleList()、handleAdd()、handleUpdate()、handleDelete()对应CRUD └── utils/ └── helper.php # 工具函数sanitizeInput()过滤XSS、generateToken()生成JWT、response()统一封装JSON输出重点看api.php它不直接处理业务而是做三件事——① 从Header或POST中提取Authorization: Bearer xxx② 调用auth.php::checkToken()验证③ 根据$_SERVER[PATH_INFO]如/user/list拼接controllers/user.php路径并include执行。这种设计让新增API只需在controllers下建新PHP文件在api.php里加一条路由映射无需动核心分发逻辑。2.3 三步完成本地部署与首条API验证提示所有操作均在iapp-backend根目录下执行不要进入controllers或sql子目录第一步初始化数据库# 1. 登录MySQL假设用户名root密码123456 mysql -u root -p123456 # 2. 创建数据库编码必须为utf8mb4否则中文存入变问号 CREATE DATABASE iapp_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 3. 退出MySQL导入建表语句 exit mysql -u root -p123456 iapp_db sql/init.sqlinit.sql中users表的password_hash字段为VARCHAR(255)token字段为VARCHAR(512)这是为兼容JWT长token预留的空间若你计划用短token可缩至128位。第二步配置数据库与密钥编辑config.php修改以下四行其他保持默认?php define(DB_HOST, localhost); // 若MySQL在Docker中改为127.0.0.1或容器名 define(DB_NAME, iapp_db); define(DB_USER, root); define(DB_PASS, 123456); define(JWT_SECRET, your_32_byte_secret_key_here); // 必须32字节可用openssl rand -base64 32生成 define(TOKEN_EXPIRE, 3600); // token有效期3600秒1小时按需调整JWT_SECRET是安全关键若用弱密钥如123攻击者可伪造token若长度不足32字节hash_hmac(sha256, $payload, $secret)会报错。我习惯用php -r echo base64_encode(random_bytes(32));生成。第三步启动服务并测试登录API# 使用PHP内置服务器PHP 5.4支持端口8000 php -S localhost:8000 -t . # 或用Nginx/Apache确保DocumentRoot指向iapp-backend根目录新开终端用curl测试curl -X POST http://localhost:8000/api/login \ -H Content-Type: application/json \ -d {username:test,password:123456}预期返回{code:200,message:success,data:{token:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...}}若返回{code:500,message:database connection failed}说明db.php中mysqli_connect()失败请检查config.php数据库凭证及MySQL服务是否运行。3. 接口调用规范与iApp前端对接实操3.1 iApp标准请求格式与PHP端解析逻辑iApp前端发送请求时默认将整个请求体作为JSON字符串提交且不自动设置Content-Type: application/json。这意味着PHP端不能依赖$_POST它只解析application/x-www-form-urlencoded而必须读取原始输入流。源码中api.php第15行正是这样处理的// api.php 第15行 $input file_get_contents(php://input); $data json_decode($input, true); if (json_last_error() ! JSON_ERROR_NONE) { response(400, invalid json format); }file_get_contents(php://input)是关键它读取原始POST体json_decode($input, true)将其转为关联数组。因此iApp前端代码必须显式设置Header// iApp中JavaScript调用示例非jQuery fetch(http://localhost:8000/api/user/list, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer localStorage.getItem(token) }, body: JSON.stringify({page: 1, limit: 10}) })若忘记headers[Content-Type]PHP收到的是application/x-www-form-urlencoded格式$input为空字符串json_decode返回null触发400错误。这是新手最常翻车的点——以为iApp会自动设Header实际不会。3.2 四类核心API参数详解与返回结构所有API统一返回三段式JSON{code:200,message:success,data:{...}}code为HTTP状态码映射200成功400参数错401未授权500服务器错message为中文提示data为业务数据。各接口参数如下表API端点HTTP方法必需Header请求体参数JSON典型返回data字段特别说明/api/loginPOST无{username:str,password:str}{token:jwt_string}密码明文传输仅限调试上线必须前端加盐哈希或HTTPS/api/user/listPOSTAuthorization: Bearer xxx{page:1,limit:10}{list:[...],total:100}list为用户数组每项含id,username不含password_hash/api/data/addPOSTAuthorization: Bearer xxx{title:str,content:str}{id:123}插入成功返回新记录ID用于前端跳转/api/user/profilePOSTAuthorization: Bearer xxx无body{id:1,username:test}从token中解析出UID查库返回对应用户信息注意/api/user/profile无请求体iApp前端调用时body可传空对象{}或省略但api.php中$data json_decode($input, true)仍会返回[]控制器需兼容空数组。3.3 Token认证全流程与前端存储策略Token由auth.php::login()生成流程为查询users表比对username和password_hash使用password_verify()若匹配生成JWT payload[uid$row[id], exptime()3600]用JWT_SECRET签名返回完整token字符串。前端必须将token存入持久化位置。iApp中推荐用localStorage.setItem(token, response.data.token)而非sessionStorage关闭页面即丢失。但要注意iApp的WebView默认不支持localStorage跨域若你的iApp包域名是https://app.example.com而后端是http://localhost:8000则localStorage写入失败。解决方案是后端开启CORS在api.php顶部加header(Access-Control-Allow-Origin: *);或前端改用plus.storage.setItem(token, token)5 SDK提供。4. 避坑指南五条血泪经验换来的排错清单4.1 现象{code:500,message:database connection failed}原因db.php中mysqli_connect()返回false但错误信息被符号抑制源码第12行$conn mysqli_connect(...)。掩盖了真实错误导致无法定位是密码错、端口错还是MySQL未启动。解决临时注释掉db.php第12行的改为$conn mysqli_connect(...)此时PHP会抛出Warning: mysqli_connect(): (HY000/1045): Access denied for user...直接暴露凭证问题。确认无误后再加回——这是生产环境合理做法但调试阶段必须放开。4.2 现象{code:401,message:token invalid}即使token刚从login接口拿到原因auth.php::checkToken()中JWT_SECRET与login()生成时使用的密钥不一致。常见于复制config.php时JWT_SECRET在login()函数内硬编码了一个值如abc123而checkToken()读取的是config.php定义的值两者不同步。解决全局搜索abc123或JWT_SECRET确保login()和checkToken()中hash_hmac()的第三个参数完全相同。建议统一用defined(JWT_SECRET) ? JWT_SECRET : fallback避免硬编码。4.3 现象中文字段返回null或乱码如name:???原因MySQL连接未设置字符集。mysqli_connect()建立连接后必须执行mysqli_set_charset($conn, utf8mb4)否则即使数据库和表是utf8mb4连接层仍用latin1。源码db.php第22行有此调用但若你修改了连接逻辑如加了重连机制可能遗漏。解决在db.php::connect()函数末尾return $conn;前添加mysqli_set_charset($conn, utf8mb4);。同时确认init.sql中建表语句含CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci。4.4 现象iApp调用/api/user/list返回空数组[]但MySQL中确有数据原因controllers/user.php中handleList()查询语句写成SELECT * FROM users WHERE status1但init.sql创建的users表并无status字段。源码默认表结构简单若你自行添加字段如status、is_deleted控制器未同步更新WHERE条件导致查询无结果。解决检查controllers/user.php中SQL语句确保字段名与sql/init.sql完全一致。新增字段后必须同步修改所有涉及该表的SELECT、INSERT、UPDATE语句。4.5 现象Nginx下访问/api/user/list返回404但/index.php可访问原因Nginx未配置PATH_INFO支持。iApp后台依赖$_SERVER[PATH_INFO]解析路由如/api/user/list中/user/list部分而Nginx默认不传递PATH_INFO$_SERVER[PATH_INFO]为空导致api.php无法匹配控制器。解决在Nginx server块中添加location /api/ { try_files $uri $uri/ /api.php?$query_string; } location ~ \.php$ { include fastcgi_params; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass 127.0.0.1:9000; # 或unix:/var/run/php/php8.0-fpm.sock }关键是fastcgi_param PATH_INFO $fastcgi_path_info;这一行它将路径信息注入PHP环境变量。5. 安全加固与生产环境适配技巧5.1 密码存储从明文校验到bcrypt哈希的平滑迁移源码中auth.php::login()直接用password_verify($password, $row[password_hash])这本身是安全的但前提是$row[password_hash]是password_hash()生成的bcrypt哈希。问题在于init.sql中users.password_hash字段为VARCHAR(255)而旧系统可能存的是MD5或明文。迁移步骤如下新增字段在users表中添加password_bcryptVARCHAR(255) DEFAULT NULL修改登录逻辑在auth.php::login()中先查password_bcrypt若为NULL再查password_hash兼容旧数据升级哈希登录成功后若password_bcrypt为空用password_hash($password, PASSWORD_DEFAULT)生成新哈希并更新数据库停用旧字段确认所有用户都完成一次登录后删除password_hash字段。这样既不影响现有用户又逐步迁移到现代哈希标准。切记不要用md5($password)或sha1($password)——它们已被证明不安全。5.2 CORS配置精准控制而非简单Access-Control-Allow-Origin: *开发时用*方便但生产环境必须限制来源。iApp打包后Android WebView加载的HTML文件路径为file:///android_asset/www/index.htmlOrigin为nulliOS WKWebView同理。此时Access-Control-Allow-Origin: *无效需改为// 在api.php顶部添加 $origin $_SERVER[HTTP_ORIGIN] ?? ; $allowed_origins [https://your-iapp-domain.com, file://]; if (in_array($origin, $allowed_origins) || $origin null) { header(Access-Control-Allow-Origin: $origin); } header(Access-Control-Allow-Methods: GET, POST, OPTIONS); header(Access-Control-Allow-Headers: Content-Type, Authorization);$origin null是关键它允许file协议的WebView通过CORS检查。若漏掉此项iOS上fetch会直接失败连Network面板都看不到请求。5.3 错误信息脱敏关闭display_errors并自定义500页面源码默认开启error_reporting(E_ALL)开发时有用但生产环境必须关闭否则mysqli_connect()失败会暴露MySQL地址和用户名。在config.php末尾添加// 生产环境务必开启 ini_set(display_errors, Off); ini_set(log_errors, On); ini_set(error_log, __DIR__ . /logs/error.log);同时创建logs/目录并赋予Web服务器写权限如chmod 755 logs。这样错误会写入日志前端只看到{code:500,message:server error}不泄露任何技术细节。5.4 日志审计为关键操作添加行为日志源码无日志功能但sql/init.sql中已建好logs表。在controllers/user.php的handleLogin()末尾添加// 记录登录日志 $ip $_SERVER[REMOTE_ADDR] ?? unknown; $log_sql INSERT INTO logs (action, ip, created_at) VALUES (login, ?, NOW()); $stmt mysqli_prepare($conn, $log_sql); mysqli_stmt_bind_param($stmt, s, $ip); mysqli_stmt_execute($stmt);同理在handleAdd()、handleDelete()等敏感操作后插入日志。日志表虽简单但能快速定位异常IP或高频操作是安全基线。从那以后我每次部署新环境都会强制走一遍curl -X POST /api/logincurl -X POST /api/user/list双验证并用tail -f logs/error.log盯着日志滚动——只要没ERROR才敢让前端同学接入。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

不止MySQL:认识9种宝藏数据库与选型指南

不止MySQL:认识9种宝藏数据库与选型指南

数据库这事,说起来挺有意思。我周围大部分人一提到数据库,第一反应就是MySQL,面试聊到存储方案也是开口闭口“我们用的MySQL”。不能说错,MySQL确实是应用最广的关系型数据库之一,但每次遇到有人把MySQL当成数据库世界…

2026/10/11 11:39:49 阅读更多 →
RAG检索增强生成实战:从原理到落地,解决大模型幻觉问题

RAG检索增强生成实战:从原理到落地,解决大模型幻觉问题

1. 为什么RAG值得你花时间搞明白大模型这东西,用过的都知道,它有个特别让人头疼的毛病——一本正经地胡说八道。你问它一个很具体的问题,它能给你编出一套听起来特别合理但完全经不起查证的答案。这个问题在业内叫“幻觉”,说白了…

2026/10/11 11:39:42 阅读更多 →
AI落地从知识库到Agent:FDE学习路径与项目交付实战指南

AI落地从知识库到Agent:FDE学习路径与项目交付实战指南

1. 从“会用模型”到“能交项目”:AI落地到底卡在哪一步很多开发者跟我聊的时候都会问同一个问题:模型API调得挺溜了,提示词也写得像模像样,可一旦要进企业做AI落地,心里就没底。不知道企业到底要什么,不知…

2026/10/11 7:18:26 阅读更多 →

最新新闻

影刀RPA新手教程:表单自动填写实战——批量录入与动态表单处理

影刀RPA新手教程:表单自动填写实战——批量录入与动态表单处理

影刀RPA新手教程:表单自动填写实战——批量录入与动态表单处理 表单填写是企业RPA最高频的场景。HR录员工信息、财务录报销单、运营录商品信息——全是表单。但表单填写不是"定位输入框→输入文字"这么简单,动态下拉菜单、日期选择器、文件上传…

2026/10/11 13:26:58 阅读更多 →
OpenClaw 接入 deepseek 的 Windows 安装全流程攻略:从 PowerShell 到 TaoToken 配置

OpenClaw 接入 deepseek 的 Windows 安装全流程攻略:从 PowerShell 到 TaoToken 配置

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

2026/10/11 13:26:58 阅读更多 →
AI日报|Anthropic把前沿模型带进工业防御 / Google给Agent独立身份 / 边缘决策模型压到3B

AI日报|Anthropic把前沿模型带进工业防御 / Google给Agent独立身份 / 边缘决策模型压到3B

AI技术解读 AI日报 2026-10-09 AI正在把漏洞发现与边缘决策推向生产现场,但修复动作必须经过验证、维护窗口、独立终态读回与可演练回滚。 AI日报OT安全企业Agent边缘计算RAG AI可以提速风险发现;生产变更仍需独立身份、人工验证、终态读回与回滚证据…

2026/10/11 13:26:58 阅读更多 →
SpringBoot3+Vue3开源资产管理系统:从Excel台账到全生命周期数字化

SpringBoot3+Vue3开源资产管理系统:从Excel台账到全生命周期数字化

1. Excel 表格里的账实错乱,到底乱在哪1.1 一张表管三年的真实崩溃现场做资产管理这件事,绝大多数公司都是从一张 Excel 开始的。刚开始还好,电脑、显示器、办公桌椅几十件,表格里按部门登记一下,领导要什么数据当场就…

2026/10/11 13:26:58 阅读更多 →
Kotlin日历API实战:LocalDate、协程与Android避坑指南

Kotlin日历API实战:LocalDate、协程与Android避坑指南

如果让我用一句话来概括 Kotlin 里的日历相关 API,我会说:它是 Java 旧日期 API 的“现代化平替”,也是协程时代处理时间逻辑的基石。你打开绝大多数 Kotlin 项目,只要涉及日期计算、事件提醒、打卡签到,绕不开三样东西…

2026/10/11 13:26:58 阅读更多 →
OpenClaw智能网页抓取Skill:从URL到结构化JSON的工程实践

OpenClaw智能网页抓取Skill:从URL到结构化JSON的工程实践

当我在OpenClaw 2026.3.13版本里把最后一个智能网页抓取Skill的测试用例跑通时,最大的感受不是“终于能抓了”,而是“这次抓得像个正经工具了”。这个Skill并不是简单封装一个requests加正则匹配,而是把智能体场景里最常遇到的网页读取、结构…

2026/10/11 13:25:57 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/10/11 0:00:27 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 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/11 10:45:37 阅读更多 →
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/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练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/10 10:38:42 阅读更多 →