vue-router 实现动态路由和按钮

之前记录过一个动态路由实现过程,但是最近感觉那个还是臃肿,所以做了精简。以下是船新版本。

一、准备完整的路由和按钮的树

在这里需要先梳理一下父子结构关系,每个节点都给他一个唯一的id。这个完整的路由用来在设置界面展示完整的树,以及界面请求到权限id后进行递归遍历使用的。

  • 以下是我项目的结构。

权限配置的可选结构

  • 下面是我提前准备的路由结构,因为/home后期会做按钮权限,所以把他提出来,后边直接将按钮树加入homeHandle.

路由结构

  • 以下是首页的按钮结构树

按钮结构

  • 下面是界面
    首页

二、组装路由和按钮的结构树,并保存初始的整体结构

上一步为了把home界面按钮树组装到路由的树中,我们预留了homeHandle。这里只需要引入并拷贝即可。

import {rootPath, homeHandle} from "@/router/store";
import {paneItems} from "@/views/home/js/store";
import {getUserMenuPermission} from "@/apis/user";

// 组装json
homeHandle.children = paneItems.children;
// 保存一下初始的树结构,下边要根据权限进行删除了---用来为用户添加权限时的展示
const rootPathCopy = JSON.parse(JSON.stringify(rootPath));

三、什么时候去判断路由和按钮该不该展示

之前的版本是在登录的时候请求了有权限的id,这样有个问题就是只要用户不退出登录,即使管理员修改了权限,也不会更新。而且将有权限的id存在浏览器本地,这种不太安全。 所以这次我放到,每次用户刷新界面,先去请求接口,再做判断进行跳转。
另外之前的版本会保存有权限的叶子结点整个枝干, 这次尝试了只保存有权限的叶子节点。(这里导致了判断的valid函数需要修改一下)

  • 以下为router.js的代码:
import {createRouter, createWebHistory} from "vue-router";
import {useAuth} from "@/store/auth";
import {setRoutes} from "@/store/role";

const Login = () => import("@/views/login");


const routes = [
    {
        path: "/login", name: "login", component: Login
    }
];

const router = createRouter({
    history: createWebHistory(process.env.BASE_URL), routes
});
let isRefresh = true; // 做一个标志,防止每次跳转都会执行验证代码

router.beforeEach(async (to, from, next) => {
    const auth = useAuth();
    if (to.path === "/login") {
        localStorage.clear();
        isRefresh = true;
        return next();
    } else {
        if (!auth.token) return next("/login");
        if (isRefresh && to.path !== "/login") {//这里用到了isRedresh【也是主要的验证代码】
            next(false); // 先将路由截断,因为下面会有异步请求,不这样做,会直接报找不到要跳转的路由
            try {
                await setRoutes(router);
                isRefresh = false;
                return router.push(to.path);
            } catch (e) {
                return router.push("/login");
            }

        }
        return next();
    }
});
export default router;
  • 下面是验证方法的代码:
function setRoutes(Router) {
    return new Promise(async (resolve, reject) => {
        try {
            let {data} = await getUserMenuPermission({userName: localStorage.getItem("ocean_username")});
            const roles = {};
            for (const {menu_key} of data) {
                roles[menu_key] = true;
            } // 获取到权限id后存成个json,这样查找效率会快一些
            const checkNode = (node) => node.roleIndex !== undefined && roles[node.roleIndex];  // 他就是节点有无权限的判断
            /*
				下面是个后序遍历的方法,因为我这个项目不需要控制所有节点的权限,
				所以有可能后序遍历到不了叶子节点就遇到了有权限的roleIndex,
				这时候结束当下递归。如果一直到了子节点都没遇到有权限的roleIndex,
				那就砍掉这个枝干。
				在设置界面我只保存了有权限的”叶子节点”(这个是相对于设置界面的展示树来说的,
				在展示树上面的节点都是需要控制的)。
			*/
            const validRoute = (function iterate(o, parent, oIndex) {
                let newItem = null;
                const isValid = checkNode(o);
                if (o.children && o.children.length && !isValid) { 
                // 有孩子切没被验证成功,进行递归。
                    const allValid = [];
                    const routeValid = [];
                    for (let i = o.children.length - 1; i >= 0; i--) { 
                    // 因为孩子可能会用splice砍掉自己,所以倒着遍历
                        const cur = o.children[i];
                        const validChild = iterate(cur, o.children, i);
                        if (validChild) { 
                        // 如果返回了不是null,说明可能是路由,可能是按钮。
                        // 只有路由才要被加到newItem中,按钮直接操作O;
                            allValid.unshift(validChild);
                            if (validChild.component) routeValid.unshift(validChild);
                        }
                    }
                    if (!allValid.length && oIndex !== -1 && parent) parent.splice(oIndex, 1); 
                    // 每一个子节点有权限 把自己砍掉
                    else if (o.component) { 
                    // 自己是路由, 且有路由子节点把孩子加进来
                        newItem = {
                            ...o
                        };
                        delete newItem.children;
                        delete newItem.redirect;
                        if (routeValid.length) {
                            newItem.children = routeValid;
                            newItem.redirect = routeValid[0].path;
                        }
                    } else newItem = { // 自己不是路由,返回O
                        ...o
                    };
                } else {
                    if (isValid) newItem = {
                        ...o
                    }; // 被验证了 返回新节点
                    else if (oIndex !== -1 && parent) {
                        parent.splice(oIndex, 1);
                    } // 没验证成功 砍掉自己
                }
                return newItem;
            })(rootPath, null, -1);
            // 上边的跑完以后 validRoute是完整的路由树,rootPath中的按钮树也被裁剪完成了,
            // 生成UI的时候去从rootPath中找按钮树即可。
            validRoute && await Router.addRoute("", validRoute);
            resolve("ok");
        } catch (e) {
            reject(null);
        }
    });
}

四、设置界面

这里我就用的el-tree。引入了rootPathCopy来生成的tree。

至此大概过程就结束了,刚刚做完,可能有bug。往后慢慢修改吧。

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值