vite+vue-ts 如何在项目中设置菜单页面的权限

99 阅读2分钟

在业务系统的后台中,通常需要根据用户的角色给予对应的权限。 必要功能:

  1. 增删改查权限配置
  2. 对应接口配置(用与后端权限判断)
  3. 自定义权限配置
  4. 名称,图标,路由修改
  5. 是否外链
  6. 是否显示
  7. 排序

配置对应页面文件 综上考虑使用动态配置页面,

声明路由节点对象:

/**
 * 路由项接口定义
 * 描述单个路由节点的完整结构和权限信息
 */
interface RouterItem {
    /** 是否有新增权限 */
    add: boolean
    /** 新增操作相关的 API 接口列表 */
    addApi: string[]
    /** 是否需要认证 */
    auth: boolean
    /** 路由分类标识 */
    classify: number
    /** 组件文件路径,用于动态导入组件 */
    componentName: string
    /** Vue 组件实例 */
    component: any
    /** 创建时间戳 */
    createTime: number
    /** 是否有删除权限 */
    del: boolean
    /** 删除操作相关的 API 接口列表 */
    delApi: string[]
    /** 是否有编辑权限 */
    edit: boolean
    /** 编辑操作相关的 API 接口列表 */
    editApi: string[]
    /** 路由唯一标识 */
    id: number
    /** 是否有列表查看权限 */
    list: boolean
    /** 列表查询相关的 API 接口列表 */
    listApi: string[]
    /** 路由名称,用于路由跳转 */
    name: string
    /** 父级路由 ID,用于构建层级关系 */
    parentId: number
    /** 路由路径 */
    path: string
    /** 排序权重,数值越小越靠前 */
    sort: number
    /** 路由状态:1-启用,0-禁用 */
    state: number
    /** 路由显示标题 */
    title: string
    /** 子路由列表 */
    children: Array<RouterItem>
}

数据库结果与上一致,树状数据结构

用户账号——》角色关联——》目录结构

通过代码添加到路由中

 import { useRouter } from 'vue-router'
 const router = useRouter()
 router.addRoute(pageRoutes)

通过嵌套组件渲染菜单结构

<template>
    <template v-for="item, index in prop.data">
        <el-sub-menu index="1" v-if="item.children && item.children.length > 0">
            <template #title>
                <Icon v-if="item.icon" :name="item.icon"></Icon>
                <span>{{ t(item.title) }}</span>
            </template>
            <MenuItem :data="item.children">
            </MenuItem>
        </el-sub-menu>
        <el-menu-item :index="item.path" v-else>
            <Icon v-if="item.icon" :name="item.icon"></Icon>
            <template #title>{{ t(item.title) }}</template>
        </el-menu-item>
    </template>
</template>

<script lang="ts" setup>
import { useI18n } from 'vue-i18n';

const { t } = useI18n()

const prop = defineProps({
    data: {
        type: Array<any>,
        default: []
    }
})

</script>

<template>
    <div class="menu-box">
        <el-icon v-show="!isCollapse" @click="() => isCollapse = true">
            <CaretLeft />
        </el-icon>
        <el-icon v-show="isCollapse" @click="() => isCollapse = false">
            <CaretRight />
        </el-icon>
    </div>
    <el-menu style="height: 100%;" router :default-active="route.path" class="el-menu-vertical-demo"
        :collapse="isCollapse" @open="handleOpen" @close="handleClose">
        <MenuItem :data="pageRoutes.children" />
    </el-menu>
</template>

<script lang="ts" setup>
import { ref } from 'vue'

import {
    CaretLeft,
    CaretRight

} from '@element-plus/icons-vue'


import { useRoute } from 'vue-router'

import MenuItem from '@/components/MenuItem.vue'
import initEachRouter from '@/utils/router'
const route = useRoute()
const permission = JSON.parse(localStorage.getItem('permission') || '[]')
const pageRoutes = initEachRouter(permission)


const { menu } = defineProps({
    menu: {
        type: Array,
        default: () => []
    }
})
console.log(menu)



const isCollapse = ref(false)
const handleOpen = (key: string, keyPath: string[]) => {
    console.log(key, keyPath)
}
const handleClose = (key: string, keyPath: string[]) => {
    console.log(key, keyPath)
}
</script>

<style>
.menu-box {
    position: absolute;
    top: 50vh;
    width: 28px;
    height: 28px;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 50%;
    font-size: 20px;
    right: -14px;
    z-index: 1000;
    box-shadow: 0 0 3px #aaa;
    background-color: #fff;
}
</style>