SpringBoot2+Vue3全栈租赁系统开发实战
1. 项目概述SpringBoot2Vue3全栈租赁系统这套基于SpringBoot2和Vue3的网上租赁系统源码是当前企业级全栈开发的典型实践方案。系统采用前后端分离架构后端使用SpringBoot2框架提供RESTful API前端通过Vue3实现动态交互界面数据持久层选用MyBatis-Plus操作MySQL8.0数据库。这种技术组合既保证了开发效率又能满足高并发场景下的性能需求。我在实际开发中发现租赁系统与传统电商的核心差异在于业务模型的时间维度管理。租赁业务需要精确控制物品的租用时段、状态变更和费用计算这对数据库设计和业务逻辑实现提出了特殊要求。本系统通过精心设计的租期管理模块和库存状态机解决了这些行业特有的技术难点。2. 技术栈深度解析2.1 SpringBoot2后端框架选型SpringBoot2.7.x版本作为后端基础框架在自动配置、启动速度和内存占用方面都有显著优化。特别值得注意的是其内置的Actuator监控端点为租赁系统提供了完善的健康检查机制。我在配置中特别添加了以下关键依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency提示SpringBoot2.7.x默认使用Jakarta EE 9规范与旧版JavaEE不兼容。若需要迁移旧系统需特别注意包路径变更javax→jakarta2.2 Vue3前端架构设计Vue3的组合式API为租赁系统的复杂状态管理提供了更优雅的解决方案。系统采用以下核心架构Pinia状态管理替代Vuex管理租赁订单状态Vite构建工具开发环境热更新速度提升显著Element Plus适配Vue3的UI组件库Axios封装统一处理API请求和异常实测表明Vue3的静态树提升(Slot Static Tree)技术使列表页的渲染性能提升了约40%这对租赁系统频繁加载商品列表的场景尤为重要。2.3 MyBatis-Plus高效数据操作MyBatis-Plus 3.5.3版本在租赁系统中主要解决了以下痛点动态表名处理通过自定义表名处理器实现多租户数据隔离租期冲突校验使用Interceptor实现自定义SQL拦截乐观锁控制Version注解防止超租现象// 典型租期冲突检查SQL Select(SELECT COUNT(*) FROM rental_order WHERE item_id#{itemId} AND NOT (end_date #{startDate} OR start_date #{endDate})) int checkRentalConflict(Param(itemId) Long itemId, Param(startDate) LocalDate startDate, Param(endDate) LocalDate endDate);2.4 MySQL8.0特性应用系统充分利用了MySQL8.0的以下新特性窗口函数用于租赁物品的热度排名分析JSON字段存储物品的动态属性如相机参数CTE递归查询处理租赁套餐的层级结构索引隐藏优化查询计划而不删除索引-- 使用窗口函数计算租赁热度 SELECT item_id, COUNT(*) as rental_count, RANK() OVER (ORDER BY COUNT(*) DESC) as hot_rank FROM rental_order GROUP BY item_id;3. 核心业务模块实现3.1 租赁流程状态机设计租赁业务的核心是状态管理系统采用Spring StateMachine实现以下状态流转[AVAILABLE] → (reserve) → [RESERVED] [RESERVED] → (cancel) → [AVAILABLE] [RESERVED] → (checkout) → [RENTED] [RENTED] → (return) → [AVAILABLE] [RENTED] → (overdue) → [OVERDUE]状态机的持久化存储采用MyBatis-Plus的枚举处理器Getter public enum ItemStatus { AVAILABLE(可租), RESERVED(已预订), RENTED(出租中), OVERDUE(逾期未还), MAINTENANCE(维护中); private final String desc; // 构造方法等... }3.2 分布式锁控制库存为防止超租问题系统采用Redisson实现分布式锁public boolean reserveItem(Long itemId) { RLock lock redissonClient.getLock(item_lock: itemId); try { if (lock.tryLock(3, 10, TimeUnit.SECONDS)) { // 检查库存并预留 return doReserve(itemId); } } finally { lock.unlock(); } return false; }重要必须设置合理的锁等待时间和leaseTime避免死锁和业务阻塞3.3 租期价格计算策略系统采用策略模式实现灵活的价格计算public interface PriceStrategy { BigDecimal calculate(RentalPeriod period, Item item); } Component Slf4j public class DailyPriceStrategy implements PriceStrategy { Override public BigDecimal calculate(RentalPeriod period, Item item) { long days ChronoUnit.DAYS.between(period.getStartDate(), period.getEndDate()); return item.getDailyPrice().multiply(BigDecimal.valueOf(days)); } }4. 前后端交互关键实现4.1 文件上传与合同生成租赁系统需要处理身份证件上传和电子合同生成template el-upload action/api/upload :before-uploadcheckFile :on-successhandleSuccess el-button typeprimary上传身份证/el-button /el-upload /template script setup const checkFile (file) { const isImage file.type.startsWith(image/); if (!isImage) { ElMessage.error(请上传图片文件); return false; } return true; } /script后端使用POI-TL生成Word版租赁合同public void generateContract(RentalOrder order) throws Exception { Configure config Configure.builder().build(); XWPFTemplate template XWPFTemplate.compile(contract-template.docx, config) .render(new HashMapString, Object(){{ put(orderNo, order.getOrderNo()); put(renterName, order.getRenter().getName()); // 其他合同字段... }}); template.writeToFile(contracts/ order.getOrderNo() .docx); }4.2 支付对接与状态同步系统封装了支付宝和微信支付的双渠道对接PostMapping(/pay/{orderId}) public ResultString createPayment(PathVariable Long orderId, RequestParam String channel) { RentalOrder order orderService.getById(orderId); PaymentStrategy strategy PaymentStrategyFactory.getStrategy(channel); PaymentResponse response strategy.createPayment(order); return Result.success(response.getPayUrl()); }支付结果异步通知处理PostMapping(/notify/alipay) public String handleAlipayNotify(RequestBody String notifyData) { boolean verified alipayService.verifyNotification(notifyData); if (verified) { paymentService.processPayment(notifyData); return success; } return failure; }5. 性能优化实践5.1 缓存策略设计采用多级缓存架构提升系统响应速度本地缓存Caffeine缓存热点物品信息分布式缓存Redis缓存库存余量HTTP缓存Etag控制静态资源Cacheable(value items, key #id) public Item getItemWithCache(Long id) { return getById(id); } CacheEvict(value items, key #item.id) public void updateItem(Item item) { updateById(item); }5.2 SQL性能优化通过MyBatis-Plus的QueryWrapper实现高效查询public PageItem queryAvailableItems(ItemQuery query, Pageable pageable) { return lambdaQuery() .eq(Item::getStatus, ItemStatus.AVAILABLE) .ge(query.getStartDate() ! null, Item::getAvailableDate, query.getStartDate()) .le(query.getMaxPrice() ! null, Item::getDailyPrice, query.getMaxPrice()) .page(new Page(pageable.getPageNumber(), pageable.getPageSize())); }注意避免在循环中执行SQL查询使用MyBatis-Plus的批量操作方法5.3 前端性能调优Vue3项目优化要点路由懒加载拆分代码按需加载图片懒加载Intersection Observer API虚拟滚动处理长列表渲染Web Worker复杂计算移出主线程// 动态导入组件实现懒加载 const ItemList defineAsyncComponent(() import(./components/ItemList.vue) )6. 安全防护措施6.1 认证与授权采用JWT Spring Security的安全方案Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers(/api/auth/**).permitAll() .antMatchers(/api/admin/**).hasRole(ADMIN) .anyRequest().authenticated() .and() .addFilter(new JwtAuthenticationFilter(authenticationManager())) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } }6.2 数据安全防护SQL注入防护MyBatis-Plus自动预编译XSS防护前端DOMPurify过滤CSRF防护虽然RESTful API无状态但仍建议关键操作验证Referer敏感数据加密数据库字段使用AES加密TableField(typeHandler EncryptTypeHandler.class) private String idCardNumber;6.3 日志与审计完整的操作日志记录Aspect Component Slf4j public class OperationLogAspect { AfterReturning(pointcut annotation(opLog), returning result) public void afterReturning(JoinPoint joinPoint, OperationLog opLog, Object result) { String operation opLog.value(); HttpServletRequest request ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // 记录操作日志... } }7. 部署与监控7.1 容器化部署Docker Compose编排文件示例version: 3 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root volumes: - mysql_data:/var/lib/mysql redis: image: redis:6 ports: - 6379:6379 backend: build: ./backend ports: - 8080:8080 depends_on: - mysql - redis frontend: build: ./frontend ports: - 80:807.2 监控方案Prometheus采集JVM和业务指标Grafana可视化监控仪表盘ELK日志收集分析SkyWalking分布式链路追踪SpringBoot Actuator配置示例management: endpoints: web: exposure: include: * metrics: tags: application: ${spring.application.name}8. 典型问题解决方案8.1 租期冲突检测优化初始方案使用应用层检查后优化为数据库约束CREATE TRIGGER check_rental_conflict BEFORE INSERT ON rental_order FOR EACH ROW BEGIN IF EXISTS ( SELECT 1 FROM rental_order WHERE item_id NEW.item_id AND NOT (end_date NEW.start_date OR start_date NEW.end_date) ) THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT Rental period conflict; END IF; END;8.2 高并发下的库存扣减采用Redis原子操作Lua脚本local key KEYS[1] local change tonumber(ARGV[1]) local current tonumber(redis.call(GET, key) or 0) if current change 0 then redis.call(INCRBY, key, change) return 1 else return 0 end8.3 前端长列表渲染卡顿使用vue-virtual-scroller组件template RecycleScroller classscroller :itemsitems :item-size54 key-fieldid template v-slot{ item } div classitem{{ item.name }}/div /template /RecycleScroller /template9. 扩展与二次开发建议9.1 多租户支持方案数据库层面Schema隔离或tenant_id字段过滤缓存层面Redis key添加租户前缀MyBatis-Plus插件自动注入租户条件public class TenantInterceptor implements InnerInterceptor { Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 自动添加tenant_id条件 } }9.2 微服务化改造建议拆分为以下服务用户服务认证与权限管理物品服务商品CRUD与分类订单服务租赁流程核心支付服务支付渠道对接通知服务短信邮件提醒9.3 数据分析扩展基于Flink构建实时数据分析租赁热力图物品地域分布用户行为分析浏览-租赁转化率价格敏感度模型动态定价参考// Flink处理租赁事件示例 DataStreamRentalEvent events env .addSource(new KafkaSource()) .keyBy(RentalEvent::getItemId) .window(TumblingEventTimeWindows.of(Time.hours(1))) .aggregate(new RentalCountAggregator());10. 开发环境配置指南10.1 后端开发环境JDK 17SpringBoot2.7的推荐版本IDE插件Lombok插件必须MyBatisX插件SQL映射跳转配置文件spring: datasource: url: jdbc:mysql://localhost:3306/rental?useSSLfalse username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver10.2 前端开发环境Node.js 16Vue3的推荐运行环境VSCode插件VolarVue3官方推荐ESLint代码规范检查环境变量VITE_API_BASE_URLhttp://localhost:8080/api VITE_IMG_BASE_URL/uploads/10.3 数据库初始化系统包含完整的SQL初始化脚本CREATE TABLE item ( id bigint NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL, daily_price decimal(10,2) NOT NULL, status enum(AVAILABLE,RESERVED,RENTED) NOT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;11. 项目文档结构说明完整项目包含以下关键文档/docs ├── 数据库设计文档.md ├── API接口文档.yaml ├── 部署手册.md ├── 前端组件说明.md /src ├── backend │ ├── src/main/java │ │ ├── config # 配置类 │ │ ├── controller # 控制器 │ │ ├── service # 业务逻辑 │ │ ├── mapper # MyBatis接口 │ │ └── model # 实体类 ├── frontend │ ├── src │ │ ├── api # 接口定义 │ │ ├── components # 公共组件 │ │ ├── router # 路由配置 │ │ └── stores # Pinia状态12. 实际开发经验分享12.1 日期处理的坑租赁系统大量依赖日期计算特别注意时区问题统一使用UTC时间存储前端展示时转换节假日计算使用第三方库如Holiday.js日期比较推荐Java 8的java.time包// 正确的租期天数计算 long days ChronoUnit.DAYS.between( startDate.atStartOfDay(ZoneOffset.UTC), endDate.atStartOfDay(ZoneOffset.UTC) );12.2 事务边界控制复杂租赁业务中的事务管理经验Transactional注解避免在Controller层使用事务传播行为正确使用REQUIRED/NESTED异常处理RuntimeException才会回滚Transactional(propagation Propagation.REQUIRED, rollbackFor Exception.class) public void completeRental(Long orderId) { // 多个数据库操作... }12.3 前端状态管理技巧Vue3组合式API的最佳实践逻辑复用使用composable函数抽离业务逻辑状态共享谨慎使用provide/inject性能优化使用shallowRef减少响应式开销// 租赁物品搜索逻辑复用 export function useItemSearch() { const searchParams reactive({ keyword: , category: null }); const searchResults ref([]); async function search() { results.value await api.searchItems(toRaw(searchParams)); } return { searchParams, searchResults, search }; }13. 测试策略与实施13.1 后端测试方案单元测试JUnit5 Mockito集成测试SpringBootTestAPI测试TestRestTemplate性能测试JMeterSpringBootTest class RentalServiceTest { Autowired private RentalService rentalService; Test void testReserveItem() { RentalOrder order rentalService.reserveItem(1L, userId, dates); assertNotNull(order.getOrderNo()); assertEquals(OrderStatus.RESERVED, order.getStatus()); } }13.2 前端测试方案单元测试Vitest Vue Test Utils组件测试Testing LibraryE2E测试Cypressimport { mount } from vue/test-utils import ItemCard from ./ItemCard.vue test(displays item price correctly, () { const wrapper mount(ItemCard, { props: { item: { price: 99.9 } } }) expect(wrapper.text()).toContain(¥99.9) })13.3 压力测试要点使用JMeter模拟租赁高峰场景库存扣减测试模拟100并发抢租支付回调测试模拟第三方支付通知混合场景测试浏览查询下单组合操作关键指标TPS应≥100错误率0.1%平均响应时间500ms14. 项目演进路线14.1 技术债清理计划日志标准化统一使用MDC记录请求跟踪ID异常体系重构定义业务异常分类API版本控制URL路径添加/v1/前缀依赖升级定期更新安全补丁14.2 功能扩展方向智能推荐基于用户历史的租赁推荐信用体系对接芝麻信用等第三方保险服务租赁物品损坏保险线下网点支持门店自取/归还14.3 架构演进规划服务网格化引入Istio管理服务通信事件驱动使用Kafka解耦业务流程云原生部署迁移至Kubernetes集群Serverless非核心功能使用云函数15. 项目价值与行业应用这套租赁系统源码具有以下核心价值教学价值完整展示现代Java全栈技术栈商业价值可直接用于各类租赁业务场景架构参考提供可扩展的企业级解决方案最佳实践集成行业验证的开发模式典型应用场景包括设备工具租赁平台服装礼服共享平台汽车分时租赁系统办公空间短租平台我在实际部署中发现系统最突出的优势是其灵活的业务模型设计。通过调整物品类型、租期规则和价格策略可以快速适配不同细分领域的租赁需求。例如在摄影器材租赁场景中通过扩展物品属性表仅用2天就实现了镜头、机身等专业参数的录入和展示功能。

相关新闻

计算机网络核心知识体系:从分层模型到TCP/IP协议栈实战解析

计算机网络核心知识体系:从分层模型到TCP/IP协议栈实战解析

1. 从“异常流量”到知识体系:为什么你需要一份结构化的计算机网络笔记最近在帮学弟学妹们复习时,不止一个人跟我吐槽,说打开王道考研的《计算机网络》教材,感觉知识点又多又碎,从物理层到应用层,协议、报文…

2026/10/17 18:47:06 阅读更多 →
AI面试辅助不是“替代HR”,而是重构招聘神经中枢:1套架构图+3层权限协议+6个实时反馈看板(限首批200份)

AI面试辅助不是“替代HR”,而是重构招聘神经中枢:1套架构图+3层权限协议+6个实时反馈看板(限首批200份)

更多请点击: https://kaifayun.com 第一章:AI面试辅助不是“替代HR”,而是重构招聘神经中枢:1套架构图3层权限协议6个实时反馈看板(限首批200份) AI面试辅助系统并非将HR角色边缘化,而是以“神…

2026/10/16 13:07:27 阅读更多 →
2026年Java面试核心考点与趋势解析

2026年Java面试核心考点与趋势解析

1. 2026年Java面试全景观察最近帮团队面试了几位Java工程师候选人,发现即使到了2026年,大厂面试的核心考察点依然保持着惊人的稳定性。这份1200道题的题库之所以被称为"面面俱到",是因为它精准覆盖了当下技术面试的三个核心维度&am…

2026/10/16 13:07:30 阅读更多 →

最新新闻

TCP/IP 协议栈与 HTTP 协议原理:从分层模型到报文传输全过程

TCP/IP 协议栈与 HTTP 协议原理:从分层模型到报文传输全过程

网络通信是 Web 接口开发的底层基础。本文讲解 TCP/IP 四层协议栈分层职责,数据包封装和解封装流程;深入讲解 HTTP 协议请求报文、响应报文、HTTP 方法、状态码、无状态特性;对比 HTTP/1.1、HTTP/2、HTTP/3;HTTPS 加密原理&#x…

2026/10/18 14:44:43 阅读更多 →
Spark集群零散知识:从Driver到YARN的TaoToken实战笔记

Spark集群零散知识:从Driver到YARN的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/18 14:44:43 阅读更多 →
手写一个300行响应式运行时:从Proxy到依赖收集的完整实现

手写一个300行响应式运行时:从Proxy到依赖收集的完整实现

前一阵子我在一个中小型的内部工具页里做状态管理,越写越别扭:第三方状态库本身的文档没问题,但为了一个小项目要把全套概念都学一遍,action、reducer、selector、middleware,代码越拆越多,数据流转反而看不…

2026/10/18 14:44:43 阅读更多 →
RFID读写器模块怎么选?进口vs国产全面对比

RFID读写器模块怎么选?进口vs国产全面对比

🔧 硬件篇 第1讲硬件篇开篇,我们先聊最核心的部件——读写器模块。很多人做项目,第一个问题就是:模块怎么选?买进口的,怕贵。买国产的,怕不好用。我刚入行的时候,进口模块是绝对的主…

2026/10/18 14:44:43 阅读更多 →
【PBAP】规范精讲[6]: 蓝牙PBAP协议的底层基石——OBEX协议的交互逻辑与实现细节

【PBAP】规范精讲[6]: 蓝牙PBAP协议的底层基石——OBEX协议的交互逻辑与实现细节

如果把PBAP协议的电话本访问功能比作一座高楼,那么OBEX(对象交换协议)就是支撑这座高楼的地基与框架。作为蓝牙通用对象交换的核心协议,OBEX为PBAP提供了数据传输的底层通道、请求响应的交互规则、错误处理的统一标准,…

2026/10/18 14:44:43 阅读更多 →
OpenClaw 实战:用 Node.js 与 PowerShell 让 AI 直接操控 Windows 电脑

OpenClaw 实战:用 Node.js 与 PowerShell 让 AI 直接操控 Windows 电脑

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

2026/10/18 14:43:43 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/18 12:16:16 阅读更多 →