AntDesignPro of Vue权限管理实战:路由与按钮级权限设计
1. 权限管理在后台系统里到底管什么后台管理系统做久了你会发现一个规律功能越堆越多最先扛不住的往往不是性能而是权限。谁能看到哪个菜单、谁只能看不能改、谁连按钮都不该看见这些问题如果一开始没设计好后面每加一个页面都要打补丁维护成本会指数级上升。AntDesignPro of Vue 这套中后台前端方案本身提供了一套基于路由和角色的权限控制骨架但很多人拿到手之后只是照着文档跑通了登录真正落到业务里还是一团乱麻。这篇文章围绕 AntDesignPro of Vue 的权限管理展开把菜单权限、路由权限、按钮级权限、动态路由加载这几块拆开讲清楚。适合正在用这套框架做中后台的开发者也适合想理解前端权限设计思路的人。我会从整体设计思路讲到具体实现再到实际踩过的坑尽量把每个“为什么这么做”都说明白让你看完能直接对照自己的项目改。先说结论AntDesignPro of Vue 的权限体系核心就两条线一条是路由级权限决定用户能进哪些页面另一条是操作级权限决定用户在页面里能点哪些按钮。两条线都依赖一个东西——当前用户的权限标识集合。这个集合从哪来、长什么样、怎么用是整个权限系统的地基。2. 整体设计思路与方案选型2.1 为什么权限要分两层来做很多人一开始会把权限理解成“能不能登录”登录成功就全放行。这在内部小工具里没问题但一旦系统面向多个角色比如管理员、运营、普通员工就必须区分粒度。AntDesignPro of Vue 把权限拆成路由级和操作级是有道理的。路由级权限解决的是“页面能不能进”。如果用户没有某个页面的权限理想情况下这个菜单根本不该出现在侧边栏用户手动敲 URL 也应该被拦下来。操作级权限解决的是“进了页面能干什么”。同一个订单详情页管理员能点“删除”客服只能点“备注”这两种角色的页面结构一样但按钮的可见性不同。把这两层分开的好处是职责清晰。路由权限在路由跳转时统一拦截操作权限在组件内部按需判断。如果混在一起每个页面都要写一遍“我能不能进”代码会非常冗余。2.2 权限标识的存储与流转权限判断的本质是“比对”。用户身上带着一组权限标识页面或按钮声明自己需要什么标识两者匹配就放行。所以设计的第一步是确定权限标识的格式。常见做法是用字符串比如dashboard:view、order:delete、user:edit。这种冒号分隔的命名方式可读性好也方便做前缀匹配。AntDesignPro of Vue 默认的角色判断用的是角色名比如admin、user但实际业务里更推荐用细粒度的权限点因为角色是会变的权限点相对稳定。这些标识从哪来一般是登录接口返回的。用户登录成功后后端返回用户信息里面包含permissions数组或者roles数组。前端拿到之后存到状态管理里通常是 Vuex 的 store。存的位置很关键因为路由守卫、菜单渲染、按钮判断都要读它放在全局 store 里最方便。注意权限标识不要存在 localStorage 里做唯一来源。localStorage 可以被用户手动改虽然前端权限本身防不住有心人但至少要以内存中的 store 为准localStorage 只做刷新后的恢复用。2.3 静态路由与动态路由的取舍AntDesignPro of Vue 默认给了一套静态路由表所有页面都注册好然后通过meta.permission字段来控制访问。这种方式简单直接适合权限变化不频繁的系统。另一种是动态路由登录后根据用户权限从后端拉取路由配置前端用router.addRoutes动态注册。这种方式适合权限差异极大的系统比如不同租户看到的菜单完全不同。但它的复杂度也高刷新页面时路由还没注册完就会出现白屏需要额外处理。我的建议是中小型系统用静态路由加权限过滤就够了别一上来就搞动态路由。动态路由带来的收益在多数项目里并不明显反而容易在刷新、面包屑、标签页这些地方出问题。AntDesignPro of Vue 的静态路由方案配合meta.permission已经能覆盖大部分场景。3. 核心细节解析与实操要点3.1 路由 meta 里的权限字段怎么配在 AntDesignPro of Vue 里路由配置通常在router.config.js或者按模块拆分的文件里。每条路由的meta对象可以挂权限信息。一个典型的配置长这样{ path: /order, name: Order, meta: { title: 订单管理, permission: [order:view] }, component: () import(/views/order/Index) }这里的permission是个数组意味着进入这个页面需要同时满足数组里的所有权限还是满足任意一个这取决于你守卫里的判断逻辑。我一般用“任意一个满足即可”因为一个页面往往对应多个角色写“同时满足”反而别扭。菜单渲染的时候会遍历路由表检查每条路由的meta.permission和当前用户的权限集合是否有交集。没有交集的路由直接从菜单里过滤掉。这一步在src/layouts/BasicLayout.vue或者菜单生成逻辑里做。提示meta里除了permission还常放title、icon、hidden这些字段。hidden: true用来标记那些不该出现在菜单里但需要注册的路由比如详情页、编辑页。3.2 权限判断工具函数的封装判断逻辑不要散落在各个组件里一定要封装成工具函数。AntDesignPro of Vue 自带了一个checkPermission或者类似的指令但实际项目里我建议自己再包一层方便统一处理。// src/utils/permission.js import store from /store export function hasPermission(permission) { if (!permission) return true const userPermissions store.getters.permissions || [] if (Array.isArray(permission)) { return permission.some(p userPermissions.includes(p)) } return userPermissions.includes(permission) }这个函数是整个权限系统的枢纽。路由守卫用它菜单过滤用它按钮指令也用它。把它写好后面所有地方都省心。这里有个细节permission参数可能是字符串也可能是数组函数要能兼容。另外如果传空默认返回true表示不需要权限这样调用方不用每次都判断。3.3 按钮级权限的三种实现方式按钮级权限是权限管理里最琐碎的部分。一个页面可能有十几个按钮每个按钮的权限要求都不一样。常见有三种做法。第一种是v-if手动判断。在模板里写v-ifhasPermission(order:delete)。这种方式最灵活但每个按钮都要写一遍代码里到处是权限判断可读性差。第二种是自定义指令。注册一个v-permission指令用法是v-permissionorder:delete。指令内部调用hasPermission没有权限就把元素从 DOM 里移除。这种方式比v-if干净推荐使用。// src/directives/permission.js import { hasPermission } from /utils/permission export default { inserted(el, binding) { const { value } binding if (!hasPermission(value)) { el.parentNode el.parentNode.removeChild(el) } } }第三种是封装权限组件比如Permission :valueorder:deletebutton删除/button/Permission。这种方式适合需要保留占位或者做更复杂逻辑的场景但用起来比指令啰嗦。我一般用指令为主个别复杂场景用组件。指令的inserted钩子只在元素插入时执行一次如果权限在运行时动态变化需要配合update钩子重新判断不过这种场景很少见。注意指令移除元素用的是removeChild如果元素本身有事件监听或者被其他逻辑引用移除后可能报错。更稳妥的做法是设置el.style.display none或者用v-if配合计算属性。实测下来直接移除在多数场景没问题但涉及第三方组件时要注意。4. 实操过程与核心环节实现4.1 登录后权限数据的获取与存储整个流程从登录开始。用户提交账号密码后端验证通过后返回 token 和用户信息。用户信息里最关键的是权限标识数组。// src/store/modules/user.js const actions { async login({ commit }, userInfo) { const { data } await login(userInfo) commit(SET_TOKEN, data.token) commit(SET_PERMISSIONS, data.permissions) commit(SET_ROLES, data.roles) return data } }拿到权限后存到 Vuex 的 state 里同时通过 getter 暴露出去。这里有个坑如果用户刷新页面Vuex 里的数据会丢失因为 Vuex 是内存存储。所以需要在应用初始化时用 token 重新拉一次用户信息把权限恢复回来。这一步通常在路由守卫的beforeEach里做。判断有没有 token有 token 但 store 里没有权限信息就调接口拉取拉取成功后再放行。如果拉取失败说明 token 失效跳回登录页。router.beforeEach(async (to, from, next) { const token store.getters.token if (!token) { if (to.path /login) { next() } else { next(/login) } return } const permissions store.getters.permissions if (permissions permissions.length) { next() } else { try { await store.dispatch(getUserInfo) next() } catch (e) { await store.dispatch(logout) next(/login) } } })这段守卫逻辑是权限系统的入口写的时候要特别注意死循环问题。比如getUserInfo失败后跳登录页登录页本身又触发守卫如果判断不当就会无限循环。所以登录页要单独放行。4.2 菜单的动态过滤与渲染菜单过滤发生在布局组件里。AntDesignPro of Vue 的侧边栏菜单是根据路由表生成的所以过滤路由就等于过滤菜单。// src/layouts/BasicLayout.vue computed: { menuData() { return filterMenuData(this.$router.options.routes) } } function filterMenuData(routes) { return routes.filter(route { if (route.meta route.meta.permission) { return hasPermission(route.meta.permission) } return true }).map(route { if (route.children) { route.children filterMenuData(route.children) } return route }) }这段递归过滤的逻辑要注意两点。一是过滤后要保留原始路由表不能直接改this.$router.options.routes否则下次过滤时数据已经被改过了。二是父级路由如果没有权限但子路由有权限处理方式要明确。我一般让父级只要有任意子级可见就保留否则整个父级隐藏。菜单渲染还有个细节是图标和标题。meta.title用来显示菜单名meta.icon用来显示图标。如果标题需要国际化meta.title存的是 i18n 的 key渲染时再翻译。4.3 路由守卫中的权限拦截菜单过滤只是让用户看不见但用户手动输入 URL 还是能访问。所以路由守卫里必须再做一次拦截。router.beforeEach((to, from, next) { const permission to.meta to.meta.permission if (permission !hasPermission(permission)) { next(/403) return } next() })这段逻辑要放在权限数据加载完成之后。如果权限还没拉回来就判断会把有权限的用户也拦下来。所以守卫的执行顺序很重要先确保权限数据就绪再做权限判断。403 页面是必须的用来告诉用户“你没权限”。有些项目会直接跳回首页但这样用户会困惑不如给个明确的提示页。提示路由守卫里不要做太多异步操作否则每次跳转都会卡顿。权限数据只在第一次跳转时拉取后续从 store 读这样性能最好。4.4 按钮权限指令的注册与使用指令注册在main.js里全局挂载import permission from /directives/permission Vue.directive(permission, permission)使用的时候直接在按钮上写a-button v-permissionorder:delete typedanger删除/a-button指令的inserted钩子在元素插入父节点时触发这时候判断权限并决定是否移除。如果按钮是在v-if或者列表渲染里动态生成的inserted也会正常触发。有个场景要注意如果按钮的权限依赖的数据是异步加载的比如权限列表在按钮渲染后才回来那inserted执行时权限还是空的按钮会被误删。解决办法是把权限数据放在路由守卫里提前加载好确保组件渲染时权限已经就绪。5. 常见问题与排查技巧实录5.1 刷新页面后菜单消失或权限失效这是最常见的问题根源是 Vuex 数据在刷新后丢失。表现是刷新页面后菜单变少或者直接跳到 403。排查思路先看路由守卫里有没有在权限为空时重新拉取用户信息。如果没有加上拉取逻辑。如果有检查拉取接口是否正常返回以及返回后有没有正确 commit 到 store。还有一种情况是拉取是异步的但守卫没有await导致权限还没写入就执行了判断。这种问题在开发环境因为接口快不容易发现生产环境接口慢就暴露了。5.2 动态路由刷新后 404如果用动态路由方案刷新页面时路由还没注册用户访问的路径匹配不到任何路由就会 404。解决办法是在路由守卫里判断如果路由表里没有当前路径先注册动态路由再next({ ...to, replace: true })重新导航。这个replace: true很关键避免在历史记录里留下多余条目。不过前面也说了我一般不建议用动态路由静态路由加权限过滤能避开这个坑。5.3 按钮权限指令不生效指令不生效通常有几个原因。一是指令没注册检查main.js里有没有Vue.directive。二是权限标识写错了和后端返回的对不上这种要打印出来比对。三是指令的钩子用错了inserted只在插入时执行如果元素是后来才插入的要用update或者componentUpdated。还有一种隐蔽情况是按钮被包裹在v-if里父级没渲染时指令不执行父级渲染后指令才执行这时候权限判断是正常的但如果权限数据此时还没到就会误判。5.4 权限标识的设计混乱很多项目后期权限难维护根源是权限标识一开始没设计好。比如有的用角色名有的用权限点有的用中文混在一起。建议统一用模块:操作的格式比如user:view、user:edit、user:delete。模块名和路由的 path 对应操作名用动词。这样一看就知道这个权限是管什么的。另外权限标识要集中管理不要散落在各个文件里。可以建一个src/constants/permission.js把所有权限点定义成常量用的时候引用常量而不是写字符串避免拼写错误。常见问题排查方向解决要点刷新后菜单消失Vuex 数据丢失守卫里重新拉取用户信息动态路由 404路由未注册守卫里注册后 replace 重导航按钮指令不生效指令未注册或标识错误检查注册和权限字符串权限标识混乱命名不统一统一格式并集中管理5.5 权限变更后的实时更新用户权限在运行中被管理员修改了前端怎么感知一般有两种做法。一是轮询定时拉取权限简单但浪费请求。二是用长连接推送复杂但实时。多数系统不需要实时用户重新登录或者刷新页面时更新就够了。如果确实需要实时可以在关键操作前再校验一次权限后端接口本身也要做权限校验前端只是体验层。注意前端权限永远只是体验优化真正的安全防线在后端。每个接口都要独立校验权限不能依赖前端传过来的权限标识。这一点在安全评审时经常被提到。6. 权限系统的扩展与维护建议6.1 权限点与角色的解耦早期项目容易把角色和权限点绑死比如代码里写if (role admin)。这样一旦角色调整代码就要改。正确做法是角色只作为权限点的集合代码里只判断权限点不判断角色。后端返回用户信息时可以返回角色也可以直接返回权限点。我倾向于直接返回权限点前端不关心角色只关心有没有某个权限。这样角色怎么变前端都不用动。6.2 权限配置的可视化系统做大之后权限配置会变成运维负担。这时候可以考虑做一个权限管理页面让管理员在界面上勾选角色对应的权限点。这个页面的数据存到后端前端登录时拉取。这个页面本身也需要权限控制只有超级管理员能进。实现上就是普通的增删改查但要注意权限点的树形结构展示以及保存时的批量处理。6.3 权限日志与审计对于敏感操作比如删除、导出除了权限控制还应该记录操作日志。日志里包含谁在什么时间做了什么操作。这部分通常在后端做前端只需要在调用接口时带上必要信息。前端能做的辅助是在按钮点击时先做一次权限校验校验不通过就不发请求减少无效请求。但这只是优化不能替代后端校验。6.4 多标签页与权限的配合AntDesignPro of Vue 有多标签页功能用户打开的页面会以标签形式保留。如果用户权限变了已经打开的标签页怎么处理一般是在权限变更后刷新页面或者关闭所有标签重新加载。实现上可以在权限更新后清空标签页缓存强制重新渲染。这部分逻辑在store/modules/tagsView.js里处理清空visitedViews和cachedViews即可。7. 我在实际项目里踩过的几个坑第一个坑是权限标识用了中文。早期图省事meta.permission直接写订单查看结果国际化的时候傻眼了而且后端返回的权限点是英文两边对不上。后来全部改成英文的模块:操作格式才理顺。第二个坑是路由守卫里做了太多事。一开始把用户信息拉取、菜单生成、权限判断全塞在beforeEach里导致每次跳转都要跑一遍页面切换明显卡顿。后来把能缓存的都缓存到 store守卫里只做必要的判断流畅多了。第三个坑是按钮权限指令直接移除 DOM 导致第三方组件报错。有个表格的操作列用了第三方组件指令移除按钮后组件内部还在引用这个节点控制台一直报错。后来改成用v-if配合计算属性虽然代码多写一点但稳定。第四个坑是忘了处理 403 页面本身的权限。403 页面如果也配了权限用户没权限访问 403就会无限重定向。所以 403、404、登录页这些基础页面一定要在守卫里放行不参与权限判断。这些坑说到底都是细节但权限系统恰恰是细节决定成败的地方。设计的时候多想一步后面就少改十行。AntDesignPro of Vue 给的骨架已经够用关键是把权限标识设计好、把守卫逻辑写稳、把边界情况考虑全。剩下的就是根据业务慢慢调没有一劳永逸的方案只有不断迭代的实践。

相关新闻

ponytail:当代职场的情绪标点与轻量协作语言

ponytail:当代职场的情绪标点与轻量协作语言

1. 项目概述:从“ponytail”这个词开始,我们到底在聊什么?“ponytail”这个词最近频繁出现在社交平台的评论区、弹幕和短视频标题里,但它早已不是字典里那个“马尾辫”的简单释义。我做内容运营和用户行为分析十多年,见…

2026/10/14 18:48:03 阅读更多 →
React Native应用难以破解?morphe-patches的Hermes字节码十六进制补丁与哈希修复技巧

React Native应用难以破解?morphe-patches的Hermes字节码十六进制补丁与哈希修复技巧

【免费下载链接】morphe-patches 🍃 Human-made patches for Morphe 项目地址: https://gitcode.com/gh_mirrors/mor/morphe-patches 点击查看 免费下载 morphe-patches 是一个为 Android 补丁工具 Morphe 提供社区补丁的开源仓库,其中最硬核…

2026/10/14 18:47:02 阅读更多 →
短链码生成:MurmurHash 与 62 进制转换

短链码生成:MurmurHash 与 62 进制转换

Guava 的 MurmurHash 测试与封装 简介:Guava 自带 MurmurHash 算法,直接调用生成 32 位哈希值,再封装到工具类 背景 短链码用 MurmurHash 生成,Guava、Jedis 等框架已经集成了这个算法,不用从头开发会用即可&#xf…

2026/10/14 18:47:02 阅读更多 →

最新新闻

小白速通嵌入式第三天——计算机硬件组成及主要部件功能

小白速通嵌入式第三天——计算机硬件组成及主要部件功能

小白速通嵌入式第三天——计算机硬件组成及主要部件功能 目录 计算机硬件组成总览(冯诺依曼结构)中央处理单元 CPU存储器总线输入/输出控制高频考点与易错点速记 1. 计算机硬件组成总览 1.1 冯诺依曼体系五大部件(背) 计算机硬…

2026/10/14 19:34:47 阅读更多 →
Cocos Creator JS-OC交互避坑指南:参数、线程与签名的三重校准

Cocos Creator JS-OC交互避坑指南:参数、线程与签名的三重校准

1. 为什么Cocos Creator里JS与OC交互不是“写个接口就完事”?在某跨平台游戏项目中,我接手了一个已上线半年的iOS版本,核心功能是玩家点击一个特殊道具后,调用原生相册选择图片并上传至服务器。当时代码里只有一行看似干净的调用&…

2026/10/14 19:34:47 阅读更多 →
航天器电子电气紧固件安装工艺与Q/W 1036B标准解析

航天器电子电气紧固件安装工艺与Q/W 1036B标准解析

1. 航天器紧固件安装工艺的特殊性航天器电子电气产品的紧固件安装工艺与普通工业领域有着本质区别。在太空环境中,微重力、极端温度变化(-150℃至150℃)、高真空和强辐射等严苛条件对紧固连接提出了近乎苛刻的要求。Q/W 1036B-2016标准正是针…

2026/10/14 19:34:47 阅读更多 →
考虑时序相关性的蒙特卡洛场景生成与削减实战解析

考虑时序相关性的蒙特卡洛场景生成与削减实战解析

先交代一个背景:前一阵子帮某高校研究团队做新能源出力场景库建设,他们手里有一批某风电场的数年逐小时出力数据,要做随机调度优化。起初大家用的都是常规套路——先拟合每个时刻的边缘分布,再独立蒙特卡洛采样生成一千条“日场景…

2026/10/14 19:34:47 阅读更多 →
SpringBoot智能CRM系统开发实践与架构设计

SpringBoot智能CRM系统开发实践与架构设计

1. 项目概述"SpringBoot客户关系智能管理系统"是一个基于SpringBoot框架开发的现代化客户关系管理解决方案。这类系统在当今数字化商业环境中扮演着至关重要的角色,特别是在销售自动化、客户服务优化和数据分析领域。我在实际企业级应用开发中发现&#x…

2026/10/14 19:34:47 阅读更多 →
基于Zafer-Liu/Data-Analysis-Agent的智能数据分析助手:构建自动化洞察与大模型交互实战指南

基于Zafer-Liu/Data-Analysis-Agent的智能数据分析助手:构建自动化洞察与大模型交互实战指南

基于Zafer-Liu/Data-Analysis-Agent的智能数据分析助手:构建自动化洞察与大模型交互实战指南 在数据驱动决策的今天,数据分析的门槛依然存在,特别是对于非技术背景的业务人员来说,编写SQL或Python脚本往往是一道难以逾越的鸿沟。G…

2026/10/14 19:33:47 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/14 7:53:34 阅读更多 →