-
+
+
}
@@ -43,8 +39,8 @@ export default function PermissionManagement() {
const [resourceModalVisible, setResourceModalVisible] = useState(false);
const [roleForm] = Form.useForm();
const [resourceForm] = Form.useForm();
+ const [editingResource, setEditingResource] = useState(null);
- // 获取所有权限数据(包括关联数据)
const fetchPermissions = async (params = {}) => {
try {
setLoading(true);
@@ -56,15 +52,6 @@ export default function PermissionManagement() {
resources:resource_id(*)
`, { count: 'exact' });
- // 添加搜索条件
- if (params.search) {
- query = query.or(`
- roles.name.ilike.%${params.search}%,
- resources.resource_name.ilike.%${params.search}%,
- resources.description.ilike.%${params.search}%
- `);
- }
-
// 添加排序
if (params.field && params.order) {
const ascending = params.order === 'ascend';
@@ -240,6 +227,47 @@ export default function PermissionManagement() {
}
};
+ // 添加删除资源的函数
+ const handleDeleteResource = async (resourceId) => {
+ try {
+ const { error } = await supabase
+ .from('permission_resources')
+ .delete()
+ .eq('id', resourceId);
+
+ if (error) throw error;
+ message.success('删除资源成功');
+ fetchResources();
+ } catch (error) {
+ message.error('删除资源失败');
+ console.error(error);
+ }
+ };
+
+ // 添加更新资源的函数
+ const handleUpdateResource = async (values) => {
+ try {
+ const { error } = await supabase
+ .from('permission_resources')
+ .update({
+ resource_name: values.resource_name,
+ resource_type: values.resource_type,
+ attributes_type: values.attributes_type,
+ description: values.description
+ })
+ .eq('id', editingResource.id);
+
+ if (error) throw error;
+ message.success('更新资源成功');
+ setResourceModalVisible(false);
+ resourceForm.resetFields();
+ fetchResources();
+ } catch (error) {
+ message.error('更新资源失败');
+ console.error(error);
+ }
+ };
+
// 初始化加载
useEffect(() => {
fetchPermissions();
@@ -274,7 +302,6 @@ export default function PermissionManagement() {
title: '角色',
dataIndex: ['roles', 'name'],
key: 'role_name',
- sorter: true,
render: (name) => (
{name}
@@ -285,10 +312,9 @@ export default function PermissionManagement() {
title: '资源',
dataIndex: ['resources', 'resource_name'],
key: 'resource_name',
- sorter: true,
},
{
- title: '资源类型',
+ title: '控制颗粒度',
dataIndex: ['resources', 'resource_type'],
key: 'resource_type',
render: (type) => (
@@ -306,10 +332,8 @@ export default function PermissionManagement() {
key: 'attributes_type',
render: (attributes_type) => (
- {!attributes_type ? '无' : attributes_type}
-
+ {!attributes_type ? '-' : attributes_type}
-
)
},
{
@@ -333,12 +357,12 @@ export default function PermissionManagement() {
);
}
},
- {
- title: '描述',
- dataIndex: ['resources', 'description'],
- key: 'description',
- ellipsis: true,
- },
+ // {
+ // title: '描述',
+ // dataIndex: ['resources', 'description'],
+ // key: 'description',
+ // ellipsis: true,
+ // },
{
title: '操作',
key: 'operation',
@@ -365,82 +389,129 @@ export default function PermissionManagement() {
];
// Modal 表单内容
- const renderFormItems = () => (
- <>
-
-
-
+ const renderFormItems = () => {
+ const [roleSelectOpen, setRoleSelectOpen] = useState(false);
+ const [resourceSelectOpen, setResourceSelectOpen] = useState(false);
-
-
-
+
+
-
-
-
- >
- );
+
+
+
+
+
+
+
+ >
+ );
+ };
return (
@@ -535,18 +606,26 @@ export default function PermissionManagement() {
{/* 添加资源 Modal */}
{
setResourceModalVisible(false);
+ setEditingResource(null);
resourceForm.resetFields();
}}
onOk={() => resourceForm.submit()}
+ zIndex={1100}
>
(
);
-// 这里做premeision 路由限制
-const renderRoutes = (routes) => {
- return routes
- // .filter(route => !route.hidden)
- .map(route => {
- const Component = route.component;
- if (route.children) {
- return (
-
}>
-
-
- }>
- {renderRoutes(route.children)}
-
- );
- }
+// 渲染路由
+const renderRoutes = (routes) => {
+ if (!Array.isArray(routes)) {
+ console.warn('routes is not an array:', routes);
+ return [];
+ }
+ return routes.map(route => {
+ const Component = route.component;
+ if (route.children) {
return (
-
}>
}
- />
+ >
+ {renderRoutes(route.children)}
+
);
- });
+ }
+
+ return (
+
}>
+
+
+ }
+ />
+ );
+ });
};
const AppRoutes = () => {
- const { user } = useAuth();
- const { adminRole: role } = user;
+ const { user, loading } = useAuth();
+
+ if (loading) {
+ return (
+
+
+
+ );
+ }
return (
:
+ user?.id && user.menukeys?.includes('company/serviceTemplate') ? (
+
+ ) : (
+
+ )
}
/>
@@ -67,11 +84,15 @@ const AppRoutes = () => {
}
>
- }
/>
- {renderRoutes(generateRoutes(role))}
+
+ {/* 渲染所有路由 */}
+ {renderRoutes(allRoutes)}
+
} />
diff --git a/src/routes/routes.js b/src/routes/routes.js
index 5a02bb2..23cb9c8 100644
--- a/src/routes/routes.js
+++ b/src/routes/routes.js
@@ -1,210 +1,180 @@
import { lazy } from "react";
-// Resource Management routes
-const resourceRoutes = [
+// 所有可用的路由配置
+export const allRoutes = [
{
- path: "team",
- component: lazy(() => import("@/pages/resource/team")),
- name: "团队管理",
- icon: "team",
- roles: ["OWNER"],
+ path: "dashboard",
+ component: lazy(() => import("@/pages/Dashboard")),
+ name: "仪表盘",
+ icon: "dashboard",
+ key: "dashboard",
},
{
- path: "role",
- component: lazy(() => import("@/pages/resource/role")),
- name: "角色管理",
- icon: "setting",
- roles: ["OWNER"],
- },
- {
- path: "bucket",
- component: lazy(() => import("@/pages/resource/bucket")),
- name: "对象存储",
- icon: "shop",
- roles: ["OWNER"],
- },
-
- {
- path: "task/edit/:id?",
- component: lazy(() => import("@/pages/resource/resourceTask/edit")),
- hidden: true,
- name: "新增/编辑任务",
- roles: ["OWNER"],
- },
-];
-
-// Company routes
-const companyRoutes = [
- {
- path: "quotation",
- component: lazy(() => import("@/pages/company/quotation")),
- name: "报价单",
- icon: "file",
- roles: ["ADMIN", "OWNER"],
- },
-
- {
- path: "quotaInfo/:id?", // 添加可选的 id 参数
- hidden: true,
- component: lazy(() => import("@/pages/company/quotation/detail")),
- name: "报价单详情",
- icon: "file",
- roles: ["ADMIN", "OWNER"],
- },
- {
- path: "project",
- component: lazy(() => import("@/pages/company/project")),
- name: "专案管理",
+ path: "resource",
+ component: lazy(() => import("@/pages/resource")),
+ name: "资源管理",
icon: "appstore",
- roles: ["ADMIN", "OWNER"],
- },
- {
- path: "task",
- component: lazy(() => import("@/pages/company/task")),
- name: "任务管理",
- icon: "appstore",
- roles: ["OWNER"],
+ key: "resource",
+ children: [
+ {
+ path: "team",
+ component: lazy(() => import("@/pages/resource/team")),
+ name: "团队管理",
+ icon: "team",
+ key: "resource/team",
+ },
+ {
+ path: "role",
+ component: lazy(() => import("@/pages/resource/role")),
+ name: "角色管理",
+ icon: "setting",
+ key: "resource/role",
+ },
+ {
+ path: "menu",
+ component: lazy(() => import("@/pages/resource/menu")),
+ name: "菜单管理",
+ icon: "menu",
+ key: "resource/menu",
+ },
+ {
+ path: "bucket",
+ component: lazy(() => import("@/pages/resource/bucket")),
+ name: "对象存储",
+ icon: "shop",
+ key: "resource/bucket",
+ },
+ {
+ path: "task/edit/:id?",
+ component: lazy(() => import("@/pages/resource/resourceTask/edit")),
+ name: "新增/编辑任务",
+ hidden: true,
+ key: "resource/task/edit",
+ }
+ ]
},
{
- path: "taskInfo/:id?",
- hidden:true,
- component: lazy(() => import("@/pages/company/task/detail")),
- name: "任务管理详情",
- icon: "appstore",
- roles: ["OWNER"],
- },
- {
- path: "serviceTemplate",
- component: lazy(() => import("@/pages/company/service")),
- name: "服务管理",
- icon: "container",
- roles: ["ADMIN", "OWNER"],
- },
- {
- path: "templateItemManage",
- component: lazy(() => import("@/pages/company/service/itemsManange")),
- name: "资源类型",
- icon: "container",
- roles: ["ADMIN", "OWNER"],
- },
- {
- path: "serviceTemplateInfo/:id?",
- hidden: true,
- component: lazy(() => import("@/pages/company/service/detail")),
- name: "服务模版详情",
- icon: "container",
- roles: ["ADMIN", "OWNER"],
- },
- {
- path: "quotaInfo/preview/:id?", // 添加可选的 id 参数
- hidden: true,
- component: lazy(() => import("@/pages/company/quotation/view")),
- name: "报价单预览",
- icon: "file",
- roles: ["ADMIN", "OWNER"],
- },
- {
- path: "customer",
- component: lazy(() => import("@/pages/company/customer")),
- name: "客户管理",
- icon: "user",
- roles: ["ADMIN", "OWNER"],
- },
- {
- path: "customerInfo/:id?",
- hidden: true,
- component: lazy(() => import("@/pages/company/customer/detail")),
- name: "客户详情",
- icon: "user",
- roles: ["ADMIN", "OWNER"],
- },
- {
- path: "supplier",
- component: lazy(() => import("@/pages/company/supplier")),
- name: "供应商管理",
- icon: "branches",
- roles: ["ADMIN", "OWNER"],
- },
- {
- path: "supplierInfo/:id?",
- hidden: true,
- component: lazy(() => import("@/pages/company/supplier/detail")),
- name: "供应商详情",
- icon: "branches",
- roles: ["ADMIN", "OWNER"],
- },
-
- {
- path: "projectInfo/:id?",
- hidden: true,
- component: lazy(() => import("@/pages/company/project/detail")),
- name: "专案管理详情",
- icon: "appstore",
- roles: ["ADMIN", "OWNER"],
- },
- {
- path: "projectView/:id?",
- hidden: true,
- component: lazy(() => import("@/pages/company/project/info")),
- name: "专案详情",
- icon: "appstore",
- roles: ["ADMIN", "OWNER"],
+ path: "company",
+ component: lazy(() => import("@/pages/company")),
+ name: "公司管理",
+ icon: "bank",
+ key: "company",
+ children: [
+ {
+ path: "quotation",
+ component: lazy(() => import("@/pages/company/quotation")),
+ name: "报价单",
+ icon: "file",
+ key: "company/quotation",
+ },
+ {
+ path: "quotaInfo/:id?",
+ component: lazy(() => import("@/pages/company/quotation/detail")),
+ name: "报价单详情",
+ icon: "file",
+ hidden: true,
+ key: "company/quotaInfo",
+ },
+ {
+ path: "project",
+ component: lazy(() => import("@/pages/company/project")),
+ name: "专案管理",
+ icon: "appstore",
+ key: "company/project",
+ },
+ {
+ path: "task",
+ component: lazy(() => import("@/pages/company/task")),
+ name: "任务管理",
+ icon: "appstore",
+ key: "company/task",
+ },
+ {
+ path: "taskInfo/:id?",
+ component: lazy(() => import("@/pages/company/task/detail")),
+ name: "任务管理详情",
+ icon: "appstore",
+ hidden: true,
+ key: "company/taskInfo",
+ },
+ {
+ path: "serviceTemplate",
+ component: lazy(() => import("@/pages/company/service")),
+ name: "服务管理",
+ icon: "appstore",
+ key: "company/serviceTemplate",
+ },
+ {
+ path: "supplier",
+ component: lazy(() => import("@/pages/company/supplier")),
+ name: "供应商管理",
+ icon: "branches",
+ key: "company/supplier",
+ },
+ {
+ path: "supplierInfo/:id?",
+ component: lazy(() => import("@/pages/company/supplier/detail")),
+ name: "供应商详情",
+ icon: "branches",
+ hidden: true,
+ key: "company/supplierInfo",
+ },
+ {
+ path: "projectInfo/:id?",
+ component: lazy(() => import("@/pages/company/project/detail")),
+ name: "专案管理详情",
+ icon: "appstore",
+ hidden: true,
+ key: "company/projectInfo",
+ },
+ {
+ path: "projectView/:id?",
+ component: lazy(() => import("@/pages/company/project/info")),
+ name: "专案详情",
+ icon: "appstore",
+ hidden: true,
+ key: "company/projectView",
+ }
+ ]
}
];
-const marketingRoutes = [];
+export const getRouteByKey = (key) => {
+ const keys = key.split('/').filter(Boolean);
+ let current = allRoutes;
+ let result = null;
-// const roleRoutes = [
-// {
-// path: "role",
-// component: lazy(() => import("@/pages/role")),
-// name: "角色管理",
-// icon: "setting",
-// roles: ["ADMIN", "OWNER"],
-// },
-// ];
+ keys.forEach(k => {
+ const found = current.find(r => r.path.split('/')[0] === k);
+ if (found) {
+ result = found;
+ current = found.children || [];
+ }
+ });
-export const generateRoutes = (role) => {
- return [
- {
- path: "dashboard",
- component: lazy(() => import("@/pages/Dashboard")),
- name: "仪表盘",
- icon: "dashboard",
- roles: ["ADMIN", "OWNER", "MEMBER"],
- },
- {
- path: "resource",
- component: lazy(() => import("@/pages/resource")),
- name: "资源管理",
- icon: "appstore",
- children: resourceRoutes.filter((route) => route.roles.includes(role)),
- roles: ["OWNER"],
- },
- {
- path: "company",
- component: lazy(() => import("@/pages/company")),
- name: "公司管理",
- icon: "bank",
- children: companyRoutes.filter((route) => route.roles.includes(role)),
- roles: ["ADMIN", "OWNER"],
- },
- // {
- // path: "marketing",
- // component: lazy(() => import("@/pages/marketing")),
- // name: "行销中心",
- // icon: "shopping",
- // children: marketingRoutes.filter((route) => route.roles.includes(role)),
- // roles: ["ADMIN", "OWNER"],
- // },
-
- // {
- // path: "role",
- // component: lazy(() => import("@/pages/role")),
- // name: "权限管理",
- // icon: "setting",
- // children: roleRoutes.filter((route) => route.roles.includes(role)),
- // roles: ["ADMIN", "OWNER"],
- // },
- ].filter((route) => route.roles.includes(role));
+ return result;
};
+
+export const flattenRoutes = (routes, parentPath = '') => {
+ return routes.reduce((acc, route) => {
+ const path = parentPath ? `${parentPath}/${route.path}` : route.path;
+ acc.push({ ...route, path });
+
+ if (route.children) {
+ acc.push(...flattenRoutes(route.children, path));
+ }
+
+ return acc;
+ }, []);
+};
+
+// 获取所有可选的路由选项(用于菜单管理)
+export const getAllRouteOptions = () => {
+ return flattenRoutes(allRoutes)
+ .filter(route => !route.hidden)
+ .map(route => ({
+ label: route.name,
+ value: route.key,
+ isLeaf: !route.children
+ }));
+};
\ No newline at end of file
diff --git a/src/utils/menuUtils.jsx b/src/utils/menuUtils.jsx
index f59f487..ca0ee54 100644
--- a/src/utils/menuUtils.jsx
+++ b/src/utils/menuUtils.jsx
@@ -1,7 +1,7 @@
import React from "react";
-import { generateRoutes } from "@/routes/routes";
import * as AntIcons from "@ant-design/icons";
import { ColorIcon } from "@/components/Layout/ColorIcon";
+import { allRoutes } from "@/routes/routes";
const getAntIcon = (iconName) => {
const iconKey = `${iconName.charAt(0).toUpperCase()}${iconName.slice(
@@ -10,25 +10,69 @@ const getAntIcon = (iconName) => {
return AntIcons[iconKey] ? React.createElement(AntIcons[iconKey]) : null;
};
-const generateMenuItems = (routes, parentPath = "") => {
+const generateMenuItems = (routes, menuKeys = [], parentPath = "") => {
return routes
- .filter((route) => !route.hidden)
+ .filter((route) => {
+ if (!menuKeys.length) return !route.hidden;
+
+ const isRouteAllowed = menuKeys.includes(route.key);
+
+ // 如果有子路由,只要子路由中有被授权的,父路由就应该显示
+ if (route.children) {
+ const hasAllowedChildren = route.children.some(child =>
+ menuKeys.includes(child.key) ||
+ (child.children && child.children.some(grandChild => menuKeys.includes(grandChild.key)))
+ );
+ return hasAllowedChildren || isRouteAllowed;
+ }
+
+ return isRouteAllowed;
+ })
.map((route) => {
const fullPath = `${parentPath}/${route.path}`.replace(/\/+/g, "/");
const icon = route.icon &&
;
const menuItem = {
- key: fullPath,
+ key: route.key, // 使用 key 而不是 fullPath
icon,
label: route.name,
};
if (route.children) {
- menuItem.children = generateMenuItems(route.children, fullPath);
+ const filteredChildren = generateMenuItems(route.children, menuKeys, fullPath);
+ if (filteredChildren.length > 0) {
+ menuItem.children = filteredChildren;
+ }
}
-
+
return menuItem;
});
};
-export const getMenuItems = (role) => generateMenuItems(generateRoutes(role));
+
+export const getMenuItems = (menuKeys = []) => generateMenuItems(allRoutes, menuKeys);
+export const filterRoutesByMenuKeys = (routes, menuKeys) => {
+ if (!Array.isArray(routes) || !Array.isArray(menuKeys)) {
+ return [];
+ }
+ return routes.reduce((acc, route) => {
+ // 检查当前路由的 key 是否在 menuKeys 中
+ const isRouteAllowed = menuKeys.includes(route.key);
+
+ // 递归处理子路由
+ let filteredChildren = [];
+ if (route.children) {
+ filteredChildren = filterRoutesByMenuKeys(route.children, menuKeys);
+ }
+
+ // 如果当前路由被允许或者有被允许的子路由,则保留该路由
+ if (isRouteAllowed || filteredChildren.length > 0) {
+ acc.push({
+ ...route,
+ children: filteredChildren.length > 0 ? filteredChildren : undefined
+ });
+ }
+ return acc;
+ }, []);
+};
+export { generateMenuItems, getAntIcon };