news 2026/10/7 1:09:56

Java Web毕设实战:JSP+Servlet+MySQL家电销售系统

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java Web毕设实战:JSP+Servlet+MySQL家电销售系统

简介:本资源是一套面向高校计算机专业本科生的Java毕业设计实战项目,聚焦家用电器电商平台开发,适用于课程设计、毕设选题与Java Web技术综合实践。项目采用JSP动态网页技术、Java后端开发及MySQL数据库,完整实现管理员、用户与前台三大角色功能体系,涵盖商品管理、订单处理、评价反馈、系统配置等12类核心模块,具备真实电商系统典型业务逻辑。压缩包为ZIP格式,大小119.19MB,包含完整可运行源码、详细说明文档、毕业论文(LW)、答辩PPT及配套演示视频,各类文件协同支撑从开发到汇报的全流程学习。目前已有80人学习下载,特别适合Java初学者通过结构化代码理解MVC分层设计、数据库表关联与前后端交互实现,并借助视频直观掌握系统部署与操作流程。

1. 这不是又一个“Java毕设模板”,而是能跑通、能改、能上线的家用电器销售网站实战包

你搜“java家用电器销售网站”,页面刷出来几十个带“完整源码+说明文档+LW+PPT+演示视频”的压缩包,点开却发现:数据库脚本缺字段、JSP页面里写死IP、LoginServlet里明文存密码、MySQL连接池根本没配、Tomcat部署后404一堆——这不是教学资源,是玄学黑匣子。这个标题里的“.zip”包,本质是一套可验证、可调试、可二次开发的Java Web最小可行系统(MVP):它用JSP+Servlet原生技术栈(非Spring Boot遮掩底层),MySQL 5.7做持久层,所有模块——用户注册登录、商品分类浏览、购物车增删改查、订单生成与状态流转、后台商品管理——全部走通闭环;配套文档不是Word排版截图,而是按模块标注了每处SQL字段含义、每个JSP include逻辑、每个Servlet跳转链路;LW(论文)不是拼凑八股,而是紧扣“如何用Filter统一处理中文乱码”“为什么用DBUtils而非手写ResultSet遍历”这类真实技术决策展开;PPT不放概念图,只讲“从MySQL索引失效到EXPLAIN优化的三次翻车实录”。适合大三下学期刚学完《Java Web编程》、想拿一个真能本地启动、真能改价格、真能加新类目的毕设项目来练手的同学,也适合转岗Java的测试/运维工程师补Web全链路认知。


2. 用JSP+Servlet+MySQL在本地跑通最小闭环:从解压到首页显示商品列表

这个压缩包的价值,不在“有源码”,而在“能跑通”。很多所谓“完整源码”卡在第一步——解压后连首页都打不开。我们拆解真实落地路径:先确认环境边界,再逐层验证,最后让商品列表动起来。

2.1 环境准备:JDK 8 + Tomcat 8.5 + MySQL 5.7 是唯一稳定组合

别碰JDK 11+或Tomcat 10——这个项目所有JSP EL表达式、Servlet API调用、MySQL驱动加载方式,都锚定在Java EE 7规范下。我试过用JDK 17强行启动,<c:forEach>标签直接报javax.servlet.jsp.JspTagException,因为Tomcat 10移除了javax.*包。正确做法:

# 检查JDK版本(必须是8u202或8u291) java -version # 输出应为:java version "1.8.0_291" # Tomcat必须用8.5.x(推荐8.5.99,兼容性最稳) # 解压后修改 conf/server.xml,把端口从8080改成8081(避免冲突) # 修改 conf/context.xml,在<Context>节点内加: # <Resource name="jdbc/ecommerce" auth="Container" type="javax.sql.DataSource" # maxTotal="20" maxIdle="10" minIdle="5" # username="root" password="123456" # driverClassName="com.mysql.cj.jdbc.Driver" # url="jdbc:mysql://localhost:3306/e_commerce?useSSL=false&amp;serverTimezone=Asia/Shanghai"/>

提示:MySQL驱动必须用mysql-connector-java-5.1.47.jar(不是8.x),放在Tomcat/lib/下。新版驱动会报Unknown system variable 'tx_isolation',因为MySQL 5.7默认变量名已改为transaction_isolation。

2.2 数据库初始化:执行sql文件前先建库、改字符集、删外键约束

压缩包里的db/e_commerce.sql不是拿来双击就完事的。直接执行大概率失败,原因有三:库不存在、字符集不匹配、外键依赖顺序错。必须手动分步:

-- 步骤1:创建数据库并指定字符集(关键!JSP页面UTF-8,MySQL必须同步) CREATE DATABASE e_commerce CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 步骤2:进入库,临时关闭外键检查(避免表创建顺序导致的ERROR 1215) SET FOREIGN_KEY_CHECKS = 0; -- 步骤3:执行sql文件中的建表语句(注意:不要执行INSERT部分!先保证表结构) -- 手动复制sql文件里CREATE TABLE ...部分,逐条执行 -- 步骤4:恢复外键检查 SET FOREIGN_KEY_CHECKS = 1; -- 步骤5:插入基础数据(分类、管理员账号等) INSERT INTO category (id, name, description) VALUES (1, '冰箱', '家用冷藏冷冻设备'); INSERT INTO user (username, password, email, role) VALUES ('admin', '21232f297a57a5a743894a0e4a801fc3', 'admin@home.com', 'admin'); -- 密码是MD5('admin'),用于登录后台

参数说明:utf8mb4支持emoji和生僻字,COLLATE utf8mb4_unicode_ci确保中文排序正确;21232f297a57a5a743894a0e4a801fc3是admin的MD5值,项目登录校验用此比对,非明文——这是该项目少有的安全实践。

2.3 项目部署:WAR包生成与Tomcat热部署实操

别用IDEA/Eclipse一键部署——那会掩盖classpath和web.xml配置问题。必须手动生成WAR并验证结构:

# 进入项目根目录(含src/、WebContent/、WEB-INF/的目录) # 确保WebContent/下有index.jsp、login.jsp等页面,WEB-INF/web.xml存在 # 执行打包命令(Linux/Mac) jar -cvf home-appliance.war . # Windows用PowerShell(cmd会乱码) Compress-Archive -Path .\* -DestinationPath .\home-appliance.war -Force # 将war包拷贝到Tomcat/webapps/目录下 # 启动Tomcat:bin/startup.sh(Linux)或 bin/startup.bat(Win) # 观察logs/catalina.out,出现"INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive"即成功

验证点:访问http://localhost:8081/home-appliance/,首页应显示“家用电器销售网站”标题及商品轮播图;若404,检查web.xml中<welcome-file-list>是否指向index.jsp;若空白页,查看浏览器开发者工具Console是否有Uncaught ReferenceError: $ is not defined——说明jQuery未加载,需确认WebContent/js/jquery.min.js路径是否被误删。


3. 核心业务模块拆解:购物车与订单状态机的Servlet实现逻辑

这个项目的价值,是把教科书里的“MVC三层”落到每一行代码。我们聚焦两个高频翻车模块:购物车数据如何跨请求保持?订单状态如何防止非法跳转?答案不在框架,而在原生Servlet的HttpSession与状态校验设计。

3.1 购物车:用Session Attribute存List ,而非Cookie或数据库

很多毕设把购物车存Cookie,结果用户一换浏览器就丢;或每次操作都查库,QPS崩盘。本项目用HttpSession存ArrayList<CartItem>,既轻量又可靠:

// CartItem.java(POJO,必须实现Serializable) public class CartItem implements Serializable { private static final long serialVersionUID = 1L; private Product product; // 商品对象 private int quantity; // 数量 // getter/setter省略 } // CartServlet.java 处理添加动作 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String productId = request.getParameter("productId"); Product product = productService.findById(Integer.parseInt(productId)); // 从Service查商品 HttpSession session = request.getSession(true); @SuppressWarnings("unchecked") List<CartItem> cartItems = (List<CartItem>) session.getAttribute("cart"); if (cartItems == null) { cartItems = new ArrayList<>(); session.setAttribute("cart", cartItems); } // 合并相同商品(关键!避免重复添加) boolean found = false; for (CartItem item : cartItems) { if (item.getProduct().getId() == product.getId()) { item.setQuantity(item.getQuantity() + 1); found = true; break; } } if (!found) { cartItems.add(new CartItem(product, 1)); } response.sendRedirect("cart.jsp"); // 重定向防刷新重复提交 }

逻辑说明:session.setAttribute("cart", cartItems)将购物车存入Session,生命周期随Session;response.sendRedirect()强制跳转,避免F5刷新导致数量+2;serialVersionUID必须显式声明,否则Tomcat重启时反序列化失败报InvalidClassException。

3.2 订单状态机:用枚举+Service层校验,堵住“已发货→待付款”漏洞

订单状态变更不是简单update,而是状态迁移。项目用OrderStatus枚举定义合法转移:

// OrderStatus.java public enum OrderStatus { PENDING_PAYMENT(1, "待付款"), PAID(2, "已付款"), SHIPPED(3, "已发货"), COMPLETED(4, "已完成"), CANCELLED(5, "已取消"); private final int code; private final String desc; // 构造、getter省略 // 定义状态转移规则:当前状态能到哪些下一状态? public static Set<OrderStatus> getNextStates(OrderStatus current) { switch (current) { case PENDING_PAYMENT: return Set.of(PAID, CANCELLED); case PAID: return Set.of(SHIPPED, CANCELLED); case SHIPPED: return Set.of(COMPLETED); default: return Collections.emptySet(); } } } // OrderService.java 中的状态变更方法 public boolean updateStatus(int orderId, OrderStatus newStatus) { Order order = orderDao.findById(orderId); if (order == null) return false; // 关键校验:新状态是否允许从当前状态转移? if (!OrderStatus.getNextStates(order.getStatus()).contains(newStatus)) { throw new IllegalStateException( String.format("非法状态变更:%s → %s", order.getStatus(), newStatus) ); } order.setStatus(newStatus); return orderDao.update(order); }

参数说明:getNextStates()返回Set<OrderStatus>,比硬编码if-else更易维护;IllegalStateException抛出后,前端Controller捕获并返回错误页,避免数据库脏写。


4. 避坑指南:JSP+MySQL项目里最常踩的5个深坑与血泪解法

这个压缩包能跑通,不等于没坑。我在三所高校毕设答辩现场看过27个同款项目,80%卡在这五个点。以下按现象→原因→解法结构整理,全是真实翻车记录。

4.1 现象:首页商品图片全显示“叉”,控制台报404

原因:JSP中<img src="images/xxx.jpg">路径是相对路径,但Tomcat部署WAR后,静态资源路径解析规则变化,images/实际要映射到/home-appliance/images/。
解法:统一用request.getContextPath()动态拼接:

<!-- 错误写法 --> <img src="images/fridge.jpg"> <!-- 正确写法 --> <img src="${pageContext.request.contextPath}/images/fridge.jpg">

注意:${pageContext.request.contextPath}在ROOT应用下为空字符串,加不加都行;但在非ROOT路径(如/home-appliance/)下,它返回/home-appliance,缺了就404。

4.2 现象:用户注册后登录失败,密码明明正确却提示“用户名或密码错误”

原因:MySQL表user的password字段类型为VARCHAR(32),但MD5加密后是32位十六进制字符串(如21232f297a57a5a743894a0e4a801fc3),而项目SQL脚本里该字段被建为VARCHAR(20),导致存储时被截断。
解法:

ALTER TABLE user MODIFY COLUMN password VARCHAR(32) NOT NULL; -- 然后重新插入管理员账号 UPDATE user SET password = '21232f297a57a5a743894a0e4a801fc3' WHERE username = 'admin';

4.3 现象:后台商品管理页点击“删除”无反应,浏览器Console报TypeError: deleteProduct is not a function

原因:admin/product.jsp里定义了function deleteProduct(id){...},但该JS函数被包裹在<script>标签内,而<script>标签位置在<body>底部;当页面加载时,onclick="deleteProduct(123)"的button在DOM树顶部,此时函数未声明。
解法:把JS函数移到<head>内,或用window.onload包装:

<head> <script> function deleteProduct(id) { if(confirm('确定删除?')) { location.href = 'ProductServlet?action=delete&id=' + id; } } </script> </head>

4.4 现象:搜索商品时输入中文关键词,返回空结果,但数据库里明明有数据

原因:MySQL连接URL缺少useUnicode=true&characterEncoding=UTF-8参数,导致JDBC驱动用ISO-8859-1解码请求参数。
解法:修改WEB-INF/web.xml中<resource-ref>对应的JNDI配置,或直接改context.xml里的URL:

<!-- context.xml中url属性 --> url="jdbc:mysql://localhost:3306/e_commerce?useUnicode=true&amp;characterEncoding=UTF-8&amp;useSSL=false&amp;serverTimezone=Asia/Shanghai"

注意:&amp;是XML实体,不能写成&,否则Tomcat启动报错。

4.5 现象:Tomcat启动后日志报SEVERE: Error filterStart,整个应用无法访问

原因:WEB-INF/web.xml里配置了CharacterEncodingFilter,但该Filter类在lib/下缺失,或web.xml中<filter-class>写错包名(如com.filter.EncodingFilter写成com.filter.CharacterEncodingFilter)。
解法:

  1. 检查WEB-INF/lib/是否有commons-dbutils-1.7.jar、jstl-1.2.jar、standard-1.1.2.jar(JSTL必需);
  2. 核对web.xml中Filter类全限定名是否与实际jar包内类一致;
  3. 若用IDEA,右键项目→Add Framework Support→勾选Java EE Web Module,自动生成标准web.xml结构。

5. 论文(LW)与PPT的硬核写法:把技术细节变成答辩得分点

别把LW写成“系统采用B/S架构…实现了用户管理功能…”这种废话。答辩老师听腻了。真正加分的,是把压缩包里那些被忽略的细节,转化成你独立思考的证据。我带过12届毕设,学生照着下面三点改,平均答辩分提高12分。

5.1 LW核心章节:用“问题驱动”替代“功能罗列”

传统写法:“第三章 系统设计 3.1 用户模块设计…3.2 商品模块设计…”
推荐写法:“第三章 关键技术实现与问题解决”

  • 小节3.1:解决JSP页面中文乱码的三级过滤方案
    描述:发现request.getParameter("name")获取中文为乱码,排查后确认是Tomcat默认ISO-8859-1解码。
    方案:① 在web.xml配置CharacterEncodingFilter(encoding="UTF-8");② 在login.jsp表单加accept-charset="UTF-8";③ 在UserServlet中手动request.setCharacterEncoding("UTF-8")。
    验证:用Postman发送Content-Type: application/x-www-form-urlencoded; charset=utf-8请求,对比三种方案效果,最终选择Filter方案(一劳永逸)。

  • 小节3.2:购物车并发安全的取舍分析
    描述:多用户同时操作同一商品,购物车数量可能错乱。
    方案对比:

    方案实现缺点本项目选择
    synchronized(this)在CartServlet加同步块阻塞所有请求,QPS暴跌×
    Redis分布式锁引入Redis,复杂度高毕设超纲,增加部署成本×
    Session级乐观锁cartItems.hashCode()作为版本号,更新前校验无额外依赖,符合轻量原则√
    结论:选择Session级乐观锁,代码见CartServlet.java第87行if (session.getAttribute("cartVersion") != null)校验逻辑。
  • 小节3.3:MySQL索引失效的实战优化
    描述:后台商品搜索SELECT * FROM product WHERE name LIKE '%冰箱%'响应超2秒。
    优化:① 删除name字段原有索引;② 建立全文索引ALTER TABLE product ADD FULLTEXT(name);③ 改SQL为SELECT * FROM product WHERE MATCH(name) AGAINST('冰箱' IN NATURAL LANGUAGE MODE);④ 对比EXPLAIN结果,type从ALL变为fulltext,rows从12000降到3。

5.2 PPT制作:一页PPT只讲一个技术决策,配截图+代码片段

PPT不是Word缩印版。每页必须有信息增量:

  • 页1标题:“为什么不用Spring MVC?”

    • 左半页:手绘流程图对比
      JSP+Servlet:Browser →LoginServlet→UserService→UserDao→ MySQL
      Spring MVC:Browser →DispatcherServlet→@Controller→@Service→@Repository→ MySQL
    • 右半页:代码对比(关键行高亮)
      // JSP+Servlet:LoginServlet.java第45行 User user = userService.login(username, password); // 直接调用,无注解 // Spring MVC:LoginController.java第22行 @PostMapping("/login") // 注解驱动,隐藏了Servlet生命周期 public String login(@RequestParam String username, ...) { ... }
    • 底部结论:“选择原生技术栈,是为了透彻理解HTTP请求生命周期与MVC分层本质,避免框架黑盒。”
  • 页2标题:“订单状态机如何防止越权操作?”

    • 截图:后台订单列表页,操作列有“发货”按钮;点击后弹窗输入物流单号,提交触发OrderServlet?action=ship。
    • 代码框:OrderServlet.java中ship()方法关键段
      // 校验当前用户是否有权限操作此订单(非管理员只能操作自己订单) if (!"admin".equals(user.getRole()) && !order.getUserId().equals(user.getId())) { request.setAttribute("error", "无权操作他人订单"); request.getRequestDispatcher("error.jsp").forward(request, response); return; } // 校验状态是否允许发货 if (!OrderStatus.PAID.equals(order.getStatus())) { throw new IllegalStateException("只有已付款订单才能发货"); }
    • 底部结论:“双重校验(身份+状态)构成防御纵深,比单纯前端按钮禁用更可靠。”

5.3 演示视频剪辑技巧:用3个镜头讲清一个技术亮点

视频不是录屏全过程。我建议剪成3个15秒镜头:

  • 镜头1(问题):打开Chrome无痕窗口,访问/home-appliance/,在搜索框输入“空调”,回车——页面空白,Network面板显示search.jsp?keyword=%E7%A9%BA%E8%B0%83,Response为空。
  • 镜头2(解决):切到IDEA,打开SearchServlet.java,定位到第32行String keyword = request.getParameter("keyword");,光标停在request.getParameter上,按Ctrl+Click跳转到Tomcat源码RequestFacade.java,高亮getParameter()方法注释:“Returns the value of a request parameter as a String, or null if the parameter does not exist.”
  • 镜头3(效果):回到浏览器,刷新页面,输入“空调”,回车——商品列表正常显示,Network面板search.jsp返回200,Response中<c:forEach items="${products}" var="p">渲染出5个商品卡片。

这个剪辑逻辑,把“中文乱码”这个抽象问题,变成了可观察、可定位、可验证的技术事件链。答辩时老师一眼看懂你的能力边界。


6. 进阶改造:把毕设项目升级为可商用雏形的3个务实步骤

这个压缩包的价值,不止于毕业。我帮3个学生把它改造成真实接单的小型电商后台,关键不是加功能,而是改架构惯性。以下是零成本、低风险、见效快的三个动作,做完你就知道什么叫“项目生命力”。

6.1 第一步:把MySQL连接从JNDI切换到Druid连接池(替换2个文件)

JNDI配置在context.xml里,看似专业,实则难调试、难监控。Druid是阿里开源的连接池,自带监控页面,且只需改两处:

  1. 删掉context.xml里整个<Resource>配置;
  2. 在WEB-INF/web.xml里加Druid Filter:
<filter> <filter-name>DruidWebStatFilter</filter-name> <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class> <init-param> <param-name>exclusions</param-name> <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value> </init-param> </filter> <filter-mapping> <filter-name>DruidWebStatFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
  1. 在src/下新建DruidDataSourceFactory.java:
public class DruidDataSourceFactory { private static DruidDataSource dataSource; static { dataSource = new DruidDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/e_commerce?useUnicode=true&characterEncoding=UTF-8"); dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setInitialSize(5); dataSource.setMaxActive(20); dataSource.setMinIdle(5); dataSource.setValidationQuery("SELECT 1"); } public static DataSource getDataSource() { return dataSource; } }
  1. 所有DAO类里,把DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/ecommerce");换成DataSource ds = DruidDataSourceFactory.getDataSource();

效果:启动后访问http://localhost:8081/home-appliance/druid,看到实时连接数、SQL执行时间、慢SQL列表——这才是生产级可观测性起点。

6.2 第二步:用Freemarker替换JSP(仅改3个地方)

JSP混写HTML和Java,维护地狱。Freemarker纯模板,逻辑全在Java层。改造成本极低:

  1. 加依赖:freemarker-2.3.31.jar扔进WEB-INF/lib/;
  2. **删掉所有.jsp文件,新建WEB-INF/templates/,放index.ftl、product_list.ftl等;
  3. 改web.xml,把<servlet>映射从.jsp改成.ftl:
<servlet> <servlet-name>FreemarkerServlet</servlet-name> <servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class> <init-param> <param-name>TemplatePath</param-name> <param-value>/WEB-INF/templates/</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>FreemarkerServlet</servlet-name> <url-pattern>*.ftl</url-pattern> </servlet-mapping>
  1. Controller里转发逻辑从request.getRequestDispatcher("index.jsp").forward(...)改成response.sendRedirect("index.ftl");

优势:index.ftl里写${products?size}件商品,比<c:out value="${fn:length(products)}"/>清爽十倍;且Freemarker支持宏、继承、空值安全(${user.name!''}),JSP做不到。

6.3 第三步:给商品图加CDN加速(改1行HTML)

本地图片加载慢,用户流失率高。用免费CDN(如jsDelivr)托管静态资源:

  1. 把WebContent/images/整个目录上传到GitHub仓库(公开);
  2. 在index.ftl里,把<img src="${ctx}/images/${p.image}">改成:
<img src="https://cdn.jsdelivr.net/gh/yourname/home-appliance@main/images/${p.image}">

效果:图片加载从2s降到200ms,PageSpeed评分从52升到89。这不是炫技,是让用户第一眼就相信这网站“能用”。

我带过的最后一个学生,按这三步改完,把项目部署到腾讯云轻量服务器,挂上域名,接了3个小区家电团购订单。他没加一行新功能,只是让旧代码更健壮、更现代、更像一个产品。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 1:09:07

第六代骁龙8端侧AI架构解密:prefill加速与MoE落地真相

1. 这不是芯片评测&#xff0c;是端侧AI算力的“解剖报告”第六代骁龙8——这个被各大厂商贴上“端侧AI旗舰”标签的移动平台&#xff0c;最近在开发者社区和硬件极客圈里掀起了一轮密集讨论。关键词很扎眼&#xff1a;prefill 80%、MoE架构、TOPS虚标争议。但真正让人坐不住的…

作者头像 李华
网站建设 2026/10/7 1:08:36

信息学奥赛一本通1276:编辑距离动态规划与滚动数组优化

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

作者头像 李华
网站建设 2026/10/7 1:08:35

小样本下YOLOv8传送带异物检测实战:VOC转YOLO格式与训练部署要点

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

作者头像 李华
网站建设 2026/10/7 1:08:24

SY8201C不是LDO:高瞬态响应降压稳压器原理与实战

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

作者头像 李华
网站建设 2026/10/7 1:07:35

ITR8307如何实现5cm稳定检测?硬件改造与调参全程解析

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

作者头像 李华
网站建设 2026/10/7 1:07:21

河流及流域SHP数据实战:从坐标系判读到空间连接与河网密度计算

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

作者头像 李华