SSM框架实战:从源码到部署,完整跑通大数据技术学习网站
做Java后端这些年我见过太多人卡在“会写代码、不会跑项目”这个阶段。尤其是手里拿到一个像“ssm大数据技术学习网0y331”这样的完整项目时光看标题以为只是套了个SSM框架的学习网站真正导入IDEA、配置数据库、启动Tomcat的那一刻各种问题才刚开始冒头。这个项目是典型的SSM三件套全家桶——Spring管理Bean、SpringMVC处理请求、MyBatis操作MySQL配了一套大数据技术方向的学习内容展示还带完整的程序源码、数据库脚本、调试部署流程和开发环境配置。对正在学Java Web的人、需要交课设的在校生、或者想复盘SSM整合细节的初级开发者来说都是很好的练手素材。这篇就按我实际跑通这类项目的顺序把架构设计、数据库表结构、环境搭建、部署调试到问题排查的整条链路拆开讲清楚。1. 项目整体设计与思路拆解1.1 为什么SSM至今仍是练手首选很多人一提到Java Web就直接奔Spring Boot去了觉得SSM过时。但真实的企业系统里SSM框架改造的老项目数量依然庞大而且Spring Boot本身并没有完全脱离Spring和SpringMVC的底层逻辑只是把配置简化了。用SSM做学习网站最大的收获在于必须手动理解Bean的注册、依赖注入、事务管理、拦截器配置这一整套流程。Spring Boot用自动配置把这些全藏起来了出了问题反而更难定位。这个项目的名字里带了“大数据”三个字容易让人误以为要上Hadoop、Spark那些东西。实际并不是它是一个面向大数据技术学习者的内容平台核心业务是课程、资料、资讯这类学习资源的管理和展示。“大数据”更多体现在内容定位上比如大数据技术栈介绍、课程分类、数据可视化统计报表等。真要从技术栈角度说项目本身用的是经典SSMMySQL组合数据量级和并发量都远没达到需要分布式架构的程度所以不要被名字带偏。1.2 功能模块该怎么拆拿到这套源码后先别急着启动第一步应该对照数据库表和前端页面把功能模块梳理清楚。按我多年看项目的习惯这种学习类网站通常逃不出两条主线前台学习展示和后台管理维护。前台核心包括用户注册登录、课程列表浏览、课程详情查看、学习资料下载、学习笔记记录和搜索功能。后台则围绕管理员对课程、资料、用户、评论等实体的增删改查来设计。每一个模块都不是孤立存在的比如用户在前台注册后才能记录学习进度、收藏课程管理员在后台发布的课程会直接同步到前台课程列表。1.3 大数据元素在系统中的落点作为“大数据技术学习网”项目里有一个关键点容易被忽略——数据可视化展示。很多类似项目把数据可视化当摆设实际这个功能在SSM架构里实现起来并不难底层就是用一个SQL查询统计数据然后把数据序列化为JSON交给前端图表库渲染。比如统计各类课程的学习人数、用户增长趋势、热门资料下载排行完全可以做成柱状图、折线图和饼图作为大数据学习的入门示例也说得通。我当时拿到项目后为了确认数据可视化的具体实现翻看了Mapper层和Service层发现统计类查询基本都写在Mapper的XML文件里用COUNT、GROUP BY这类聚合函数完成。这里想提醒一句如果项目里数据量变大SQL统计会逐渐变慢这类查询可以考虑加缓存或定期汇总表这也是一个可扩展的方向。2. 核心技术点与数据库设计细节2.1 技术栈选型背后的原因SSM项目的技术栈选型通常很固定Spring做IoC容器和事务管理SpringMVC负责路由分发和参数绑定MyBatis作为ORM框架操作MySQL数据库。前端则用JSP加JSTL、Bootstrap或Layui这类现成UI库配合jQuery和AJAX做异步交互。这套组合虽然不像前后端分离项目那样时髦但胜在资料全、问题易排查、学习曲线平缓。依赖版本上Spring常见的是4.3.x或5.xMyBatis是3.4.x对应mybatis-spring整合包1.3.x。为什么不建议盲目升级到最新版因为老项目的依赖互相牵制Spring 5和Spring 4在Web整合时的部分API有变化MySQL驱动版本又必须和数据库版本匹配。新版本未必兼容老代码这就是为什么SSM项目往往自带一份能跑通的pom.xml那就是“标准答案”。2.2 数据库表结构的关键设计思路数据库是SSM项目里最重要的部分之一数据表一般是SQL脚本里预置好的。根据我对这类项目的经验核心表大概有这些表名用途关键字段t_user前台用户id、username、password、nickname、phone、create_timet_admin管理员id、username、password、levelt_course课程信息id、name、category、teacher、cover、intro、click_numt_resource学习资料id、title、file_url、download_num、course_id、create_timet_note学习笔记id、user_id、course_id、content、create_timet_comment课程评论id、user_id、course_id、content、create_timet_progress学习进度id、user_id、course_id、progress、update_time字段设计上有三个细节值得注意。第一是密码字段虽然项目里经常用MD5加密但实际生产中至少要加盐MD5本身对彩虹表攻击几乎没有防御力。第二是时间字段建议用datetime类型而不要用varchar存字符串否则后续做时间范围查询、排序统计会很痛苦。第三是外键关联很多课设项目为了省事不建物理外键用Java代码维护逻辑关联这个在小型项目里问题不大但一定要保证Mapper查询时手写JOIN逻辑正确。2.3 数据库连接配置与事务管理的坑SSM项目的数据库连接通常写在jdbc.properties里核心参数无非是driver、url、username、password。但这里有一个高频踩坑点MySQL 5.7和8.x的驱动类不一样连接URL的时区参数要求也不一样。老项目用com.mysql.jdbc.Driver新驱动要用com.mysql.cj.jdbc.Driver并且URL上要加serverTimezoneAsia/Shanghai否则启动直接报时区错误。事务管理则是另一个容易忽略的环节。SSM中一般用DataSourceTransactionManager管理事务然后在Spring-MyBatis配置XML里开启经过注解驱动的事务再在Service层方法上加Transactional。这里说句实在话对于学习网站这种读多写少的场景事务边界不用设得特别细但凡是涉及“先查询-再判断-再写库”的操作比如用户下订单、管理员修改课程后要记录日志就必须加事务否则中途出异常时数据会处于半写状态。3. 开发环境搭建与调试部署全流程3.1 从JDK到Tomcat的环境准备清单拿到源码后第一个任务不是看代码而是把开发环境拉齐。以本类项目最常用的环境为例JDK 1.8SSM系列项目的默认版本JDK 11以上对老框架的反射操作和模块化限制可能会导致意外问题IDEA 2020以上版本社区版就够用配置Maven插件方便Maven 3.6.x需要修改settings.xml加上阿里云镜像否则拉依赖速度慢到怀疑人生Tomcat 8.5/9.0版本要和JDK 1.8匹配Tomcat 10把javax改成了jakarta老SSM项目跑不起来MySQL 5.7/8.0导入项目SQL脚本建库建表。这个清单看着不起眼但版本组合一旦错了后面全是连锁反应。我遇到过同学装了个Tomcat 10来跑SSM项目结果项目里所有javax.servlet相关的类全部编译报错也遇到过装了MySQL 8但用的还是老驱动无限报ClassNotFoundException。版本匹配是SSM调试的第一课。3.2 数据库初始化最容易翻车的地方数据库脚本通常在项目里的db目录或SQL文件夹下文件名一般叫xxx.sql。导入的时候别直接在命令行里use database之后source一下完事要先看一眼脚本里的CREATE DATABASE语句确认库名和项目jdbc.properties里写的库名一致否则连接串指向一个不存在的数据库启动时就报Unknown database。导入成功后建议先手动在Navicat或命令行里跑几条业务SQL验证脚本完整性。比如查询用户表数据、课程表数据是否和预期一致因为有的脚本会内置管理员账号或测试数据确保这些数据存在后面登录页面才能直接试通。还要检查一下SQL脚本中是否有乱码问题尤其是UTF-8编码的表注释和初始数据导入后乱码会影响前端页面展示中文内容。3.3 IDEA导入项目的关键配置步骤IDEA导入SSM项目并不是“Open”一下就能跑的正常流程如下在IDEA中选择File - Open选中项目根目录的pom.xml以Maven项目方式导入等待依赖下载完成检查Project Structure里的Project SDK是否为JDK 1.8Language Level是否对应确认Maven设置里用的是本机配置的settings.xml而不是IDEA内置的默认配置打开src/main/resources里的jdbc.properties核对数据库用户名、密码、URL中的库名配置TomcatRun - Edit Configurations - 新建Tomcat Server - Local在Deployment选项卡里把项目的war包或war exploded添加进去把Application context设置为根路径比如/省得访问时还要带项目名启动前先把依赖完整的Maven打包跑一遍命令是mvn clean package -DskipTests能提前暴露出大量编译层面的问题。这一步一步做下来项目跑不起来的概率会小很多。很多初学者图省事直接点绿色三角形Tomcat插件和部署目录都没配好自然404或启动失败。3.4 增删改查在SSM里的完整链路这个学习网站本质上就是一组增删改查的集合。我以“用户注册”为例把这条链路串一下用户在注册页面填写信息点击提交后前端AJAX把JSON数据POST到SpringMVC的ControllerController使用RequestBody接收参数并手动封装User对象然后调用UserService的register方法Service里先通过UserMapper.selectByUsername判断用户名是否已被占用没有则调用insert并返回成功提示。Mapper层是SSM项目里代码量最大也最容易写错的地方。MyBatis的XML文件里参数用#{param}预编译排序字段这种不能参数化的字段才用${}但使用${}必须做好白名单校验否则会有SQL注入风险。还有一个小点Service层事务的类被Spring扫描到后方法名若是以接口形式调用时出了问题多半是因为没给接口方法加Transactional或者AOP切面指向了实现类而非接口。4. 大数据量场景下的常见性能优化点4.1 分页查询是学习网站的核心刚需这个学习网站的课程和资料数量多了以后“一次性加载全部数据”的模式会明显变慢。SSM项目里最直接有效的方案是使用PageHelper这个基于MyBatis的分页插件核心用法就一句话在查询前调用PageHelper.startPage(pageNum, pageSize)插件会自动拦截下一条SQL并改写为带LIMIT的分页查询。我用PageHelper时习惯在Controller层或Service层入口取页码参数默认值设为1每页条数设为10或12前端表格或卡片列表按页渲染。项目里如果没有集成PageHelper手写分页也是可行的先查COUNT得到总记录数再查LIMIT offset,pageSize得到当前页数据顺便封装一个PageResult类包含list、total、pageNum、pageSize思路不算复杂但没有插件用起来顺滑。4.2 前端表格大数据渲染的优化方向很多人在Web端渲染大量数据时思路还停留在“一次性把几千行数据全塞进表格”结果页面卡顿甚至假死。之前看到atik有讨论Qt里从TableWidget升级到QTableView配合自定义Model来解决大数据渲染问题本质是“只渲染可见区域而不是渲染所有数据”的思路这套思路放到Web表格也同样成立一是后端分页默认只返回一二十条二是前端的虚拟滚动或懒加载让页面只保留可视区那几十行DOM节点。在SSM项目里最简单的落地方式是先用分页接口把数据量控制在perPage以内后端提供count前端渲染页码按钮。后续如果数据量进一步变大可以把分页参数细化加入keyword、category、orderBy等条件参数让接口做得更通用这比单纯调大pageSize要靠谱。4.3 大数据统计查询要留意慢SQL项目里的数据可视化报表核心就是几条聚合SQL。用户量、课程点击量、资料下载量这些指标实时统计没问题但如果数据到达万级甚至更高聚合时全表扫描会导致页面打开变慢。优化方向有三个给WHERE条件里的category、create_time等字段建联合索引统计类查询改用汇总表或Redis缓存定时刷新给列表类查询只查出需要的字段别SELECT *。我实际做过一次对比一张20万行的课程点击日志表没索引的GROUP BY全表扫描要1.8秒加了索引之后降到0.2秒效果立竿见影。这类分析是面试常考的“数据库性能优化”知识点放在大数据学习网站项目里正好拿来练手。5. 常见问题与排查技巧实录5.1 数据库连接失败类问题速查数据库连接失败是SSM项目里出现频率最高的启动报错遇到的典型原因我整理成了表报错现象常见原因排查方向Access denied for user用户名或密码错误检查jdbc.properties确认无多余空格MySQL本地密码策略Unknown database库名写错或未建库核对项目SQL脚本中的建库语句确认库名严格一致Communications link failure连接URL端口写错、MySQL未启动、防火墙拦截用Navicat测试本地连接telnet 3306查看端口连通Server returns invalid timezoneMySQL 8时区问题URL加上serverTimezoneAsia/Shanghai驱动换新ClassNotFoundException: com.mysql.jdbc.DriverMySQL 8用了老驱动将驱动依赖升级到mysql-connector-java 8.x改驱动类为com.mysql.cj.jdbc.Driver排查这类问题有个通用顺序先用数据库客户端软件直接连MySQL确认账号密码和库名能通再确认项目里jdbc.properties同步无误最后看Tomcat日志里有没有异常堆栈能直接定位是SQL语法问题还是连接配置问题。5.2 MyBatis映射文件不被加载的经典坑SSM项目里Mapper接口和Mapper XML是分离的二者靠命名空间和接口全限定名绑定。最常见的报错是Invalid bound statement (not found)这句话的意思是运行时代理对象找不到SQL语句。排查方向就两个XML文件的namespace是否等于接口的完整类路径XML里每个statement的id是否等于接口方法名且resultType是否正确。再有一个隐蔽问题是Maven构建时resources目录没有把mapper的XML打包进classpath。解决办法是在pom.xml的build标签里显式声明resources将src/main/java下的xml也纳入构建范围。方法简单但极其容易遗漏很多项目换电脑后重新导入跑不起来都是因为这个。5.3 静态资源被拦截导致的白屏问题SpringMVC中DispatcherServlet的url-pattern通常配成/这意味着所有请求都会经过DispatcherServlet。如果配置里没有放行静态资源访问CSS、JS、图片全部会被404拦截页面就只剩光秃秃的HTML结构。解决方案是在SpringMVC配置XML里加上resources标签把static目录和前端框架的静态文件夹映射到对应URL路径。我记得自用的配置是这样mvc:resources mapping/static/** location/static// mvc:resources mapping/assets/** location/assets//还要在xml里开启注解驱动的组件扫描确认Controller、Service这些注解所在包路径被扫描到了。Controller类能被扫到但Service没被扫到的情况也很常见表现为页面能打开但调用后台接口时始终报空指针或404。5.4 部署上线与换机运行的最后提醒项目在本机跑通之后要换电脑运行或者部署到服务器有三件事我每次都要检查第一配置文件里的数据库连接是否改成了目标机器的地址和端口生产环境不要用root账号尽量单独建库建用户第二项目的部署方式是用Tomcat的webapps目录直接放war包还是用IDEA里配置的虚拟目录换环境后这个必须重新确认第三日志配置里log4j或logback的输出路径服务器上如果不存在该目录会导致日志组件初始化失败但Tomcat启动往往不会直接报出来。关于调试部署这块我建议所有初学者都亲手走一遍“IDEA配Tomcat启动-浏览器访问-后台管理-改写一个Mapper-重新打包-替换war包部署”的完整流程。这个过程中涉及的Java编译、Maven打包、版本兼容、上下文路径配置等知识比单纯看CRUD代码值钱得多因为这才是开发环境到生产环境的真实衔接。说到底SSM这个技术栈给学习网站这类项目提供了一套足够清晰的路由、持久层和事务管理框架。你在跑通它的过程中会同时接触到Spring容器初始化、MyBatis动态SQL、MySQL连接管理、Tomcat部署原理这些重要概念而这些恰恰是后面深入分布式系统和微服务架构时必须打牢的基础。我个人的体会是把“程序源码数据库调试部署开发环境”这个套路完整走一遍比看十遍网上教程都顶用。之后不管你是改项目功能还是照着这套结构重写一个自己的类目课程平台心里都会更有底。

相关新闻

pstack-claude:本地可调试的Claude代码辅助代理中间件

pstack-claude:本地可调试的Claude代码辅助代理中间件

1. 项目概述:pstack-claude 是什么,它解决的是哪类开发者的真实痛点?pstack-claude 这个名字乍看像一个工具组合词,但拆开来看——“pstack”是 Linux 系统中用于打印进程栈跟踪(process stack trace)的经典…

2026/10/9 11:08:57 阅读更多 →
从Linux镜像安装到运维实战:一份覆盖命令、原理与嵌入式部署的完整速查手册

从Linux镜像安装到运维实战:一份覆盖命令、原理与嵌入式部署的完整速查手册

2026年3月19日,我把攒了小一年的Linux笔记重新过了一遍,顺手把踩过的坑和绕过的弯按“从安装到进阶”的顺序重新整理成文。这篇东西不打算写成那种面面俱到的教科书,更多是记录那些我实际装过、配过、救回来的场景:从linux镜像安装…

2026/10/9 11:08:57 阅读更多 →
从零手写Java动态数组:理解扩容原理与ArrayList核心机制

从零手写Java动态数组:理解扩容原理与ArrayList核心机制

如果你刚学 Java 没几天,跟着网课敲到“数组”这一章,八成会产生一个疑惑: int[] arr new int[10] 这种写法,长度死死地定成了 10,万一后面要装 11 个数据怎么办?重开一个更大的数组,再把旧数…

2026/10/9 11:08:57 阅读更多 →

最新新闻

Xshell授权真相与安全终端替代方案指南

Xshell授权真相与安全终端替代方案指南

1. 项目概述:为什么“Xshell免费版下载”这个需求背后藏着大量认知偏差“Xshell官网免费版下载”——这七个字在搜索框里每天被输入成千上万次,但几乎99%的提问者并不清楚自己真正要找的是什么。我接触过上百位刚入门的运维新人、高校实验室学生、中小企…

2026/10/9 11:42:45 阅读更多 →
PHP后端活体识别接入实践:从签名鉴权到结果落库

PHP后端活体识别接入实践:从签名鉴权到结果落库

1. 为什么风控链路里要单独加一道活体识别做风控开发的朋友应该都有同感:纯规则引擎越来越顶不住黑产的攻击手法了。前几年常见的是盗号、撞库,现在更狠的是伪冒申请——手里握着别人的身份证号、手机号、照片,就能轻松注册一个账户&#xff…

2026/10/9 11:42:44 阅读更多 →
Vue3响应式核心:从effect源码看懂依赖收集与派发更新

Vue3响应式核心:从effect源码看懂依赖收集与派发更新

1. 为什么我要把 effect 源码当作 Vue3 响应式的敲门砖很多人学 Vue3 源码,上来就冲 reactive、ref 的实现,结果读完 get 和 set 的拦截逻辑还是懵的。原因很简单:reactive 和 ref 只是响应式数据的"壳",真正让数据变化…

2026/10/9 11:42:44 阅读更多 →
SQLCipher加密SQLite快速验证包testsqliteCipher.7z实战指南

SQLCipher加密SQLite快速验证包testsqliteCipher.7z实战指南

简介:本资源是面向Qt5开发者的一套SQLite数据库加密实战项目,聚焦于使用SQLCipher实现256位AES加密/解密,适用于桌面应用中敏感数据的安全存储场景,尤其适合具备C和Qt基础、正探索数据库安全增强方案的中阶开发者。压缩包为7z格式…

2026/10/9 11:42:44 阅读更多 →
AI重构前端开发:框架之争落幕,系统思维与协作能力成为新护城河

AI重构前端开发:框架之争落幕,系统思维与协作能力成为新护城河

前端圈最近有个很有意思的迹象:大家在群里讨论的不再是“React 和 Vue 哪个好”“要不要学 Next.js”,而是“AI 辅助开发怎么落地”“团队要不要引入 AI 编码工具”。说实话,“别再卷框架了”这个声音越来越多,是因为 AI 时代的前…

2026/10/9 11:42:44 阅读更多 →
会议室管理系统实战:从Excel排班到高并发预约引擎

会议室管理系统实战:从Excel排班到高并发预约引擎

简介:这份会议室管理系统资源面向高校计算机相关专业学生与课程设计实践者,围绕数据库课程设计场景,提供从需求调研、概念模型与逻辑结构设计,到数据库建表、图形化界面编程实现的完整参考方案,帮助解决会议室预约、使…

2026/10/9 11:41:42 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* 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 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 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/8 21:13:17 阅读更多 →
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/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练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/9 6:17:20 阅读更多 →