権限制御の実装アプローチ
バックエンドから提供される権限コードとローカルルート設定のIDを照合することで、ユーザー権限に基づく動的ルーティングを実装します。ロール毎に表示可能な操作項目を制御し、ルート設定を正規化後にaddRouteメソッドで動的に追加します。
ルーティング設定構造
// 基本ルート設定
export const baseRoutes: RouteConfig[] = [
{
path: "/admin",
meta: { title: "管理システム", icon: "system", alwaysVisible: true },
component: "Layout",
routeId: "sys",
parentId: "root"
}
];
// 子ルート設定
export const childRoutes: RouteConfig[] = [
{
path: "users",
name: "UserManagement",
meta: { title: "ユーザー管理", icon: "user" },
component: "admin/users/index",
routeId: "user_mgmt",
parentId: "sys"
},
{
path: "settings",
name: "SystemSettings",
meta: { title: "システム設定", icon: "config" },
component: "admin/settings/index",
routeId: "sys_config",
parentId: "sys"
}
];
ルートツリー生成関数
export function generateRouteHierarchy(
items: any[],
identifier = 'routeId',
parentRef = 'parentId',
childrenProp = 'children',
rootValue = 'root'
) {
const hierarchy = [];
items.forEach(item => {
if (item[parentRef] === rootValue) {
const descendants = generateRouteHierarchy(items, identifier, parentRef, childrenProp, item[identifier]);
if (descendants.length) item[childrenProp] = descendants;
hierarchy.push(item);
}
});
return hierarchy;
}
動的ルート生成ストア
import { defineStore } from "pinia";
import Layout from "@/layouts/MainLayout.vue";
const loadView = (viewPath: string) => () => import(`@/views/${viewPath}.vue`);
function resolveComponents(routes: any[]) {
return routes.map(route => {
route.component = route.component === "Layout" ? Layout : loadView(route.component);
if (route.children) resolveComponents(route.children);
return route;
});
}
export const useRouteStore = defineStore("routeManager", {
state: () => ({
dynamicRoutes: []
}),
actions: {
async initializeRoutes() {
const permissionCodes = localStorage.getItem("authCodes")?.split(",") || [];
if (!permissionCodes.length) return [];
const authorizedRoutes = childRoutes.filter(child =>
permissionCodes.some(code => child.routeId.includes(code))
);
const routeTree = generateRouteHierarchy([
...baseRoutes,
...authorizedRoutes
]);
const validRoutes = routeTree.filter(branch => branch.children?.length);
this.dynamicRoutes = resolveComponents(validRoutes);
return this.dynamicRoutes;
}
}
});
ルーターガード処理
router.beforeEach(async (to, from, next) => {
const authToken = getAuthToken();
const routeManager = useRouteStore();
if (!authToken) {
next("/auth/login");
} else if (!routeManager.dynamicRoutes.length) {
try {
const dynamicRoutes = await routeManager.initializeRoutes();
const staticRoutes = router.getRoutes();
dynamicRoutes.forEach(route => {
router.addRoute(route);
});
next({ ...to, replace: true });
} catch {
next("/");
}
} else {
to.name === "Login" ? next(from.name ? from.path : "/") : next();
}
});