Vite4 + Vue3 + vue-router4 动态路由

news2024/11/17 13:34:35

动态路由,基本上每一个项目都能接触到这个东西,通俗一点就是我们的菜单是根据后端接口返回的数据进行动态生成的。表面上是对菜单的一个展现处理,其实内部就是对router的一个数据处理。当然你只对菜单做处理也是可以的,但是没有任何意义,熟悉router的小伙伴都知道,如果你的一个路由存在,即使没有这个菜单,我只要改变浏览器的地址一样能访问到。所以你还是省不了修改router的步骤。通过接口获取数据的话就可以根据角色权限或者一些业务上的需求,根据不同属性实现路由的划分。达到不同的页面渲染效果。

本文只是讲解菜单的权限控制,不到按钮级别。其实按钮也是差不多的。可以设置一个属性表示菜单,一个属性表示按钮,每一个菜单的叶子节点上都包含根据权限返回的按钮数组。接着可以通过组件的形式去输出相应的按钮就可以。

一、搭建项目 😛😛😛

这里我已经提前搭建好了 Vite4+Pinia2+vue-router4+ElmentPlus搭建Vue3项目(组件、图标等按需引入)

二、根据上面链接搭建好项目,修改src/router/index.ts 😁 😉

asyncRoutes里面可以存放默认的一些路由,比如登录、404、403这些。由于我只是演示,所以就啥都不放了。清晰明朗一点。

import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'

export const asyncRoutes: RouteRecordRaw[] = []

const router = createRouter({
    history: createWebHashHistory(),
    routes: asyncRoutes,
    scrollBehavior: () => ({ left: 0, top: 0 })
})

export default router

三、创建 'src/layout/index.vue' 文件 😁 😉

这个文件就是整个项目的布局,一般我们常见的项目都分为上下结构,就如下图。导航和菜单部分基本上是用户登录以后就已经确定好了,点击菜单的时候去切换路由。我这里由于这部分不是重点,所以我就很潦草的画了一个很简单的页面。

<template>
  <div style="padding: 100px;">
    <div>
      <div v-for="(item, index) in menus[0].children" :key="index" style="margin-bottom: 20px;">
        <router-link :to="item.path">{{item.title}}</router-link>
      </div>
    </div>
    <div>
      <router-view #default="{route, Component}">
        <transition leave-from-class="ts-web-fade--leave-to" enter-active-class="animate__animated animate__bounceInRight">
          <component :is="Component"></component>
        </transition>
      </router-view>
    </div>
  </div>
</template>


<script lang="ts">
import appStore from '@/pinia';

export default defineComponent({
  setup() {

    const { menus } = appStore.permissionModule

    console.log(menus, 'menus')

    return {
      menus,
    }
  }
})
</script>

四、创建 'src/pinia/modules/permission.ts' 文件 😁 😉

由于我这里是一个demo,没有真正的去接入后端。所以我暂时放入的静态数据。自己替换成接口返回就好。

import { defineStore } from 'pinia';
import router from '@/router'
// 这是整个项目的布局页面。根据自己的项目替换就好
import Layout from "@/layout/index.vue";
import {RouteRecordRaw} from "vue-router";

export type MenuType = {
    path: string,
    title: string,
    component: string,
    redirect?: string,
    children?: Array<MenuType>
}

type RouterType = RouteRecordRaw & {
    hidden?: boolean;
    alwaysShow?: boolean;
}

export interface IPermissionState {
    routes: RouterType[]
    dynamicRoutes: RouterType[]
    menus: Array<MenuType>
}

function hasPermission<T>(roles: T[], route: RouterType) {
    if (route.meta && route.meta.roles) {
        return roles.some((role) => (route.meta?.roles as T[]).includes(role));
    }
    return true
}

const modules = import.meta.glob('../../views/**/*.vue')
const _import = (path: string) => () => import(`../../views/${path}.vue`)

const assembleRouter = (routers: any) => {
    const addRouter = routers.filter((router: any) => {
        (router.title && router.icon) && (router.meta = {
            title: router.title,
            // icon: router.icon,
            // alwaysShow: router.alwaysShow || false,
            // affix: router.affix || false,
        })
        if (router.component === 'Layout') {
            router.component = shallowRef(Layout)
        } else {
            if (import.meta.env.MODE === 'development') {
                router.component = _import(router.component)
            } else {
                router.component = modules[`../../views/${router.component}.vue`]
            }
        }
        if (router.children && router.children.length) {
            router.children = assembleRouter(router.children)
        }
        return true

    })
    return addRouter
}

export function filterAsyncRoutes(routes: RouterType[], roles: string[]) {
    const res: RouterType[] = []

    routes.forEach((route) => {
        const tmp = { ...route }
        if (hasPermission<string>(roles, tmp)) {
            if (tmp.children) {
                tmp.children = filterAsyncRoutes(tmp.children, roles)
            }
            res.push(tmp)
        }
    })
    return res
}

export const permissionModule = defineStore({
    id: 'permission',
    state(): IPermissionState{
        return {
            routes: [],
            dynamicRoutes: [],
            menus: []
        }
    },
    actions:{
        async getMenus() {
            try {
                // 这里由于不方便演示,所以我写的静态数据。换着自己对于的接口就好
                const list:MenuType[] = [
                    {
                        path: '/',
                        title: 'ts-super-web',
                        component: 'Layout',
                        redirect: '/home',
                        children: [
                            {
                                title: 'home',
                                path: 'home',
                                component: 'home'
                            },
                            {
                                title: 'home1',
                                path: 'home1',
                                component: 'home1'
                            }
                        ]
                    }
                ]
                this.menus = list
                // 组件路由
                let addRouter = assembleRouter(this.menus)
                // addRouter = assembleRouterDelete(addRouter)
                // 动态添加菜单
                addRouter.forEach((ts: any) => {
                    router.addRoute(ts)
                })
            } catch (err) {
                return Promise.reject(err);
            }
        }
    }
})

五、路由拦截器 😁 😉 😜

因为上面说到我没有真正的接入后端,所以这里我也没有进行token判断。自行增加一下就好,比较简单。除了token还可以在拦截器里面放置一个白名单列表,对于白名单里面的路由我们不做拦截。比如login登录页面、404等等。根据自己需求配置就好。

import router from '@/router'
// @ts-ignore
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import appStore from "@/pinia";

NProgress.configure({
    easing: 'ease', // 动画方式
    showSpinner: true, // 是否显示加载ico
    trickleSpeed: 200, // 自动递增间隔
    minimum: 0.4, // 更改启动时使用的最小百分比
})

router.beforeEach(async (to, form, next) => {
    // 这里处理自己的逻辑,比如需要登录以后才能访问其他页面等等
    NProgress.start()
    const { menus, getMenus } = appStore.permissionModule
    if (menus.length === 0) {
        try {
            // 调用接口获取菜单 进行跳转
            await getMenus()
            next({ ...to, replace: true })
        } catch (err) {
            next()
        }
    } else {
        next()
    }
    NProgress.done()
})

六、修改app.vue

<template>
  <router-view />
</template>

<style>
html,body,#app {
  height: 100%;
  width: 100%;
  margin: 0;
  padding: 0;
}
</style>

七、src/views下的两个home文件进行一下修改

home 这两个文件不修改也不影响。

<template>
  <p style="font-size: 32px;">你好,我是home</p>
</template>

home1 这两个文件不修改也不影响。

<template>
  <p style="font-size: 32px;">你好,我是home1</p>
</template>

八、效果浏览

我是Etc.End。如果文章对你有所帮助,记得帮我点个免费的赞和收藏。同时欢迎各位小伙伴一起学习,一起成长👉👉WX:SH--TS。🍓 🍑 🍈 🍌 🍐 🍍 🍠 🍆 🍅 🌽 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/409841.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

js中的内存泄漏

简版 内存泄漏一般是指变量的内存没有及时的回收&#xff0c;导致内存资源浪费。一般有三种情况出现内存泄露比较多。&#xff08;1&#xff09;常见的声明了一个全局变量&#xff0c;但是又没有用上&#xff0c;那么就有点浪费内存了&#xff0c;&#xff08;2&#xff09;定…

【Vue3】用Element Plus实现列表界面

&#x1f3c6;今日学习目标&#xff1a;用Element Plus实现列表界面 &#x1f603;创作者&#xff1a;颜颜yan_ ✨个人格言&#xff1a;生如芥子&#xff0c;心藏须弥 ⏰本期期数&#xff1a;第四期 &#x1f389;专栏系列&#xff1a;Vue3 文章目录前言效果图目录简介修改vite…

uniapp微信小程序实现连接低功耗蓝牙打印功能

微信小程序项目中有使用到蓝牙连接打印&#xff0c;参考官方文档做了一个参考笔记&#xff0c;这样使用的时候就按着步骤查看。 uni-app蓝牙连接 蓝牙&#xff1a; 1、初始化蓝牙 uni.openBluetoothAdapter(OBJECT) uni.openBluetoothAdapter({success(res) {console.log(…

毫米波雷达的硬件架构与射频前端

说明 本篇博文梳理(车载)毫米波雷达的系统构成&#xff0c;特别地&#xff0c;对其射频前端各部件做细节性的原理说明。本篇博文会基于对这方面知识理解的加深以及读者的反馈长期更新内容和所附资料&#xff0c;有不当之处或有其它有益的参考资料可以在评论区给出&#xff0c;我…

vue3.x结合element-plus如何使用icon图标

基于 Vue 3的Element Plus如何使用icon图标 首先注意Element Plus版本&#xff1a;官网如图所示&#xff0c; 基于vue3的具体如何使用&#xff1a; 参考官网文档&#xff1a; 1.首先选择一种方式安装 2.然后全局注册图标 在main.js或main.ts文件中引入&#xff1a; import …

【web前端开发】CSS文字和文本样式

文章目录前言字体大小字体粗细字体样式字体font复合属性文本缩进文本水平对齐方式文本修饰线行高颜色标签水平居中⭐思维导图前言 本篇文章主要讲解CSS中的文字和文本样式及一些实用技巧.文章最后有思维导图. 字体大小 属性名:font-size 取值: 数字px 注意: 谷歌浏览器默认…

无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri:[http://java.sun.com/jsp/jstl/core]

这是我在学习 JSTL 时产生的错误。没有使用 Maven 。 是手动引入 JSTL 包&#xff08;jstl.jar 和 standard.jar&#xff09;。在请求转发到 .jsp 界面&#xff0c;再没有使用 JSTL 语句前是通的&#xff0c;使用 JSTL 后就会报错。org.apache.jasper.JasperException: 无法在w…

【Vue】filter 过滤器用法

文章目录一、全局过滤器二、局部过滤器一、全局过滤器 语法&#xff1a; Vue.filter(name,function) - name: 过滤器名称 - function: 执行函数&#xff0c;必须有一个过滤后的返回值使用方法&#xff1a;变量名 | 过滤器名称 {{name | function()}}示例&#xff1a; <!D…

webpack的安装

webpack是当前前端最热门的前端资源模块化管理和打包工具。 安装webpack 首先要安装Node.js&#xff0c;Node.js自带了软件包管理工具npm&#xff0c;可以使用npm安装各种插件。Webpack至少需要Node.js v0.6版本&#xff0c;直接安装最新版就好了。 1.nodeJs官网&#xff0c…

Luckysheet本地导入时报错‘luckysheet‘ is not defined【已解决】

本文包含luckysheet本地安装以及报错解决&#xff0c;性子急可以直接按照目录划拉&#xff01; 目录 一、本地导入 二、报错luckysheet is not defined解决 最近正在开发的项目中需要类excel功能的东西&#xff0c;经过调研后决定用luckysheet&#xff0c;Luckysheet在线表格…

vite + vue3 —— vue地图大屏项目

​回顾 前期 ​ 前端利器 —— 提升《500倍开发效率》 传一张设计稿&#xff0c;点击一建生成项目 好牛_0.活在风浪里的博客-CSDN博客如果非要说它有什么缺点&#xff0c;那么我觉得就是它会&#xff0c;让你cv大法都没处使&#xff01;&#xff01;&#xff01;比如…

React 面向组件编程(下)

目录前言&#xff1a;一、受控组件与非受控组件1. 受控组件2. 非受控组件3. 效果展示4. 总结&#xff1a;二、组件的生命周期1. 对生命周期的理解2. 生命周期的三个阶段&#xff08;旧&#xff09;3. 生命周期的三个阶段&#xff08;新&#xff09;4. 新旧生命周期的区别5. 重要…

微信小程序项目实例——扫雷

今日推荐&#x1f481;‍♂️ 2023许嵩演唱会即将到来&#x1f3a4;&#x1f3a4;&#x1f3a4;大家一起冲冲冲&#x1f3c3;‍♂️&#x1f3c3;‍♂️&#x1f3c3;‍♂️ &#x1f52e;&#x1f52e;&#x1f52e;&#x1f52e;&#x1f52e;往期优质项目实例&#x1f52e…

JavaWeb之Cookie详解(一)

一.Cookie简介 Cookie是在HTTP协议下&#xff0c;服务器维护客户工作站上信息的一种方式&#xff0c;其作用是维护服务端与客户端浏览器的会话状态。由于HTTP协议的特点是无状态性&#xff0c;它对于事务处理没有记忆能力&#xff0c;所以服务端不会记录当前客户端浏览器的访问…

image-conversion 图片压缩,vue

image-conversion &#xff0c;vue&#xff0c;element 需求&#xff1a;需要用户上传图片时候&#xff0c;对大尺寸图片进行压缩&#xff0c;减小图片大小&#xff0c;减少服务器的压力。 用到的第三方插件image-conversion 文档&#xff1a; https://www.npmjs.com/packag…

vue2 前端导出el-table表格为Excel文件 (自带样式)

前话&#xff1a;我只是个自学到工作的小菜鸟&#xff0c;如果有大佬有更好的方法&#xff0c;希望不吝赐教。 1.电脑环境 node版本 14.20.0 vue-cli 4.4.0 (如果你的项目是vue-cli 5 版本&#xff08;即webpack5&#xff09;的会有很多问题&#xff0c;解决办法写在后边)…

echarts:实现3D地图版块叠加动效散点+轮播高亮效果

需求描述 如下图所示&#xff0c;展示3D效果的地图版块&#xff0c;并叠加显示动效散点&#xff1a; 实现思路 首先是3D地图版块效果的实现&#xff0c;可以参考广州3D地图&#xff1b;而动效散点的实现&#xff0c;可以参考地图发散分布。 这里再提一个经过尝试并不行的思…

大学生web前端期末大作业实例代码 (1500套,建议收藏) HTML+CSS+JS

文章目录&#x1f4da;web前端期末大作业 (1500套) 集合一、网页介绍二、网页集合&#x1f48c;表白网页 125套 (集合)&#x1f499;Echarts大屏数据展示 150套 (集合)一、基于HTMLEcharts技术制作二、基于VUEEcharts技术制作&#x1f381;更多源码&#x1f4da;web前端期末大作…

统计网站访问量(自己开发)

网站的运营者或者开发者肯定都想知道&#xff0c;自己网站每天的使用情况。比如每天有多少访问量、用户都点击哪些功能、用户对网站上哪些功能感兴趣。知道了这些信息才能定位网站后面的开发方向&#xff0c;比如持续优化点击率高的模块 、裁撤用户不感兴趣的模块&#xff0c;打…

【uniapp】上拉触底加载更多的两种方式,组件里面和页面里面 scroll-view组件和onReachBottom触底方法,以及部分安卓机型scroll-view很难触发问题,且可以固定表头

一般的话scroll-view在组件里面使用,而页面里面则使用onReachBottom()这个方法居多 1,onReachBottom 上拉触底后加载更多(未固定表头) 页面结构: <view class"box"><table border"0"><thead><th>名称</th><th style&qu…