多商户多仓库带扫描云进销存SaaS版ERP源码:从多租户数据模型到扫码链路的完整拆解
简介这是一套面向多商户、多仓库经营场景的云进销存与ERP管理系统源码采用SaaS架构支持条码扫描录入、多商户共用平台、跨仓库调拨与营销版无限商户扩展适合电商企业、连锁零售、分销商及品牌商进行二次开发或私有化部署。压缩包共1317个文件约22.71MB以509个PHP业务代码、206个JavaScript脚本、53个CSS样式与42个HTML页面构成前后端主体另含208个PNG、68个GIF等界面素材以及SQL数据库文件、安装说明、授权协议与多语言文档目录结构完整便于按模块定位功能。目前已有297人学习下载。源码开放意味着可基于原始代码做个性化定制读者能借此研究多商户权限隔离、库存同步、扫描录入与SaaS计费逻辑并参考现成模块快速搭建自己的进销存平台减少从零开发成本。1. 多商户多仓库带扫描云进销存这套 SaaS 版 ERP 源码到底能跑什么业务如果你手上同时管着两三个仓库、十几个分销商户每天还在用 Excel 对库存、用聊天工具传订单那这套「多商户多仓库带扫描云进销存系统 ERP 管理系统 SaaS 营销版无限商户源码」值得你花一个下午拆开看看。它的核心定位很明确把多商户、多仓库、条码扫描、进销存、ERP 核心模块和 SaaS 多租户能力打包成一套可私有部署、可二次开发的完整源码而不是让你去订阅一个按年付费、数据还放在别人服务器上的成品软件。适合谁一是做电商、连锁零售、分销批发的技术负责人二是接企业信息化外包、需要快速交付一套进销存底座的开发团队三是想拿一套真实业务系统练手多租户架构和扫码链路的工程师。它解决的不是「有没有系统用」而是「系统能不能按我的业务改、数据能不能留在自己手里、商户数量能不能不设上限地扩」。2. 多租户与多仓库的数据模型先看懂表结构再谈部署2.1 商户隔离到底靠什么字段实现拿到源码包别急着找安装脚本。先翻数据库设计文档或者直接看建表 SQL重点确认「多商户」是怎么隔离的。常见做法有两种一种是共享数据库、共享表靠tenant_id或merchant_id字段做行级隔离另一种是每个商户独立库靠连接路由切换。这套系统从「无限商户」的定位看大概率走的是共享表加租户字段的方案因为独立库在商户数量上去之后运维成本会失控。你要在代码里找到全局的租户上下文注入点。一般是在登录鉴权之后把当前用户的merchant_id写进 session 或者请求上下文后续所有 DAO 层的查询都自动带上这个条件。如果源码里没有做统一拦截而是每个查询手写where merchant_id ?那二次开发时漏写一个条件就是跨商户数据泄露这是血泪经验级别的坑。-- 典型的租户隔离表结构重点看 merchant_id 和 warehouse_id 的位置 CREATE TABLE stock ( id bigint NOT NULL AUTO_INCREMENT, merchant_id bigint NOT NULL COMMENT 商户ID租户隔离核心字段, warehouse_id bigint NOT NULL COMMENT 仓库ID多仓库维度, sku_id bigint NOT NULL COMMENT 商品SKU, quantity int NOT NULL DEFAULT 0 COMMENT 可用库存, locked_quantity int NOT NULL DEFAULT 0 COMMENT 锁定库存下单未出库, PRIMARY KEY (id), UNIQUE KEY uk_merchant_warehouse_sku (merchant_id,warehouse_id,sku_id), KEY idx_merchant (merchant_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;上面这段建表语句里uk_merchant_warehouse_sku这个唯一索引是关键。它保证了同一个商户、同一个仓库、同一个 SKU 只有一条库存记录避免并发入库时插出重复行。locked_quantity字段的存在说明系统支持下单锁库存这是进销存和普通商品管理的重要分界线。你部署前要确认这个唯一索引在源码里是否真实存在有些简化版会漏掉导致库存对不上。2.2 多仓库调拨的库存扣减顺序多仓库场景下最容易翻车的是调拨和出库时的库存扣减顺序。常见做法是调拨单审核通过时先从源仓库扣减可用库存并转入在途目标仓库确认收货后再增加可用库存。如果源码里是审核即双向增减那在途状态就丢了运输途中库存会凭空消失。// 调拨审核的库存处理逻辑重点看扣减和状态流转 public function approveTransfer($transferId) { $transfer $this-transferModel-find($transferId); if ($transfer[status] ! pending) { throw new Exception(调拨单状态不允许审核); } // 源仓库扣减可用库存增加在途库存 $this-stockService-decreaseAvailable( $transfer[from_warehouse_id], $transfer[sku_id], $transfer[quantity] ); $this-stockService-increaseInTransit( $transfer[from_warehouse_id], $transfer[sku_id], $transfer[quantity] ); // 更新调拨单状态为已发出 $this-transferModel-update($transferId, [status shipped]); }这段逻辑里decreaseAvailable和increaseInTransit必须是同一个事务否则扣了可用但没记在途库存就丢了。参数上要注意quantity的单位是基本单位还是包装单位扫码入库时如果单位没统一会出现扫一箱扣一件的玄学问题。我一般会在部署后先跑一遍「创建调拨单 → 审核 → 确认收货」的完整链路用两个仓库各放 100 件调拨 30 件看两边库存和在途数是否对得上。3. 条码扫描链路的落地从扫码枪到库存更新的完整闭环3.1 扫码枪的输入模式与前端监听条码扫描枪本质上是一个 HID 键盘设备扫到码之后会模拟键盘快速输入一串字符最后补一个回车。所以前端不需要装驱动只要监听输入框的keydown事件判断回车触发查询或提交就行。但这里有个细节扫码枪输入速度极快如果输入框有防抖或者异步校验很容易丢字符。// 扫码输入监听重点看回车触发和防重复提交 let scanBuffer ; let lastScanTime 0; document.getElementById(scanInput).addEventListener(keydown, function(e) { const now Date.now(); // 两次扫码间隔小于300ms视为重复直接忽略 if (now - lastScanTime 300) { e.preventDefault(); return; } if (e.key Enter) { e.preventDefault(); if (scanBuffer.length 4) { scanBuffer ; return; // 条码太短判定为误触 } handleScan(scanBuffer); scanBuffer ; lastScanTime now; } else if (e.key.length 1) { scanBuffer e.key; } });这段代码里scanBuffer累积扫码枪输入的字符回车时提交。lastScanTime做 300 毫秒的防重复是因为有些扫码枪一次扫描会触发两次回车。scanBuffer.length 4的判断是防止手动误触回车提交空数据。参数上300 这个值可以根据你的扫码枪型号调整太快会漏扫太慢会重复。handleScan 函数里应该走异步查询 SKU 并更新库存注意要做 loading 状态避免连续扫码时请求乱序。3.2 扫码入库的库存更新与并发控制扫码入库看起来简单扫一个码加一件库存但并发场景下就是另一回事。两个操作员同时扫同一个 SKU 入库如果代码里是先查再改就会丢更新。正确做法是用数据库的行锁或者原子更新。-- 原子更新库存避免并发丢更新 UPDATE stock SET quantity quantity 1, updated_at NOW() WHERE merchant_id ? AND warehouse_id ? AND sku_id ?; -- 检查影响行数如果为0说明记录不存在需要先插入这条 UPDATE 语句直接在数据库层面做自增不经过应用层的读-改-写天然避免并发问题。执行后要检查affected_rows如果返回 0说明这个商户仓库SKU 的组合还没有库存记录需要走 INSERT。INSERT 的时候要处理唯一索引冲突用INSERT ... ON DUPLICATE KEY UPDATE或者捕获异常后重试 UPDATE。扫码出库的逻辑反过来但要加库存充足性校验。常见做法是UPDATE stock SET quantity quantity - ? WHERE ... AND quantity ?如果影响行数为 0说明库存不足返回错误提示。这样就不需要先查库存再判断一条 SQL 搞定校验和扣减。提示扫码链路的测试一定要用真实扫码枪跑不要用键盘模拟。真实设备的输入速度和回车时机跟手动敲键盘差别很大很多在浏览器里测试正常的逻辑接上扫码枪就翻车。4. SaaS 营销版的权限与商户开通无限商户背后的边界4.1 商户注册与套餐权限的绑定关系「无限商户」不等于「无限制使用」源码里通常有一套套餐或者权限体系来控制每个商户能用哪些功能。你要找到商户开通的入口看它是自动注册还是后台手动开通以及套餐字段是怎么跟功能权限挂钩的。常见做法是平台超管在后台创建商户分配一个套餐 ID套餐表里定义了这个套餐包含哪些模块比如是否含扫码、是否含多仓库、商户数量上限。商户下的用户再按角色分权限。这里容易踩的坑是套餐变更后已经登录的商户 session 里还缓存着旧权限需要强制刷新或者做权限版本号。// 权限校验中间件重点看套餐模块和角色权限的合并逻辑 public function handle($request, $next) { $merchant $request-get(merchant); $user $request-get(user); // 先校验套餐是否包含当前模块 $module $request-route()-getName(); if (!$this-packageService-hasModule($merchant[package_id], $module)) { return response()-json([code 403, msg 当前套餐不含此功能], 403); } // 再校验用户角色权限 if (!$this-permissionService-check($user[role_id], $module)) { return response()-json([code 403, msg 无权限操作], 403); } return $next($request); }这段中间件先查套餐再查角色顺序不能反。因为套餐是商户级别的硬限制角色是商户内部的软分配。如果先查角色可能出现角色有权限但套餐没买的情况逻辑上就绕了。参数上package_id和role_id都要做缓存不然每个请求都查库商户一多数据库压力就上来了。4.2 商户数据导出与备份的隔离SaaS 系统里商户数据导出是个敏感操作。源码里如果有导出功能你要确认导出时是否强制带了merchant_id条件。我见过一些源码导出 SQL 是拼字符串的商户 ID 从请求参数拿改一下参数就能导出别家数据。这是部署前必须审计的点。// 安全的数据导出商户ID从session取不从请求参数取 public function exportStock(Request $request) { $merchantId $request-session()-get(merchant_id); // 从会话取不可篡改 $warehouseId $request-input(warehouse_id); // 强制带上商户条件 $query StockModel::where(merchant_id, $merchantId); if ($warehouseId) { $query-where(warehouse_id, $warehouseId); } $data $query-limit(10000)-get(); // 限制单次导出量 return $this-exportToExcel($data); }这里的关键是merchant_id从 session 取而不是从请求参数取。请求参数是用户可以改的session 是服务端维护的。另外加了limit(10000)限制单次导出量防止一个请求把数据库拖垮。如果你的业务需要导出更多走异步任务队列不要在前端请求里同步跑。5. 部署避坑与常见问题排查那些文档里不会写的事5.1 环境依赖与版本兼容性排查现象安装脚本跑完访问首页白屏或者报 500日志里提示某个类找不到。原因源码包里的依赖锁文件composer.lock 或 package-lock.json指定的版本跟你服务器上装的不一致或者 PHP/Node 版本差了一个大版本。解决先看源码根目录有没有composer.json和composer.lock有的话用composer install而不是composer update严格按锁文件装。PHP 版本看composer.json里的require字段一般会写php: 7.4这种。如果服务器 PHP 是 8.x 而源码只兼容 7.4要么降级 PHP要么改代码适配别硬跑。5.2 扫码后库存不更新的排查路径现象扫码枪能扫出条码前端也弹出了商品信息但库存数字没变。原因三种可能——一是扫码走的是查询接口而不是入库接口只查了商品没提交库存变更二是库存更新走了异步队列但队列没启动三是数据库事务回滚了但前端没收到错误提示。解决打开浏览器开发者工具看扫码回车后发出的请求 URL 和响应。如果是查询接口说明前端逻辑绑错了按钮。如果响应成功但库存没变去查队列进程ps aux | grep queue看有没有在跑。如果队列在跑但任务失败看队列日志里的异常堆栈多半是数据库字段类型不匹配或者唯一索引冲突。5.3 多商户数据串号的定位方法现象A 商户登录后看到了 B 商户的订单或库存。原因某个查询或者更新操作漏了merchant_id条件常见于后期新增的功能模块或者报表统计 SQL。解决全局搜索源码里的 SQL 查询重点看where子句里有没有merchant_id。更高效的做法是在 DAO 层加一个全局作用域自动注入商户条件但要注意有些跨商户的统计查询需要手动排除这个作用域。定位到具体文件后补上条件并加一条回归测试用例。5.4 无限商户下的性能衰减现象商户数量到几百个之后列表页越来越慢数据库 CPU 飙升。原因所有商户共享表查询没走索引或者分页查询用了count(*)全表扫描。解决检查merchant_id字段有没有建索引联合查询的字段顺序对不对。分页查询尽量用游标或者延迟关联不要用limit offset大偏移。如果数据量确实大考虑按商户 ID 做分表或者归档历史数据。5.5 授权协议与二次开发边界现象想改代码但不确定授权协议允不允许或者改了之后升级覆盖了修改。原因源码包里的 LICENSE 文件没仔细看或者修改直接改在了核心文件上。解决先读 LICENSE确认是 MIT、GPL 还是商业授权。GPL 要求衍生代码也开源商业授权通常限制分发。二次开发尽量用插件或者继承的方式扩展不要直接改核心文件这样后续升级能合并。如果必须改核心用 git 管理把修改做成独立的 commit升级时 cherry-pick。6. 二次开发进阶从跑起来到改得动把系统跑起来只是第一步真正体现这套源码价值的地方在于你能不能按自己的业务改。我一般会先做一件事找到系统的路由配置文件把所有接口列出来按模块分类。这套系统的路由大概率集中在几个文件里比如routes/api.php、routes/web.php或者框架对应的路由目录。列出来之后你就能看清哪些是进销存核心、哪些是营销模块、哪些是 SaaS 管理后台。然后挑一个最小的闭环做改造练手比如给商品表加一个自定义字段「产地」从数据库加列、到模型加 fillable、到表单加输入框、到列表加展示列走一遍完整链路。这一步能帮你摸清这套源码的分层结构控制器在哪、服务层在哪、模型在哪、前端模板在哪。走通之后再改复杂的业务逻辑就有底了。# 快速定位路由和控制器映射以 Laravel 为例 php artisan route:list --columnsmethod,uri,action # 输出示例 # GET|HEAD api/stock/list App\Http\Controllers\StockControllerlist # POST api/stock/scan App\Http\Controllers\StockControllerscan # POST api/transfer/approve App\Http\Controllers\TransferControllerapprove这条命令把路由、HTTP 方法和控制器方法列成一张表你一眼就能看出扫码走的是StockControllerscan调拨审核走的是TransferControllerapprove。想改哪个功能直接定位到对应控制器方法再顺着方法里的服务调用往下追。改造的时候有个习惯我一直在用每改一个功能先在测试环境跑一遍完整的业务链路从创建商户、开通套餐、添加商品、扫码入库、创建订单、出库扣减到生成报表。这条链路跑通说明你的改动没有破坏核心流程。跑不通看是哪一步的库存或者状态不对顺着数据流往回查。还有一个容易被忽略的点是数据库迁移。如果你加了字段或者改了表结构一定要写成 migration 文件不要手动在数据库里改。手动改的后果是换一台机器部署的时候忘了改系统就报字段不存在。migration 文件跟着代码走部署时跑php artisan migrate就自动同步了。// 给商品表加产地字段的 migration 示例 public function up() { Schema::table(product, function (Blueprint $table) { $table-string(origin, 100)-nullable()-after(name)-comment(产地); }); } public function down() { Schema::table(product, function (Blueprint $table) { $table-dropColumn(origin); }); }这个 migration 里after(name)控制字段位置nullable()允许为空comment是给后来人看的。down方法必须写回滚的时候用得上。写完跑php artisan migrate执行跑php artisan migrate:rollback回滚。养成这个习惯之后换环境部署再也没出现过字段缺失的问题。从那以后我每次拿到一套新源码都强制走一遍「列路由 → 跑闭环 → 写 migration」的流程不跳过任何一步。这套多商户多仓库带扫描的 SaaS 版 ERP 源码底子够厚能改的空间也大关键是你得先摸清它的骨架再动手。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

SWAT预训练+GNN+物理损失函数=水质-openclaw还原研究

SWAT预训练+GNN+物理损失函数=水质-openclaw还原研究

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

2026/10/12 14:25:42 阅读更多 →
智能座舱多用户身份隔离与临时授权:安当CAS在汽车密钥管理中的最小权限实践

智能座舱多用户身份隔离与临时授权:安当CAS在汽车密钥管理中的最小权限实践

一、为什么智能座舱需要"多用户"视角的密钥体系 传统车端密钥体系往往以"车辆"为唯一主体:一辆车对应一套根密钥、一个平台证书、若干固件签名密钥。ECU 安全烧录、诊断接入 Secure Access、固件完整性 Secure Boot、调试端口保护这四大场景&am…

2026/10/12 14:25:42 阅读更多 →
多模态情感识别实战:EEG、眼动与GSR特征融合全解析

多模态情感识别实战:EEG、眼动与GSR特征融合全解析

简介:面向毕业设计的多模态情感识别Python项目源代码,融合脑电(EEG)、眼动追踪与皮肤电(GSR)三类生理信号,完整覆盖信号预处理、特征提取、特征选择、机器学习分类以及情感模型构建链路,适合高校学生或研究者参考实现和算法验证。…

2026/10/13 15:25:16 阅读更多 →

最新新闻

AI工程化能力三支柱:数据智能、模型架构与系统交付

AI工程化能力三支柱:数据智能、模型架构与系统交付

1. 这不是“学AI”,而是构建一套可迁移、能落地的工程化能力体系很多人点开“AI培训学什么”这类标题,第一反应是找速成班、抄代码、背模型名字——结果学了三个月,连数据怎么清洗都卡在pandas报错上;或者花两万块报了“大模型实战…

2026/10/13 16:50:38 阅读更多 →
ASCII码与汉字编码:从乱码到UTF-8的完整解析

ASCII码与汉字编码:从乱码到UTF-8的完整解析

1. 从一段乱码说起:为什么你需要搞懂ASCII码你有没有遇到过这种情况:打开一个文本文件,里面全是“锟斤拷”或者“ ”这样的乱码,明明文件大小正常,内容却完全没法看。或者你在写程序的时候,把一个中文字符串…

2026/10/13 16:50:38 阅读更多 →
Spark on YARN GPU推理实战:资源调度、配置与性能调优

Spark on YARN GPU推理实战:资源调度、配置与性能调优

1. 为什么要在 Spark on YARN 上跑 GPU 推理把 AI 模型推理塞进 Spark on YARN 这套体系里,第一次听到的人多半会皱眉:推理不是应该用专门的在线服务框架吗,为什么要绕这么大一圈?我最初接手这个方向时也有同样的疑问,…

2026/10/13 16:50:38 阅读更多 →
MSP430G2553驱动RC522门禁系统实战:低功耗SPI时序与ISO14443-A协议精解

MSP430G2553驱动RC522门禁系统实战:低功耗SPI时序与ISO14443-A协议精解

简介:本资源是一套基于MSP430微控制器与MFRC522 RFID模块的嵌入式门禁系统完整工程,面向嵌入式初学者、电子设计竞赛备赛者及物联网硬件开发爱好者,解决低功耗门禁原型快速验证与软硬协同开发问题。压缩包共20个文件,含6个核心头文…

2026/10/13 16:50:38 阅读更多 →
IS95前向链路Rake接收机仿真:从物理层结构到多径合并的完整实现

IS95前向链路Rake接收机仿真:从物理层结构到多径合并的完整实现

简介:这份资源面向通信工程、无线通信课程学习者及CDMA仿真入门者,聚焦IS-95前向链路系统的建模与验证,帮助理解扩频调制、多径衰落与RAKE接收等核心机制。压缩包共13个文件,以10个m脚本、2个mat数据文件和1份doc说明文档为主&…

2026/10/13 16:50:38 阅读更多 →
SwiftUI中的Agent+Skill框架:摆脱if-else,构建可扩展的任务调度系统

SwiftUI中的Agent+Skill框架:摆脱if-else,构建可扩展的任务调度系统

项目名我起了个SwiftUI-Agent-Skill,翻译过来没啥歧义:一套跑在SwiftUI应用里的Agent技能框架。Agent在这里不是玄学,就是一个负责拆解用户意图、派发任务、汇总结果的调度核心;Skill则是挂在它下面的一个个独立能力模块&#xff…

2026/10/13 16:49:38 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 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/12 21:05:09 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →