当前位置: 首页 > news >正文

v3-admin-vite 改造自动路由,view页面自解释Meta

需求

v3-admin-vite是一款不错的后端管理模板,主要是pany一直都在维护,最近将后台管理也进行了升级,顺便完成一直没时间解决的小痛痒:

在不使用后端动态管理的情况下。我不希望单独维护一份路由定义,我希望页面是自解释的。就像HTML标记,一个页面的标题等信息由页面内<title>决定,而一个页面的访问地址(路由)由页面目录决定。很自然的思维是么?单独维护一份路由感觉就没那么自然了,我希望这一切都由页面自解释。访问路径我只需要移动页面的位置,Ctrl+CV目录的结构就好了

思路

之前实战过数个项目,大部分都轻车熟路了。但是对于v3-admin-vite系统,还是有几个地方需要调整 :

1.目录的定义

目录的定义除了名称外,还有图标等信息需要管理。因此需要采用文件补充信息,我的解决方案是将要输出成为左侧目录结构的目录(好绕口)下放一个index.ts文件,为了避免和其它的文件冲突,约定默认导出export default必须包含title这个string信息,表示目录名称(神马?title为空怎么办?有点正常业务思维吧),顺便把图标的定义也在导出解决。

2.View文件的定义

View文件的定义由于目录定义一样,只需要将你导出成为菜单的vue模块添加导出定义即可。把meta信息导出,自动输出路由配置。

3.Name约定

除了meta信息以外,admin-v3还要求name不能一致(没试过 ?改个一样的试试看😏),我们可以直接从文件名读取,至少一个目录下文件名是不会一致的。当然如果多个目录的话,就要注意一下了,功能页面名称唯一这个应该很容易办到。

4.递归扫描文件

webpack,vite等工具都提供了文件扫描的接口,只是不能使用变量进行路径扫描,必须字面量(常量),好在支持通配符。解决起来不难。

5.顺序问题的解决

由于工具扫描都是基于文件名称的,而实际需要显示的结构和文件顺序 不一定相同,例如我有a.vue,b.vue,按名称扫描a会出现在前面。因此我扩充了一下Meta定义,添加了一个position属性 ,没设置时,默认以100作为排序值,根据其对所有的目录递归排序,这样就OK了。

功能实现

看一下最终的对应效果

对于目录标记,我们只需要在目录下添加一个index.ts文件:

import { RouteMetaEx } from '@/router'export default {title: '二级目录测试',
} as RouteMetaEx

对于View模块,我们只需要添加多一个typescript块导出meta:

<template><div>测试节点3</div>
</template>
<script lang="ts">
import { RouteMetaEx } from '@/router'
const meta: RouteMetaEx = {title: '3级节点1', // 只有导出title的才会成为路由elIcon: 'Cpu', // element-ui的内置ICON,比svgIcon优先// svgIcon: 'dashboard',roles: ['role0'], // 哪些角色可以显示position: 100//keepAlive: true // 是否要keepAlive保持页面状态// hidden: true 默认为false,不会挂载到菜单
}
export default meta
</script>

使用是不很简单?哈哈哈哈。

这里添加了position的RouteMetaEx在后面有定义,其它结构和功能和Meta定义一致。注意vue的文件名在view下要唯一。

然后我们的src/router/index.ts里dynamicRoutes需要按照下面方式来导出:


export interface RouteMetaEx extends RouteMeta {position?: number //排序,不填写的话默认为100,用于控制菜单顺序
}/*** admin-vite-v3 自动路由* 递归扫描views下的文件,识别导出title的页面加入路由,需要配置权限 (Roles 属性)* 注意二级目录产生要求在目录index.ts里导出含title的meta* @author Jim 2024/4/1*/
const autoRoutes: Array<RouteRecordRaw> = []const scanDir: Record<string, any> = import.meta.glob('../views/**/index.ts', { eager: true }) // 处理目录
const dirNodeCache = new Map<string, RouteRecordRaw>()
for (const key in scanDir) {const component = scanDir[key]if (component.default?.title) {// 通过默认导出title判断const groups = /\.\.\/views\/((\w+\/)+)index\.ts/.exec(key) || []const dirName = groups[2].slice(0, -1) // 提取目录名const perfix = groups[1].slice(0, -dirName.length - 1) // 提取前缀目录const currentNode: RouteRecordRaw = {path: dirName,name: dirName,children: [],meta: { ...component.default, alwaysShow: true } // 合并alwaysShow进去,保持目录结构}const upperNode = dirNodeCache.get(perfix)if (upperNode) {upperNode.children?.push(currentNode)} else {// 一级目录currentNode.path = `/${dirName}` // 更改根格式currentNode.component = LayoutsautoRoutes.push(currentNode)}dirNodeCache.set(groups[1], currentNode)}
}
const scanModule: Record<string, any> = import.meta.glob('../views/**/*.vue', { eager: true }) // 处理节点
for (const key in scanModule) {const component = scanModule[key]if (component.default?.title) {// 通过默认导出title判断const groups = /\.\.\/views\/((\w+\/)*)(\w+)\.vue/.exec(key) || []const dirPath = groups[1]const moduleName = groups[3]if (!dirPath) {// 一级菜单特殊处理autoRoutes.push({path: `/${moduleName}`,name: moduleName,component: Layouts,redirect: `/${moduleName}/index`,meta: component.default,children: [{path: 'index',name: moduleName,component: () => component,meta: component.default}]})} else {const currentNode: RouteRecordRaw = {path: moduleName,name: moduleName,component: () => component,meta: component.default}const upperNode = dirNodeCache.get(dirPath)if (upperNode) {// 挂上级目录下,没有定义就不要挂了upperNode.children?.push(currentNode)} else {console.error(`upper node ${dirPath} not found`)}}}
}const sortFunc = (a: RouteRecordRaw, b: RouteRecordRaw) =>((a.meta as RouteMetaEx).position ?? 100) - ((b.meta as RouteMetaEx).position ?? 100)
function sortByPosition(nodes: RouteRecordRaw[]) {nodes.forEach((node: RouteRecordRaw) => {if (node.children) {const sortedChildren = node.children.sort(sortFunc) // 排序所有childrensortByPosition(sortedChildren)}})
}
autoRoutes.sort(sortFunc)
sortByPosition(autoRoutes)export const dynamicRoutes: RouteRecordRaw[] = autoRoutes

其他部分可以不用动。这样我们便有了路由导出的功能。

副作用

这样处理虽然开发方便,但是也有它的局限和副作用。副作用就是分包问题,由于所有的模块获取菜单定义必须读文件,因此无法懒加载,会导致扫描过程需要加载全部的模块,这样一来当模块非常多的时候加载会比较耗资源,也无法细化的分包。但对于中小项目一个gzip包全部load下来还是问题不大。大型项目可能要采用其它方案例如自动化脚本来输出路由。

相关文章:

v3-admin-vite 改造自动路由,view页面自解释Meta

需求 v3-admin-vite是一款不错的后端管理模板&#xff0c;主要是pany一直都在维护&#xff0c;最近将后台管理也进行了升级&#xff0c;顺便完成一直没时间解决的小痛痒&#xff1a; 在不使用后端动态管理的情况下。我不希望单独维护一份路由定义&#xff0c;我希望页面是自解…...

FIFO存储器选型参数,结构原理,工艺与注意问题总结

🏡《总目录》 目录 1,概述2.1,写入操作2.2,读取操作2.3,指针移动与循环2.4,状态检测3,结构特点3.1,双口RAM结构3.2,无外部读写地址线3.3,内部读写指针自动递增3.4,固定深度的缓冲区4,工艺流程4.1,硅晶圆准备...

jvm高级面试题-2024

说下对JVM内存模型的理解 JVM内存模型主要是指Java虚拟机在运行时所使用的内存结构。它主要包括堆、栈、方法区和程序计数器等部分。 堆是JVM中最大的一块内存区域&#xff0c;用于存储对象实例。一般通过new关键字创建的对象都存放在堆中&#xff0c;堆的大小可以通过启动参数…...

DeepL Pro3.1 下载地址及安装教程

DeepL Pro是DeepL公司推出的专业翻译服务。DeepL是一家专注于机器翻译和自然语言处理技术的公司&#xff0c;其翻译引擎被认为在质量和准确性方面表现优秀.DeepL Pro提供了一系列高级功能和服务&#xff0c;以满足专业用户的翻译需求。其中包括&#xff1a; 高质量翻译&#xf…...

第十一届 “MathorCup“- B题:基于机器学习的团簇能量预测及结构全局寻优方法

目录 摘 要 第 1 章 问题重述 1.1 问题背景 1.2 问题描述 第 2 章 思路分析...

云计算探索-如何在服务器上配置RAID(附模拟器)

一&#xff0c;引言 RAID&#xff08;Redundant Array of Independent Disks&#xff09;是一种将多个物理硬盘组合成一个逻辑单元的技术&#xff0c;旨在提升数据存取速度、增大存储容量以及提高数据可靠性。在服务器环境中配置RAID尤其重要&#xff0c;它不仅能够应对高并发访…...

LeetCode226:反转二叉树

题目描述 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 解题思想 使用前序遍历和后序遍历比较方便 代码 class Solution { public:TreeNode* invertTree(TreeNode* root) {if (root nullptr) return root;swap(root->left, root…...

特征融合篇 | 利用RT-DETR的AIFI去替换YOLOv8中的SPPF(附2种改进方法)

前言:Hello大家好,我是小哥谈。RT-DETR模型是一种用于目标检测的深度学习模型,它基于transformer架构,特别适用于实时处理序列数据。在RT-DETR模型中,AIFI(基于注意力的内部尺度特征交互)模块是一个关键组件,它通过引入注意力机制来增强模型对局部和全局信息的处理能力…...

MVCC多版本并发控制

1.什么是MVCC MVCC &#xff08;Multiversion Concurrency Control&#xff09;&#xff0c;多版本并发控制。MySQL通过MVCC来实现隔离性。隔离性本质上是因为同时存在多个并发事务可能会导致脏读、幻读等情况。要解决并发问题只有一种方案就是加锁。当然&#xff0c;锁不可避免…...

图片转换成base64如何在html文件中使用呢

在HTML文件中使用Base64编码的图片非常简单。Base64编码是一种将二进制数据转换为ASCII字符串的方法&#xff0c;这使得可以直接在网页上嵌入图片数据&#xff0c;而无需引用外部图片文件。以下是如何在HTML中使用Base64编码的图片的步骤&#xff1a; 步骤 1: 将图片转换为Bas…...

【MATLAB源码-第24期】基于matlab的水声通信中海洋噪声的建模仿真,对比不同风速的影响。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 水声通信&#xff1a; 水声通信是一种利用水中传播声波的方式进行信息传递的技术。它在水下环境中被广泛应用&#xff0c;特别是在海洋科学研究、海洋资源勘探、水下军事通信等领域。 1. **传输媒介**&#xff1a;水声通信利…...

七、函数的使用方法

函数的调用 nameinput&#xff08;&#xff09;#输入参数并赋值name print&#xff08;name&#xff09;#d打印name 格式&#xff1a;返回值函数名&#xff08;参数&#xff09; def get_sum(n):#形式参数计算累加和:param n::return: sumsum0for i in range(1,n1):sumiprint…...

数据分析之Tebleau 简介、安装及数据导入

Tebleau简介 Tebleau基于斯坦福大学突破性交互式技术 可以将结构化数据快速生成图表、坐标图、仪表盘与报告 将维度拖放到画布等地方是他的主要操作方式 例&#xff1a;Tebleau是手机相机 &#xff08;相对来说更简单&#xff09; POWER BI是单反相机 Tebleau各类产品 Teblea…...

分享一下设计模式的学习

分享一下设计模式的学习 1、什么是设计模式&#xff1f; 设计模式是在软件设计过程中&#xff0c;经过实践和总结得出的描述、解决软件设计问题的一种经验总结。它是一种被广泛接受和验证的最佳实践&#xff0c;用于解决特定类型的问题&#xff0c;并提供了可重用的解决方案。…...

【JavaEE初阶系列】——CAS

目录 &#x1f388;什么是 CAS &#x1f4dd;CAS 伪代码 &#x1f388;CAS 是怎么实现的 &#x1f388;CAS 有哪些应用 &#x1f6a9;实现原子类 &#x1f308;伪代码实现: &#x1f6a9;实现自旋锁 &#x1f308;自旋锁伪代码 &#x1f388;CAS 的 ABA 问题 &#…...

webGIS 之 智慧校园案例

1.引入资源创建地图 //index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content&qu…...

CVAE——生成0-9数字图像(Pytorch+mnist)

1、简介 CVAE&#xff08;Conditional Variational Autoencoder&#xff0c;条件变分自编码器&#xff09;是一种变分自编码器&#xff08;VAE&#xff09;的变体&#xff0c;用于生成有条件的数据。在传统的变分自编码器中&#xff0c;生成的数据是完全由潜在变量决定的&…...

【Linux】TCP网络套接字编程+守护进程

文章目录 日志类&#xff08;完成TCP/UDP套接字常见连接过程中的日志打印&#xff09;单进程版本的服务器客户端通信多进程版本和多线程版本守护进程化的多线程服务器 日志类&#xff08;完成TCP/UDP套接字常见连接过程中的日志打印&#xff09; 为了让我们的代码更规范化&…...

【Qt 学习笔记】Day2 | 认识QtSDK中的重要工具

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Day2 | 认识QtSDK中的重要工具 文章编号&#xff1a;Qt 学习笔记 / 03…...

adc123456

DMA主要用于协助CPU完成数据转运的工作 DMA&#xff0c;英文全称Direct Memory Access&#xff0c;DMA这个外设是可以直接访问STM32内部存储器的&#xff0c;包括运行内存SRAM&#xff0c;程序存储器flash和寄存器等等&#xff0c;DMA都有权限访问&#xff0c;所以DMA能完成数据…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统

目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索&#xff08;基于物理空间 广播范围&#xff09;2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...