Vue3后台管理-项目总结

持续更新中。。。

1. 动态路由

  1. 后台路由模型数据 (如果后端不知道怎么转为 这种树结构的路由,可以参考 普通数组转树结构的数组

    const dynamicRoutes = [
    	
    	    {
         
    	        path: '/',
    	        name: 'Layout',
    	        redirect: '/home',
    	        component: 'Layout',
    	        meta: {
         
    	            label: '',
    	            icon: '',
    	            hidden: false,
    	        },
    	        children: [
    	            {
         
    	                path: '/home',
    	                name: 'Home',
    	                component: 'Home',
    	                meta: {
         
    	                    label: '首页',
    	                    icon: 'HomeFilled',
    	                    hidden: false
    	                }
    	            },
    	        ]
    	    },
    	    {
         
    	        path: '/perm',
    	        name: 'perm',
    	        component: 'Layout',
    	        redirect: '/perm/user',
    	        meta: {
         
    	            label: '权限管理',
    	            icon: 'Lock',
    	            hidden: false
    	        },
    	        children: [
    	            {
         
    	                path: '/perm/user',
    	                name: 'user',
    	                component: 'User',
    	                meta: {
         
    	                    label: '用户管理',
    	                    icon: 'User',
    	                    hidden: false
    	                },
    	            },
    	            {
         
    	                path: '/perm/role',
    	                name: 'role',
    	                component: 'Role',
    	                meta: {
         
    	                    label: '角色管理',
    	                    icon: 'Avatar',
    	                    hidden: false
    	                },
    	            },
    	            {
         
    	                path: '/perm/menu',
    	                name: 'permMenu',
    	                component: 'PermMenu',
    	                meta: {
         
    	                    label: '菜单管理',
    	                    icon: 'Grid',
    	                    hidden: false
    	                },
    	            },
    	        ]
    	    },
    	    // 这个路由不能放到,常量路由里面,必须放到动态路由
    	    // 原因:在刷新页面时,动态路由丢失,需要重新加载动态路由,
    	    //      因为当前还没有动态路由,只能重定向到404,
    	    //      所以即使后面动态路由加载成功,也不会再去重定向到 动态路由
    	    {
         
    	        path: '/:pathMatcher(.*)*',
    	        redirect: '/404',
    	        name: 'any',
    	        meta: {
         
    	            label: '',
    	            icon: '',
    	            hidden: true
    	        }
    	    },
    	]
    
  2. 后台路由模型 转 前端需要的路由对象
    2.1 准备前端组件信息。这相当于是一个Map,在下面 转换 的时候会用到。

    import Layout from '@/layout/index.vue'
    import Home from '@/views/home/index.vue'
    import Screen from '@/views/screen/index.vue'
    import User from '@/views/perm/user/index.vue'
    import Role from '@/views/perm/role/index.vue'
    import PermMenu from '@/views/perm/menu/index.vue'
    
    export const allRouteComponents = {
         
        Layout,
        Home,
        Screen,
        User,
        Role,
        PermMenu,
    }
    

    2.2 转换操作

    // 将后端获取到的动态路由 添加到 router
    // 在路由守卫中使用,刷新页面时,重新添加动态路由,解决内存中路由丢失,白屏问题
    // 这里传入的路由数组就是后端传过来的
    const addRoutes = (routesArr) => {
         
        const routesObjArr = convertRoutes(routesArr)
        routesObjArr.forEach(item => {
         
            router.addRoute(item)
        })
    }
    
    // 将后端获取到的路由对象的 component(字符串类型) 转为 component(组件对象)
    const convertRoutes = (routesArr) => {
         
        return routesArr.map(item => {
         
            let routeObj = allRouteComponents[item.component]
            if (routeObj) {
         
                item.component = routeObj
            }
            if (item.children) {
         
                item.children = convertRoutes(item.children)
            }
            return item
        })
    }
    
  3. 动态路由添加 addRoutes()

    const userStore = useUserStore()
    const menuStore = useMenuStore()
    仓库函数的使用要写在 路由守卫里面,写在外面会报pinia没有激活错误。
    原因:
    1、 在项目启动时,仓库可能还没有被use,肯定会报错,
    2、 如果写在路由守卫里面,只有在切换路由的时候才会使用到仓库,这个时候仓库肯定已经被use了。

    registerFlag ,每次刷新页面这个值都会重新初始化为false,内存中路由也被清除。这个就需要重新添加 routes。添加routes后,registerFlag 置为 true。再执行路由切换不需要重复添加(只有刷新,才会重新触发)

    // 刷新页面时,重新添加动态路由
    const registerFlag = ref(false)
    
    router.beforeEach(async (to, from, next) => {
         
        const userStore = useUserStore()
        const menuStore = useMenuStore()
    
        // 判断用户是否登录
        if (userStore.token) {
         
            // 解决刷新页面,内存中路由丢失问题
            if (!registerFlag.value) {
         
                addRoutes(menuStore.userMenus)
                registerFlag.value = true
                // 添加完路由,重新访问目标页面
                await router.replace(to)
            }
            // 已经登录,访问/login,会重定向到 / (首页)
            if (to.path === '/login') {
         
                next('/home')
            } else {
         
                next()
            }
        } else {
         
            // 未登录
            if (to.path === '/login') {
         
                next()
            } else {
         
                // 重定向到/login,并追加一个 登录成功后重定向地址
                next({
         path: '/login', query: {
         redirect: to.path}})
            }
        }
    })
    

2. 动态侧边栏菜单

  1. 后端数据模型

    const dynamicRoutes = [
    
        {
         
            path: '/',
            name: 'Layout',
            redirect: '/home',
            component: 'Layout',
            meta: {
         
                label: '',
                icon: '',
                hidden: false,
            },
            children: [
                {
         
                    path: '/home',
                    name: 'Home',
                    component: 'Home',
                    meta: {
         
                        label: '首页',
                        icon: 'HomeFilled',
                        hidden: false
                    }
                },
            ]
        },
        {
         
            path: '/perm',
            name: 'perm',
            component: 'Layout',
            redirect: '/perm/user',
            meta: {
         
                label: '权限管理',
                icon: 'Lock',
                hidden: false
            },
            children: [
                {
         
                    path: '/perm/user',
                    name: 'user',
                    component: 'User',
                    meta: {
         
                        label: '用户管理',
                        icon: 'User',
                        hidden: false
                    },
                },
                {
         
                    path: '/perm/role',
                    name: 'role',
                    component: 'Role',
                    meta: {
         
                        label: '角色管理',
                        icon: 'Avatar',
                        hidden: false
                    },
                },
                {
         
                    path: '/perm/menu',
                    name: 'permMenu',
                    component: 'PermMenu',
                    meta: {
         
                        label: '菜单管理',
                        icon: 'Grid',
                        hidden: false
                    },
                },
            ]
        },
        // 这个路由不能放到,常量路由里面,必须放到动态路由
        // 原因:在刷新页面时,动态路由丢失,需要重新加载动态路由,
        //      因为当前还没有动态路由,只能重定向到404,
        //      所以即使后面动态路由加载成功,也不会再去重定向到 动态路由
        {
         
            path: '/:pathMatcher(.*)*',
            redirect: '/404',
            name: 'any',
            meta: {
         
                label: '',
                icon: '',
                hidden: true
            }
        },
    ]
    
  2. 后端路由数据–>过滤出hidden==false(用于侧边栏展示的)

    <script setup>
    import AsideMenu from '@/layout/menu/index.vue'
    import {
           computed,defineOptions} from "vue";
    defineOptions({
           name: 'Layout'})
    
    // 去 hidden 函数
    // 如果父路由 隐藏,那么子路由hidden不论true,false,都不会显示
    // 如果父路由 显示,那么子路由hidden为false会显示,hidden为true会隐藏
    const filterHiddenRoute = (routes) => {
           
      return routes.filter(item => {
           
        if (!item.meta.hidden) {
           
          if (item.children) {
           
            item.children = filterHiddenRoute(item.children)
          }
          return true
        } else {
           
          return false
        }
      })
    }
    // 去掉hidden后的菜单树
    // 这里传入的routes,就是后端传来的路由数组(树结构)
    const menuList = computed(() => {
           
      return filterHiddenRoute(routes)
    })
    </script>
    <template>
       <el-menu>
         <aside-menu :menuList="menuList"></aside-menu>
       </el-menu>
    </template>
    
  3. 动态菜单递归组件定义

    递归菜单必须有组件名,用于在组件中调用自身

    <script setup>
    import router from '@/router'
    
    defineOptions({
           name: 'AsideMenu'})
    const menus = defineProps(['menuList'])
    
    // 跳转路由
    const goRoute = (e) => {
           
      router.push(e.index)
    }
    </script>
    
    <template>
      <template v-for="item of menus.menuList" :key="item.path">
        <!--     没有子菜单-->
        <el-menu-item v-if="!item.children" :index="item.path" @click="goRoute">
          <el-icon>
            <component :is="item.meta.icon"></component>
          </el-icon>
          <template #title>
            <span>{
        { item.meta.label }}</span>
          </template>
        </el-menu-item>
        <!--     有子菜单,但是只有1个-->
        <el-menu-item v-if="item.children && item.children.length===1" :index="item.children[0].path" @click="goRoute">
          <el-icon>
            <component :is="item.children[0].meta.icon"></component>
          </el-icon>
          <template #title>
            <span>{
        { item.children[0].meta.label }}</span>
          </template>
        </el-menu-item>
        <!--     有子菜单,子菜单大于1个-->
        <el-sub-menu v-if="item.children && item.children.length>1" :index="item.path">
          <template #title>
            <el-icon>
              <component :is="item.meta.icon"></component>
            </el-icon>
            <span>{
        { item.meta.label }}</span>
          </template>
          <AsideMenu :menuList="item.children"></AsideMenu>
        </el-sub-menu>
      </template>
    </template>
    <style scoped lang="scss">
    </style>
    

相关推荐

  1. Vue3后台管理-项目总结

    2023-12-15 04:46:06       42 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-15 04:46:06       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-15 04:46:06       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-15 04:46:06       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-15 04:46:06       18 阅读

热门阅读

  1. vue3制作类微信的六位的密码输入框

    2023-12-15 04:46:06       29 阅读
  2. B - Team Gym - 102801B ( 网络流问题)

    2023-12-15 04:46:06       39 阅读
  3. 在浏览器中存储数组和对象(js的问题)

    2023-12-15 04:46:06       36 阅读
  4. centos7配置国内源

    2023-12-15 04:46:06       35 阅读
  5. Python基础List列表定义与函数

    2023-12-15 04:46:06       41 阅读
  6. 【Python】正则表达式

    2023-12-15 04:46:06       34 阅读
  7. 在MFC(Microsoft Foundation Classes)中 CreateThread函数

    2023-12-15 04:46:06       33 阅读
  8. CSS BFC详解

    2023-12-15 04:46:06       37 阅读
  9. C#教程(二):继承

    2023-12-15 04:46:06       34 阅读
  10. Kotlin 中的 `as` 关键字:类型转换的艺术

    2023-12-15 04:46:06       35 阅读