Vue3后台管理-项目总结

持续更新中。。。

1. 动态路由

前端路由配置信息

export const constantRoutes = [
    {
        path: '/login',
        component: () => import('../views/login/index.vue'),
        name: 'login',
        meta: {
            label: '登录页面',
            icon: '',
            hidden: true,
        }
    },
    {
        path: '/404',
        component: () => import('@/views/404/index.vue'),
        name: '404',
        meta: {
            label: '404',
            icon: '',
            hidden: true
        },
    },
]

export const asyncRoutes = [
    {
        path: '/',
        name: 'Layout',
        redirect: '/home',
        component: () => import('@/layout/index.vue'),
        meta: {
            label: '',
            icon: '',
            hidden: false,
        },
        children: [
            {
                path: '/home',
                name: 'Home',
                component: () => import('@/views/home/index.vue'),
                meta: {
                    label: '首页',
                    icon: 'HomeFilled',
                    hidden: false
                }
            },
        ]
    },
    {
        path: '/perm',
        name: 'Perm',
        component: () => import('@/layout/index.vue'),
        redirect: '/perm/user',
        meta: {
            label: '权限管理',
            icon: 'Lock',
            hidden: false
        },
        children: [
            {
                path: '/perm/user',
                name: 'User',
                component: () => import('@/views/perm/user/index.vue'),
                meta: {
                    label: '用户管理',
                    icon: 'User',
                    hidden: false
                },
            },
            {
                path: '/perm/role',
                name: 'Role',
                component: () => import('@/views/perm/role/index.vue'),
                meta: {
                    label: '角色管理',
                    icon: 'Avatar',
                    hidden: false
                },
            },
            {
                path: '/perm/menu',
                name: 'Menu',
                component: () => import('@/views/perm/menu/index.vue'),
                meta: {
                    label: '菜单管理',
                    icon: 'Grid',
                    hidden: false
                },
            },
        ]
    }
]
export const anyRoutes = [
    {
        path: '/:pathMatcher(.*)*',
        redirect: '/404',
        name: 'Any',
        meta: {
            label: '',
            icon: '',
            hidden: true
        }
    },
]
  1. 获取token后,请求后端接口获取用户信息
{
      code: 200,
      message: '操作成功',
      data: {
          username: 'wuyu',
          roles: ['admin'],
          avatar: '/public/vite.svg',
          routes: ['Layout', 'Home', 'Screen', 'Perm', 'User', 'Role', 'Menu', 'Product', 'Attr', 'Sku', 'Spu', 'Trademark', 'Any'],
          buttons: ['sys:user:add','sys:user:delete','sys:user:insert','sys:user:select']
      }
}
  1. 通过 路由唯一标识符Name 从前端的动态路由中过滤出我们需要的路由
import {anyRoutes, asyncRoutes, constantRoutes} from "@/router/routes.js"; // 导入前端路由
import {cloneDeep} from "lodash/lang.js"; // 第三方包,用于深拷贝

// 过滤出来异步路由
function filterAsyncRoutes(asyncRoutes, routes) {
    return asyncRoutes.filter(item => {
        if (routes.includes(item.name)) {
            if (item.children && item.children.length > 0) {
                item.children = filterAsyncRoutes(item.children, routes)
            }
            return true
        }
    })
}

export const useUserStore = defineStore('user', () => {

        const userInfo = ref(null)

        const getUserInfo = async () => {
            userInfo.value = await userInfoService()
            const filteredAsyncRoutes = filterAsyncRoutes(cloneDeep(asyncRoutes), userInfo.value.routes)
            userInfo.value.routes = [...filteredAsyncRoutes, ...constantRoutes, ...anyRoutes]
            userInfo.value.routes.forEach(item => {
                router.addRoute(item)
            })
        }

        return {
            userInfo,
            getUserInfo
        }
    },
)
  1. 路由守卫
router.beforeEach(async (to, from, next) => {
    const userStore = useUserStore()

    // 判断用户是否登录
    if (userStore.token) {
        if (userStore.userInfo) {
            // 已经登录,访问/login,会重定向到 / (首页)
            if (to.path === '/login') {
                next('/home')
            } else {
                next()
            }
        } else {
            await userStore.getUserInfo()
            next({...to, replace: true})
        }

    } else {
        // 未登录
        if (to.path === '/login') {
            next()
        } else {
            // 重定向到/login,并追加一个 登录成功后重定向地址
            next({path: '/login', query: {redirect: to.path}})
        }
    }
})

2. 动态侧边栏菜单

  1. 获取动态菜单路由数据
// 获取用户路由
<script setup>
const userStore = useUserStore()
const routes = userStore.userInfo.routes

// 去 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后的菜单树
const menuList = computed(() => {
  return filterHiddenRoute(routes)
})
</script>

<template>
   <el-menu>
     <aside-menu :menuList="menuList"></aside-menu>
   </el-menu>
</template>
  1. 动态菜单递归组件定义

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

<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>

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值