weixin_58412143 2024-04-09 16:09 采纳率: 91.6%
浏览 21
已结题

vue admin pro 从后台获取路由并根据不同角色设置页面权限?

后台系统里的角色管理可以设置某个角色对哪些页面有权限,相对应的侧边栏要呼应,我是用vueadminpro这个框架,他里面配置好了动态路由获取和roles权限控制以及根据roles数组拦截路由,但是我把后端接口写到他留好的位置并没有实现,这个都会贴出
两个接口,一个是全部的菜单路由,一个是某个角色的页面权限,返回内容如下
第一个

[
    {
        "id": 1,
        "parent_id": 0,
        "level": 1,
        "path": "/distribution-generalization",
        "component": "Layout",
        "name": "分销概括",
        "meta_title": "分销概括",
        "meta_icon": "apps-line"
    },
    {
        "id": 6,
        "parent_id": 1,
        "level": 2,
        "path": "/generalization",
        "component": "Distribution-generalization/index",
        "name": "分销概括",
        "meta_title": "分销概括",
        "meta_icon": null
    },
    {
        "id": 7,
        "parent_id": 2,
        "level": 2,
        "path": "/distributor",
        "component": "Distributor-management/index",
        "name": "分销员列表",
        "meta_title": "分销员列表",
        "meta_icon": null
    },
    {
        "id": 8,
        "parent_id": 2,
        "level": 2,
        "path": "/audit",
        "component": "Distributor-management/Distributor-audit/index",
        "name": "分销员审核",
        "meta_title": "分销员审核",
        "meta_icon": null
    },
    {
        "id": 9,
        "parent_id": 2,
        "level": 2,
        "path": "/level",
        "component": "Distributor-management/Distribution-level/index",
        "name": "分销等级",
        "meta_title": "分销员等级",
        "meta_icon": null
    },
]
//第二个
[
    {
        "id": 1,
        "parent_id": 0,
        "level": 1,
        "name": "分销概括",
        "checked": true
    },
    {
        "id": 2,
        "parent_id": 0,
        "level": 1,
        "name": "分销员管理",
        "checked": true
    },
    {
        "id": 6,
        "parent_id": 2,
        "level": 2,
        "name": "分销员列表",
        "checked": false
    },
    {
        "id": 7,
        "parent_id": 2,
        "level": 2,
        "name": "分销员审核",
        "checked": false
    },
    {
        "id": 8,
        "parent_id": 2,
        "level": 2,
        "name": "分销员等级",
        "checked": false
    },
]

动态路由配置如下
src/store/modules/routes.js——这个setRoutes里配置后端路由,其他的都是框架写好的没有改动

/**
 * @description 路由拦截状态管理,目前两种模式:all模式与intelligence模式,其中partialRoutes是菜单暂未使用
 */
import Vue from 'vue'
import { asyncRoutes, constantRoutes, resetRouter } from '@/router'
import { getList } from '@/api/router'
import { convertRouter, filterRoutes } from '@/utils/routes'
import { authentication, rolesControl } from '@/config'
import { isArray } from '@/utils/validate'
import {generaMenu} from './permission.js'
import { translateDataToTree } from '@/utils'

const state = () => ({
  routes: [],
  activeName: '',
})
const getters = {
  routes: (state) => state.routes,
  activeName: (state) => state.activeName,
}
const mutations = {
  /**
   * @description 多模式设置路由
   * @param {*} state
   * @param {*} routes
   */
  setRoutes(state, routes) {
    state.routes = routes
  },
  /**
   * @description 修改Meta
   * @param {*} state
   * @param options
   */
  changeMenuMeta(state, options) {
    function handleRoutes(routes) {
      return routes.map((route) => {
        if (route.name === options.name) Object.assign(route.meta, options.meta)
        if (route.children && route.children.length)
          route.children = handleRoutes(route.children)
        return route
      })
    }
    state.routes = handleRoutes(state.routes)
  },
  /**
   * @description 修改 activeName
   * @param {*} state
   * @param activeName 当前激活菜单
   */
  changeActiveName(state, activeName) {
    state.activeName = activeName
  },
}
const actions = {
  /**
   * @description 多模式设置路由
   * @param {*} { commit }
   * @param mode
   * @returns
   */
  async setRoutes({ commit }, mode = 'none') {
    // 默认前端路由
    let routes = [...asyncRoutes]
    // 设置游客路由关闭路由拦截(不需要可以删除)
    const control = mode === 'visit' ? false : rolesControl
    // 设置后端路由(不需要可以删除)
    if (authentication === 'all') {
      // const {
      //   data: { list },
      // } = await getList()
            //new
            await getList().then((res)=>{
                console.log('我是store路由')
                list = generaMenu(asyncRoutes,res)
            })
      if (!isArray(list))
        Vue.prototype.$baseMessage(
          '路由格式返回有误!',
          'error',
          'vab-hey-message-error'
        )
      if (list[list.length - 1].path !== '*')
        list.push({ path: '*', redirect: '/404', meta: { hidden: true } })
      routes = convertRouter(list)
    }
    // 根据权限和rolesControl过滤路由
    const accessRoutes = filterRoutes([...constantRoutes, ...routes], control)
    // 设置菜单所需路由
    commit('setRoutes', JSON.parse(JSON.stringify(accessRoutes)))
    // 根据可访问路由重置Vue Router
    await resetRouter(accessRoutes)
  },
  /**
   * @description 修改Route Meta
   * @param {*} { commit }
   * @param options
   */
  changeMenuMeta({ commit }, options = {}) {
    commit('changeMenuMeta', options)
  },
  /**
   * @description 修改 activeName
   * @param {*} { commit }
   * @param activeName 当前激活菜单
   */
  changeActiveName({ commit }, activeName) {
    commit('changeActiveName', activeName)
  },
}
export default { state, getters, mutations, actions }
//这个是其他页面的方法我复制到这里方便看
export function generaMenu(routes, data) {
  const len = data.length
  // eslint-disable-next-line no-const-assign
  for (let i = 0; i < len; i++) {
    /*    alert(data[i].path)
    alert(data[i].meta.title)*/
    const menu = {
      path: data[i].path + '',
      // component: () => import(`${item.component}`),
            name: data[i].name + '',
      component: Layout,
      // alwaysShow: data[i].alwaysShow,
      // redirect: data[i].redirect + '',
      // hidden: true,
      meta: { title: data[i].meta_title + '', icon: data[i].meta_icon },
      children: []
    }
    // alert('data[i].children' + data[i].children)
    if (data[i].children) {
      const children = data[i].children
      for (let j = 0; j < children.length; j++) {
        const viewPath = children[j].component
        const childrenList = {
          path: children[j].path + '',
          component: (resolve) => require([`@/views/${viewPath}`], resolve),
          name: children[j].name + '',
          // hidden: true,
          // component: () => import(`@/views/${viewPath}`),
          meta: { title: children[j].meta_title + '', icon: children[j].meta_icon }
        }
        menu.children.push(childrenList)
      }
    }
    routes.push(menu)
  }
}

src/utils/routes.js
src/store/modules/user.js

/**
 * @description 登录、获取用户信息、退出登录、清除token逻辑,不建议修改
 */
    /**
     * @description 获取用户信息接口 这个接口非常非常重要,如果没有明确底层前逻辑禁止修改此方法,错误的修改可能造成整个框架无法正常使用
     * @param {*} { commit, dispatch, state }
     * @returns
     */
    async getUserInfo({
        commit,
        dispatch
    }) {
        const {
            data: {
                username,
                avatar,
                roles,
                permissions
            },
        } = await getUserInfo()
        /**
         * 检验返回数据是否正常,无对应参数,将使用默认用户名,头像,Roles和Permissions
         * username {String}
         * avatar {String}
         * roles {List}
         * ability {List}
         */
        if (
            (username && !isString(username)) ||
            (avatar && !isString(avatar)) ||
            (roles && !isArray(roles)) ||
            (permissions && !isArray(permissions))
        ) {
            const err = 'getUserInfo核心接口异常,请检查返回JSON格式是否正确'
            Vue.prototype.$baseMessage(err, 'error', 'vab-hey-message-error')
            throw err
        } else {
            // 如不使用username用户名,可删除以下代码
            if (username) commit('setUsername', username)
            // 如不使用avatar头像,可删除以下代码
            if (avatar) commit('setAvatar', avatar)
            // 如不使用roles权限控制,可删除以下代码
            if (roles) dispatch('acl/setRole', roles, {
                root: true
            })
            // 如不使用permissions权限控制,可删除以下代码
            if (permissions)
                dispatch('acl/setPermission', permissions, {
                    root: true
                })
        }
    },
    
export default {
    state,
    getters,
    mutations,
    actions
}

mock/controller/user.js

src/vab/plugins/permissions.js

/**
 * @description 路由守卫,目前两种模式:all模式与intelligence模式
 */
import router from '@/router'
import store from '@/store'
import VabProgress from 'nprogress'
import 'nprogress/nprogress.css'
import getPageTitle from '@/utils/pageTitle'
import {
    toLoginRoute
} from '@/utils/routes'
import {
    authentication,
    loginInterception,
    routesWhiteList,
    supportVisit,
} from '@/config'

VabProgress.configure({
    easing: 'ease',
    speed: 500,
    trickleSpeed: 200,
    showSpinner: false,
})
router.beforeEach(async (to, from, next) => {
    const {
        showProgressBar
    } = store.getters['settings/theme']
    if (showProgressBar) VabProgress.start()
    let hasToken = store.getters['user/token']
    // console.log('permissionToken',hasToken)
    if (!loginInterception) hasToken = true
    if (hasToken) {
        if (store.getters['routes/routes'].length) {
            // 禁止已登录用户返回登录页
            if (to.path === '/login') {
                next({
                    path: '/'
                })
                if (showProgressBar) VabProgress.done()
            } else next()
            // // 检查用户是否已登录,如果已登录则重定向到主页
            //获取原型对象上的push函数
            // const originalPush = VueRouter.prototype.push
            // //修改原型对象中的push方法
            // VueRouter.prototype.push = function push(location) {
            //   return originalPush.call(this, location).catch(err => err)
            // }

            // if (!to.meta.isPublic && !localStorage.token){
            //         return next('/login')
            //     }
            //     next()
        } else {

            try {
                if (loginInterception) await store.dispatch('user/getUserInfo')
                // config/setting.config.js loginInterception为false(关闭登录拦截时)时,创建虚拟角色
                else await store.dispatch('user/setVirtualRoles')
                // 根据路由模式获取路由并根据权限过滤
                await store.dispatch('routes/setRoutes', authentication)
                next({
                    ...to,
                    replace: true
                })
            } catch (err) {
                console.error('vue-admin-beautiful错误拦截:', err)
                await store.dispatch('user/resetAll')
                next(toLoginRoute(to.path))
            }
        }
    } else {
        console.log('没有token',hasToken)
        if (routesWhiteList.includes(to.path)) {
            // 设置游客路由(不需要可以删除)
            if (supportVisit && !store.getters['routes/routes'].length) {
                await store.dispatch('routes/setRoutes', 'visit')
                next({
                    ...to,
                    replace: true
                })
            } else next()
        } else next(toLoginRoute(to.path))
    }
})
router.afterEach((to) => {
    document.title = getPageTitle(to.meta.title)
    if (VabProgress.status) VabProgress.done()
})

页面渲染部分

<template>
    <el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px" @close="close">
        <el-form ref="form" label-width="80px" :model="form" :rules="rules">
            <el-form-item label="角色码" prop="name">
                <el-input v-model="form.name" />
            </el-form-item>
            <el-form-item label="菜单">
                <div class="vab-tree-border">
                    <el-table border :data="list" ref="table" row-key="id" @selection-change="setTabelSelected">
                        <el-table-column align="center" show-overflow-tooltip type="selection"
                            :reserve-selection="true" />
                        <el-table-column align="center" label="序号" width="55">
                            <template #default="{ $index }">
                                {{ $index + 1 }}
                            </template>
                        </el-table-column>
                        <el-table-column align="center" label="角色菜单" prop="name" show-overflow-tooltip />
                    </el-table>
                    <!-- <el-tree ref="tree" :data="list" :default-checked-keys="[
              '/',
                             '/distribution-generalization',
                             '/distributors',
                             '/business-card-management',
                             '/business-card-management',
                             '/withdrawal-management',
              '/vab',
              '/other',
              '/mall',
              '/setting',
              '/error',
            ]" :default-expanded-keys="[]" node-key="path" show-checkbox> -->
                    <!-- <template #default="{ data }">
                            <span>{{ data.name }}</span>
                            <span>{{ data.meta.title }}</span>
                        </template> -->
                    <!-- </el-tree> -->
                </div>
            </el-form-item>
            <!-- <el-form-item label="按钮权限">
                <el-checkbox-group v-model="form.btnRolesCheckedList">
                    <el-checkbox v-for="item in btnRoles" :key="item.value" :label="item.value">
                        {{ item.lable }}
                    </el-checkbox>
                </el-checkbox-group>
            </el-form-item> -->
        </el-form>
        <template #footer>
            <el-button @click="close">取 消</el-button>
            <el-button type="primary" @click="save">确 定</el-button>
        </template>
    </el-dialog>
</template>

<script>
    import {
        doEdit,
        getListMenus,
        getAllList,
        doAdd
    } from '@/api/roleManagement'
    import {
        getList
    } from '@/api/router'

    export default {
        name: 'RoleManagementEdit',
        data() {
            return {
                form: {
                    btnRolesCheckedList: [],
                },
                rules: {
                    name: [{
                        required: true,
                        trigger: 'blur',
                        message: '请输入角色码'
                    }],
                },
                title: '',
                dialogFormVisible: false,
                list: [],
                ListMenus: [],
                // 存放选中的数据
                selectedList: [],
                /* btnRoles demo */
                btnRoles: [{
                        lable: '读',
                        value: 'read:system',
                    },
                    {
                        lable: '写',
                        value: 'write:system',
                    },
                    {
                        lable: '删',
                        value: 'delete:system',
                    },
                ],
            }
        },
        created() {
            // this.fetchData()
        },
        methods: {
            showEdit(row) {
                // this.selectedList = []
                if (!row) {
                    this.title = '添加'
                    this.form = Object.assign({}, row)
                    this.fetchData()
                } else {
                    this.title = '编辑'
                    this.form = Object.assign({}, row)
                    this.fetchData()
                }
                this.dialogFormVisible = true
            },
            close() {
                this.selectedList = []
                this.$refs['form'].resetFields() //重置表单
                this.form = this.$options.data().form
                this.dialogFormVisible = false
            },
            //获取数据
            fetchData() {
                const params = "id=" + this.form.id
                getListMenus(params).then((res) => {
                    this.list = res
                    //选中初始回显
                    this.list.forEach(item => {
                        this.$refs.table.clearSelection(); // 清除之前的选中状态
                        if (item.checked == true) {
                            // this.selectedList.push(item)
                            // this.$refs.table.toggleRowSelection(item);
                            this.selectedList = this.list.reduce((accumulator, item) => {
                                if (item.checked == true && !accumulator.find(selectedItem =>
                                        selectedItem.id === item.id)) {
                                    accumulator.push(item);
                                }
                                return accumulator;
                            }, []);
                        }
                    })
                    this.selectedList.forEach(item => {
                        this.$refs.table.toggleRowSelection(item);
                    });
                    if (this.selectedList !== 0) {
                        this.test()
                    }
                });

            },
            test() {
                getAllList().then((res) => {
                    // 使用filter方法筛选出包含指定id的对象
                    const filteredObjects = res.filter(obj1 => {
                        return this.selectedList.some(obj2 => obj2.id === obj1.id);
                    });
                    // 输出筛选后的对象数组
                    console.log(this.selectedList)
                    console.log('filteredObjects');
                    console.log(filteredObjects);
                })
            },
            // rows  将存放选中的数据( selectedList )传给setTabelSelected方法
            setTabelSelected(row) {
                // 检查当前行是否已经在selectedList中
                // console.log(this.selectedList)
                const index = this.selectedList.findIndex(item => item.id === row.id);
                const obj = this.selectedList.find(item => item.id === row.id);
                if (index === -1) {
                    // 如果不存在,添加到selectedList中
                    this.selectedList = row;
                } else {
                    // 如果存在,从selectedList中移除
                    this.selectedList.splice(index, 1);
                }
                // this.ListMenus=row
                // console.log('this.selectedList')
                // console.log(this.selectedList)
            },
            save() {
                this.$refs['form'].validate(async (valid) => {
                    if (valid && this.selectedList.length !== 0) {
                        const params = this.form.id
                        // 使用map方法从每个对象中提取id,并创建一个新数组
                        const idsExtracted = this.selectedList.map(item => item.id);
                        this.form.menu_ids = idsExtracted
                        console.log('this.form提交')
                        console.log(this.form)
                        if (this.title == '编辑') {
                            doEdit(params, this.form).then((res) => {
                                console.log('编辑')
                                this.$emit('fetch-data')
                            })
                        }
                        if (this.title == '添加') {
                            doAdd(this.form).then((res) => {
                                console.log('添加')
                                this.$emit('fetch-data')
                            })
                        }
                        this.close()
                    }
                })
            },
        },
    }
</script>

<style lang="scss" scoped>
    .vab-tree-border {
        height: 200px;
        padding: $base-padding;
        overflow-y: auto;
        border: 1px solid #dcdfe6;
        border-radius: $base-border-radius;
    }
</style>



  • 写回答

2条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2024-04-09 16:10
    关注

    引自免费微信小程序:皆我百晓生

    好的,我很乐意帮助您解决这个问题。首先,我们需要了解您的后台管理系统是如何使用的。您可以分享一下具体的配置和接口吗?然后我们可以开始讨论如何在 Vue Admin Pro 中设置不同角色的权限来实现不同的页面显示。

    这是一个基本的方案:

    1. 首先,我们需要安装所需的依赖项。
    2. src/store/modules/user.js 文件中,初始化用户的注册、登录和登出流程。确保该文件中的逻辑与您的后端服务器一致。在这里,我们以插件形式处理密码验证和角色验证,使用 auth.interception 保护以阻止未授权的访问。尝试删除其他一些方法,但最好在 test() 方法中添加它们以确保使用了正确的逻辑。
    3. 选择要允许的角色。我们可以在 src/store/modules/user.js 文件中增加一个函数,根据路径与 _LIST.unshift("/*", "/logistics")ListMenus.push("/*", "/menu") 来定义该角色的菜单视图,并创建相应的数据结构。
    4. src/components/L avent/index.vue 中,添加一个表格用于显示角色列表。这里我们将不再遍历模型数组并应用任何更改,而是将它添加到模板中。接下来,将 ListMenus 建立为模块化结构,以便于管理和维护。您可以在此结构中提取相关数据并将权限映射到列表中的具体元素上。
    5. 针对某些特定的角色,例如分配给餐厅经理的页面,添加对应的菜单选项和检查逻辑。
    6. 在前端,我们通过以下方式设置角色权限: a. 用户注册:使用导航守卫设置所有公共页面权限,但在跳转至特定页面时,添加相应的公共页面过滤器。 b. 用户登录:根据权限检查用户是否存在。检查用户ID以防止未授权访问。 c. 更改角色权限:使用路由守卫和规则。

    这是基于这些基础的方案进行操作的基本示例代码。当需要修改其他功能时,请提供更多的上下文信息,我会尽力提供详细的指导和代码。

    如果您有任何其他疑问或问题,请随时向我提问。

    评论 编辑记录

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 4月10日
  • 创建了问题 4月9日

悬赏问题

  • ¥15 数据量少可以用MK趋势分析吗
  • ¥15 使用VH6501干扰RTR位,CANoe上显示的错误帧不足32个就进入bus off快慢恢复,为什么?
  • ¥15 大智慧怎么编写一个选股程序
  • ¥100 python 调用 cgps 命令获取 实时位置信息
  • ¥15 两台交换机分别是trunk接口和access接口为何无法通信,通信过程是如何?
  • ¥15 C语言使用vscode编码错误
  • ¥15 用KSV5转成本时,如何不生成那笔中间凭证
  • ¥20 ensp怎么配置让PC1和PC2通讯上
  • ¥50 有没有适合匹配类似图中的运动规律的图像处理算法
  • ¥15 dnat基础问题,本机发出,别人返回的包,不能命中