Hwf hai 5 meses
pai
achega
547da5b15d

+ 11 - 0
src/api/menu.ts

@@ -0,0 +1,11 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { RouteRecordRaw } from 'vue-router';
+
+// 获取路由
+export function getRouters(): AxiosPromise<RouteRecordRaw[]> {
+  return request({
+    url: '/api/system/menu/getRouters',
+    method: 'get'
+  });
+}

+ 60 - 0
src/plugins/auth.ts

@@ -0,0 +1,60 @@
+import useUserStore from '@/store/modules/user';
+
+const authPermission = (permission: string): boolean => {
+  const all_permission = '*:*:*';
+  const permissions: string[] = useUserStore().permissions;
+  if (permission && permission.length > 0) {
+    return permissions.some((v) => {
+      return all_permission === v || v === permission;
+    });
+  } else {
+    return false;
+  }
+};
+
+const authRole = (role: string): boolean => {
+  const super_admin = 'admin';
+  const roles = useUserStore().roles;
+  if (role && role.length > 0) {
+    return roles.some((v) => {
+      return super_admin === v || v === role;
+    });
+  } else {
+    return false;
+  }
+};
+
+export default {
+  // 验证用户是否具备某权限
+  hasPermi(permission: string): boolean {
+    return authPermission(permission);
+  },
+  // 验证用户是否含有指定权限,只需包含其中一个
+  hasPermiOr(permissions: string[]): boolean {
+    return permissions.some((item) => {
+      return authPermission(item);
+    });
+  },
+  // 验证用户是否含有指定权限,必须全部拥有
+  hasPermiAnd(permissions: string[]): boolean {
+    return permissions.every((item) => {
+      return authPermission(item);
+    });
+  },
+  // 验证用户是否具备某角色
+  hasRole(role: string): boolean {
+    return authRole(role);
+  },
+  // 验证用户是否含有指定角色,只需包含其中一个
+  hasRoleOr(roles: string[]): boolean {
+    return roles.some((item) => {
+      return authRole(item);
+    });
+  },
+  // 验证用户是否含有指定角色,必须全部拥有
+  hasRoleAnd(roles: string[]): boolean {
+    return roles.every((item) => {
+      return authRole(item);
+    });
+  }
+};

+ 188 - 0
src/store/modules/permission.ts

@@ -0,0 +1,188 @@
+import { defineStore } from 'pinia';
+import router from '@/router';
+import { constantRoutes } from '@/router/routes';
+import { store } from '@/store';
+import auth from '@/plugins/auth';
+import { RouteRecordRaw } from 'vue-router';
+
+import { createCustomNameComponent } from '@/utils/createCustomNameComponent';
+
+// 匹配views里面所有的.vue文件
+const modules = import.meta.glob('./../../views/**/*.vue');
+export const usePermissionStore = defineStore('permission', () => {
+  const routes = ref<RouteRecordRaw[]>([]);
+  const addRoutes = ref<RouteRecordRaw[]>([]);
+  const defaultRoutes = ref<RouteRecordRaw[]>([]);
+
+
+  const getRoutes = (): RouteRecordRaw[] => {
+    return routes.value;
+  };
+
+  const setRoutes = (newRoutes: RouteRecordRaw[]): void => {
+    addRoutes.value = newRoutes;
+    routes.value = constantRoutes.concat(newRoutes);
+  };
+  const generateRoutes = async (): Promise<RouteRecordRaw[]> => {
+    const res = await getRouters();
+    const { data } = res;
+    const sdata = JSON.parse(JSON.stringify(data));
+    const rdata = JSON.parse(JSON.stringify(data));
+    const defaultData = JSON.parse(JSON.stringify(data));
+    const sidebarRoutes = filterAsyncRouter(sdata);
+    const rewriteRoutes = filterAsyncRouter(rdata, undefined, true);
+    const defaultRoutes = filterAsyncRouter(defaultData);
+    const asyncRoutes = filterDynamicRoutes([]);
+    asyncRoutes.forEach((route) => {
+      router.addRoute(route);
+    });
+    setRoutes(rewriteRoutes);
+    // 路由name重复检查
+    duplicateRouteChecker(asyncRoutes, sidebarRoutes);
+    return new Promise<RouteRecordRaw[]>((resolve) => resolve(rewriteRoutes));
+  };
+
+  /**
+   * 遍历后台传来的路由字符串,转换为组件对象
+   * @param asyncRouterMap 后台传来的路由字符串
+   * @param lastRouter 上一级路由
+   * @param type 是否是重写路由
+   */
+  const filterAsyncRouter = (asyncRouterMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw, type = false): RouteRecordRaw[] => {
+    return asyncRouterMap.filter((route) => {
+      if (type && route.children) {
+        route.children = filterChildren(route.children, undefined);
+      }
+      // // Layout ParentView 组件特殊处理
+      // if (route.component?.toString() === 'Layout') {
+      //   route.component = Layout;
+      // } else if (route.component?.toString() === 'ParentView') {
+      //   route.component = ParentView;
+      // } else if (route.component?.toString() === 'InnerLink') {
+      //   route.component = InnerLink;
+      // } else {
+        route.component = loadView(route.component, route.name as string);
+      // }
+      if (route.children != null && route.children && route.children.length) {
+        route.children = filterAsyncRouter(route.children, route, type);
+      } else {
+        delete route.children;
+        delete route.redirect;
+      }
+      return true;
+    });
+  };
+  const filterChildren = (childrenMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw): RouteRecordRaw[] => {
+    let children: RouteRecordRaw[] = [];
+    childrenMap.forEach((el) => {
+      if (el.children && el.children.length) {
+        if (el.component?.toString() === 'ParentView' && !lastRouter) {
+          el.children.forEach((c) => {
+            c.path = el.path + '/' + c.path;
+            if (c.children && c.children.length) {
+              children = children.concat(filterChildren(c.children, c));
+              return;
+            }
+            children.push(c);
+          });
+          return;
+        }
+      }
+      if (lastRouter) {
+        el.path = lastRouter.path + '/' + el.path;
+        if (el.children && el.children.length) {
+          children = children.concat(filterChildren(el.children, el));
+          return;
+        }
+      }
+      children = children.concat(el);
+    });
+    return children;
+  };
+  return {
+    routes,
+    defaultRoutes,
+    getRoutes,
+    setRoutes,
+    generateRoutes,
+  };
+});
+
+// 动态路由遍历,验证是否具备权限
+export const filterDynamicRoutes = (routes: RouteRecordRaw[]) => {
+  const res: RouteRecordRaw[] = [];
+  routes.forEach((route) => {
+    if (route.permissions) {
+      if (auth.hasPermiOr(route.permissions)) {
+        res.push(route);
+      }
+    } else if (route.roles) {
+      if (auth.hasRoleOr(route.roles)) {
+        res.push(route);
+      }
+    }
+  });
+  return res;
+};
+
+export const loadView = (view: any, name: string) => {
+  let res;
+  for (const path in modules) {
+    const dir = path.split('views/')[1].split('.vue')[0];
+    if (dir === view) {
+      res = createCustomNameComponent(modules[path], { name });
+    }
+  }
+  return res;
+};
+
+// 非setup
+export const usePermissionStoreHook = () => {
+  return usePermissionStore(store);
+};
+
+interface Route {
+  name?: string | symbol;
+  path: string;
+  children?: Route[];
+}
+
+/**
+ * 检查路由name是否重复
+ * @param localRoutes 本地路由
+ * @param routes 动态路由
+ */
+function duplicateRouteChecker(localRoutes: Route[], routes: Route[]) {
+  // 展平
+  function flatRoutes(routes: Route[]) {
+    const res: Route[] = [];
+    routes.forEach((route) => {
+      if (route.children) {
+        res.push(...flatRoutes(route.children));
+      } else {
+        res.push(route);
+      }
+    });
+    return res;
+  }
+
+  const allRoutes = flatRoutes([...localRoutes, ...routes]);
+
+  const nameList: string[] = [];
+  allRoutes.forEach((route) => {
+    const name = route.name.toString();
+    if (name && nameList.includes(name)) {
+      const message = `路由名称: [${name}] 重复, 会造成 404`;
+      console.error(message);
+      ElNotification({
+        title: '路由名称重复',
+        message,
+        type: 'error'
+      });
+      return;
+    }
+    nameList.push(route.name.toString());
+  });
+}
+
+export default usePermissionStore;

+ 39 - 0
src/utils/createCustomNameComponent.tsx

@@ -0,0 +1,39 @@
+/**
+ * 后台返回的路由动态生成name 解决缓存问题
+ * 感谢 @fourteendp
+ * 详见 https://github.com/vbenjs/vue-vben-admin/issues/3927
+ */
+import { Component, defineComponent, h } from 'vue';
+
+interface Options {
+  name?: string;
+}
+
+export function createCustomNameComponent(loader: () => Promise<any>, options: Options = {}): () => Promise<Component> {
+  const { name } = options;
+  let component: Component | null = null;
+
+  const load = async () => {
+    try {
+      const { default: loadedComponent } = await loader();
+      component = loadedComponent;
+    } catch (error) {
+      console.error(`Cannot resolve component ${name}, error:`, error);
+    }
+  };
+
+  return async () => {
+    if (!component) {
+      await load();
+    }
+
+    return Promise.resolve(
+      defineComponent({
+        name,
+        render() {
+          return h(component as Component);
+        }
+      })
+    );
+  };
+}

+ 1 - 1
src/views/threePreventionResponsiblePerson/editPersonInformation.vue

@@ -131,7 +131,7 @@ import ResponsibilityType from "@/views/threePreventionResponsiblePerson/respons
 import PersonInformation2 from "@/views/threePreventionResponsiblePerson/personInformation2.vue";
 
 // 页面状态 0缺省页 1详情 2新建、编辑
-let pageStatus = ref('1');
+let pageStatus = ref('0');
 // 表单数据
 const form = ref({
   name: '',