ソースを参照

路由url层级化

zhangzl 2 ヶ月 前
コミット
4c2d8c414b

+ 3 - 3
new-react-admin-ui/src/App.tsx

@@ -42,19 +42,19 @@ export const App = () => {
         requireAuth
       >
         <Resource
-          name="users"
+          name="system/users"
           list={UserList}
           edit={UserEdit}
           create={UserCreate}
         />
         <Resource
-          name="roles"
+          name="system/roles"
           list={RoleList}
           edit={RoleEdit}
           create={RoleCreate}
         />
         <Resource
-          name="menus"
+          name="system/menus"
           list={MenuList}
           edit={MenuEdit}
           create={MenuCreate}

+ 55 - 7
new-react-admin-ui/src/adapters/ruoyiDataProvider.ts

@@ -152,6 +152,14 @@ export const ruoyiDataProvider: RuoyiDataProvider = {
     const { page = 1, perPage = 10 } = params.pagination || {};
     const { field, order } = params.sort || {};
     
+    // 解析层级化的资源名称,例如 "system/users" -> "users"
+    const getFlatResourceName = (resource: string): string => {
+      const parts = resource.split('/');
+      return parts[parts.length - 1]; // 取最后一部分作为扁平化资源名
+    };
+    
+    const flatResource = getFlatResourceName(resource);
+
     // 映射排序字段:将React Admin的默认字段名映射为若依后端的字段名
     const getMappedSortField = (resource: string, field: string) => {
       const fieldMappings: Record<string, Record<string, string>> = {
@@ -167,7 +175,7 @@ export const ruoyiDataProvider: RuoyiDataProvider = {
       return fieldMappings[resource]?.[field] || field;
     };
     
-    const mappedField = field ? getMappedSortField(resource, field) : undefined;
+    const mappedField = field ? getMappedSortField(flatResource, field) : undefined;
     
     const queryParams = new URLSearchParams({
       pageNum: page.toString(),
@@ -176,7 +184,7 @@ export const ruoyiDataProvider: RuoyiDataProvider = {
       ...params.filter,
     });
 
-    switch (resource) {
+    switch (flatResource) {
       case 'users': {
         const data = await request<UserPageResult>(`/system/user/list?${queryParams}`);
         // 将userId映射为id,以满足React Admin的数据格式要求
@@ -282,7 +290,15 @@ export const ruoyiDataProvider: RuoyiDataProvider = {
 
   // 获取单条记录
   getOne: async (resource: string, params: GetOneParams): Promise<GetOneResult> => {
-    switch (resource) {
+    // 解析层级化的资源名称
+    const getFlatResourceName = (resource: string): string => {
+      const parts = resource.split('/');
+      return parts[parts.length - 1];
+    };
+    
+    const flatResource = getFlatResourceName(resource);
+    
+    switch (flatResource) {
       case 'users': {
         const data = await request<any>(`/system/user/${params.id}`);
         // 将userId映射为id,以满足React Admin的数据格式要求
@@ -336,7 +352,15 @@ export const ruoyiDataProvider: RuoyiDataProvider = {
 
   // 创建记录
   create: async (resource: string, params: CreateParams): Promise<CreateResult> => {
-    switch (resource) {
+    // 解析层级化的资源名称
+    const getFlatResourceName = (resource: string): string => {
+      const parts = resource.split('/');
+      return parts[parts.length - 1];
+    };
+    
+    const flatResource = getFlatResourceName(resource);
+    
+    switch (flatResource) {
       case 'users': {
         // 确保roleIds参数以数组格式发送
         const requestData = {
@@ -381,7 +405,15 @@ export const ruoyiDataProvider: RuoyiDataProvider = {
 
   // 更新记录
   update: async (resource: string, params: UpdateParams): Promise<UpdateResult> => {
-    switch (resource) {
+    // 解析层级化的资源名称
+    const getFlatResourceName = (resource: string): string => {
+      const parts = resource.split('/');
+      return parts[parts.length - 1];
+    };
+    
+    const flatResource = getFlatResourceName(resource);
+    
+    switch (flatResource) {
       case 'users': {
         // 确保roleIds参数以数组格式发送
         const requestData = {
@@ -429,7 +461,15 @@ export const ruoyiDataProvider: RuoyiDataProvider = {
 
   // 删除记录
   delete: async (resource: string, params: DeleteParams): Promise<DeleteResult> => {
-    switch (resource) {
+    // 解析层级化的资源名称
+    const getFlatResourceName = (resource: string): string => {
+      const parts = resource.split('/');
+      return parts[parts.length - 1];
+    };
+    
+    const flatResource = getFlatResourceName(resource);
+    
+    switch (flatResource) {
       case 'users': {
         await request<Result>(`/system/user/${params.id}`, {
           method: 'DELETE',
@@ -466,7 +506,15 @@ export const ruoyiDataProvider: RuoyiDataProvider = {
 
   // 批量获取记录 - 由于后台没有批量接口,通过循环调用单个接口实现
   getMany: async (resource: string, params: GetManyParams): Promise<GetManyResult> => {
-    switch (resource) {
+    // 解析层级化的资源名称
+    const getFlatResourceName = (resource: string): string => {
+      const parts = resource.split('/');
+      return parts[parts.length - 1];
+    };
+    
+    const flatResource = getFlatResourceName(resource);
+    
+    switch (flatResource) {
       case 'users': {
         // 循环调用单个用户接口获取多个用户信息
         const promises = params.ids.map(id => 

+ 23 - 19
new-react-admin-ui/src/components/CustomMenu.tsx

@@ -25,24 +25,28 @@ import {
 import { menuService, MenuItem } from '@/services/menuService';
 import { useMenuPermission } from '@/hooks/usePermission';
 
-// 菜单项映射到React Admin资源
-const menuItemToResourceMap: Record<string, string> = {
-  'system/user': 'users',
-  'system/role': 'roles',
-  'system/menu': 'menus',
-  'system/dict': 'dict',
-  'system/config': 'config',
-  'system/notice': 'notice',
-  'monitor/online': 'online',
-  'monitor/job': 'job',
-  'monitor/druid': 'druid',
-  'monitor/server': 'server',
-  'monitor/cache': 'cache',
-  'monitor/cacheList': 'cacheList',
-  'tool/build': 'build',
-  'tool/gen': 'gen',
-  'tool/swagger': 'swagger',
-  'dashboard': 'dashboard',
+// 智能资源名称转换函数
+const getResourceName = (fullPath: string): string => {
+  // 特殊处理:dashboard不需要层级路径
+  if (fullPath === 'dashboard') {
+    return 'dashboard';
+  }
+  
+  // 对于其他路径,直接将最后一个路径段转换为复数形式
+  const pathSegments = fullPath.split('/');
+  const lastSegment = pathSegments[pathSegments.length - 1];
+  
+  // 简单的复数转换规则
+  let pluralSegment = lastSegment;
+  if (lastSegment.endsWith('y')) {
+    pluralSegment = lastSegment.slice(0, -1) + 'ies';
+  } else if (!lastSegment.endsWith('s')) {
+    pluralSegment = lastSegment + 's';
+  }
+  
+  // 替换最后一个路径段为复数形式
+  pathSegments[pathSegments.length - 1] = pluralSegment;
+  return pathSegments.join('/');
 };
 
 // 根据菜单类型获取图标
@@ -71,7 +75,7 @@ const renderMenuTree = (menus: MenuItem[], basePath = '', expandedItems: Set<str
     
     // 只渲染菜单类型为C(菜单)的项
     if (menu.menuType === 'C' && menu.path && fullPath) {
-      const resourceName = menuItemToResourceMap[fullPath] || menu.path;
+      const resourceName = getResourceName(fullPath);
       
       return (
         <MenuItemLink

+ 1 - 1
new-react-admin-ui/src/pages/menus/MenuList.tsx

@@ -36,7 +36,7 @@ export const MenuList = () => {
 
   // 处理编辑菜单
   const handleEdit = (menu: any) => {
-    redirect(`/menus/${menu.menuId || menu.id}/edit`);
+    redirect(`/system/menus/${menu.menuId || menu.id}/edit`);
   };
 
   // 处理删除菜单

+ 0 - 9
new-react-admin-ui/src/pages/roles/MenuPermissionTree.tsx

@@ -190,18 +190,9 @@ const MenuPermissionTree: React.FC<MenuPermissionTreeProps> = ({
 
   // 处理外部value变化
   useEffect(() => {
-    console.log('MenuPermissionTree: 接收到外部value变化:', {
-      value,
-      valueLength: value?.length,
-      valueType: typeof value,
-      isArray: Array.isArray(value)
-    });
-    
     if (value && Array.isArray(value)) {
-      console.log('MenuPermissionTree: 设置checkedKeys:', value);
       setCheckedKeys(value);
     } else {
-      console.log('MenuPermissionTree: value无效,设置为空数组');
       setCheckedKeys([]);
     }
   }, [value]);

+ 0 - 32
new-react-admin-ui/src/pages/roles/RoleEdit.tsx

@@ -33,49 +33,17 @@ export const RoleEdit = (props: any) => {
   // 获取角色ID - 使用useGetRecordId钩子获取记录ID
   const roleId = recordId || record?.id || props.id;
 
-  console.log('RoleEdit组件加载:', {
-    props,
-    record,
-    recordId,
-    roleId,
-    hasRecord: !!record,
-    hasPropsId: !!props.id,
-    hasRecordId: !!recordId
-  });
-
   // 加载角色已有的菜单权限数据
   useEffect(() => {
     const loadRoleMenuData = async () => {
-      console.log('RoleEdit: 开始加载角色菜单权限数据', {
-        recordId: record?.id,
-        propsId: props.id,
-        useGetRecordId: recordId,
-        roleId,
-        hasRecord: !!record,
-        hasPropsId: !!props.id,
-        hasRecordId: !!recordId,
-        hasRoleId: !!roleId
-      });
-      
       if (roleId) {
         try {
-          console.log('RoleEdit: 调用getRoleMenuList,roleId:', roleId);
           const roleMenuData = await dataProvider.getRoleMenuList(roleId);
-          console.log('RoleEdit: 获取到的角色菜单数据:', {
-            roleMenuData,
-            checkedKeys: roleMenuData.checkedKeys,
-            checkedKeysLength: roleMenuData.checkedKeys?.length
-          });
           setMenuIds(roleMenuData.checkedKeys || []);
-          console.log('RoleEdit: 设置menuIds完成:', roleMenuData.checkedKeys || []);
         } catch (error) {
           console.error('加载角色菜单权限数据失败:', error);
           notify('加载角色菜单权限数据失败', { type: 'error' });
         }
-      } else {
-        console.log('RoleEdit: 没有有效的角色ID,跳过数据加载');
-        console.log('RoleEdit: 当前URL:', window.location.href);
-        console.log('RoleEdit: 路由参数:', window.location.search);
       }
     };
 

+ 1 - 1
new-react-admin-ui/src/pages/users/UserList.tsx

@@ -33,7 +33,7 @@ export const UserList = (props: any) => (
     {...props}
     filters={<UserFilter />}
     sort={{ field: 'createTime', order: 'DESC' }}
-    perPage={20}
+    perPage={25}
   >
     <Datagrid rowClick="edit">
       <TextField source="userId" label="用户ID" />

+ 0 - 133
new-react-admin-ui/src/utils/routeMapping.ts

@@ -1,133 +0,0 @@
-// 路径映射配置中心
-interface RouteMappingConfig {
-  // 后端菜单路径 -> 前端路由路径的映射
-  pathMappings: Record<string, string>;
-  // 特殊路径处理规则
-  specialPaths: string[];
-  // 默认路由前缀
-  defaultPrefix: string;
-}
-
-// 路径映射配置
-export const routeMappingConfig: RouteMappingConfig = {
-  pathMappings: {
-    // 系统管理模块
-    'system/user': '/users',
-    'system/role': '/roles', 
-    'system/menu': '/menus',
-    'system/dict': '/dicts',
-    'system/config': '/configs',
-    'system/notice': '/notices',
-    'system/operlog': '/operlogs',
-    'system/logininfor': '/logininfors',
-    
-    // 监控模块
-    'monitor/online': '/online',
-    'monitor/job': '/jobs',
-    'monitor/druid': '/druid',
-    'monitor/server': '/server',
-    'monitor/cache': '/cache',
-    
-    // 工具模块
-    'tool/build': '/build',
-    'tool/gen': '/gen',
-    'tool/swagger': '/swagger',
-  },
-  specialPaths: ['dashboard', 'account'],
-  defaultPrefix: '/'
-};
-
-// 预定义资源路由配置
-interface ResourceRoute {
-  name: string; // 资源名称(单数)
-  path: string; // 路由路径(复数)
-}
-
-// 基于App.tsx中的Resource配置预定义资源路由
-const predefinedResourceRoutes: ResourceRoute[] = [
-  { name: 'user', path: '/users' },
-  { name: 'role', path: '/roles' },
-  { name: 'menu', path: '/menus' },
-];
-
-// 智能路径映射函数
-export const mapMenuPathToRoutePath = (menuPath: string): string => {
-  if (!menuPath) return '/dashboard';
-  
-  // 1. 检查是否以/开头(已经是完整路径)
-  if (menuPath.startsWith('/')) {
-    return menuPath;
-  }
-  
-  // 2. 检查预定义映射
-  if (routeMappingConfig.pathMappings[menuPath]) {
-    return routeMappingConfig.pathMappings[menuPath];
-  }
-  
-  // 3. 检查特殊路径
-  if (routeMappingConfig.specialPaths.includes(menuPath)) {
-    return `/${menuPath}`;
-  }
-  
-  // 4. 检查预定义资源路由
-  const predefinedRoute = predefinedResourceRoutes.find(route => 
-    route.name === menuPath || route.path === `/${menuPath}`
-  );
-  if (predefinedRoute) {
-    return predefinedRoute.path;
-  }
-  
-  // 5. 智能单复数转换(备用方案)
-  return `/${toPlural(menuPath)}`;
-};
-
-// 单复数转换函数(包含不规则复数处理)
-const toPlural = (word: string): string => {
-  // 不规则复数
-  const irregularPlurals: Record<string, string> = {
-    'person': 'people',
-    'child': 'children',
-    'man': 'men',
-    'woman': 'women',
-    'mouse': 'mice',
-    'goose': 'geese',
-    'foot': 'feet',
-    'tooth': 'teeth',
-    'ox': 'oxen',
-  };
-  
-  if (irregularPlurals[word]) {
-    return irregularPlurals[word];
-  }
-  
-  // 规则复数转换
-  if (word.endsWith('s') || word.endsWith('x') || word.endsWith('z') || 
-      word.endsWith('ch') || word.endsWith('sh')) {
-    return word + 'es';
-  }
-  
-  if (word.endsWith('y') && !['a', 'e', 'i', 'o', 'u'].includes(word[word.length - 2])) {
-    return word.slice(0, -1) + 'ies';
-  }
-  
-  if (word.endsWith('f') || word.endsWith('fe')) {
-    return word.endsWith('fe') ? word.slice(0, -2) + 'ves' : word.slice(0, -1) + 'ves';
-  }
-  
-  return word + 's';
-};
-
-// 获取所有可用的路由路径(用于权限验证)
-export const getAllAvailableRoutes = (): string[] => {
-  const mappedRoutes = Object.values(routeMappingConfig.pathMappings);
-  const specialRoutes = routeMappingConfig.specialPaths.map(path => `/${path}`);
-  const predefinedRoutes = predefinedResourceRoutes.map(route => route.path);
-  
-  return [...new Set([...mappedRoutes, ...specialRoutes, ...predefinedRoutes])];
-};
-
-// 验证路径是否有效
-export const isValidRoute = (path: string): boolean => {
-  const availableRoutes = getAllAvailableRoutes();
-  return availableRoutes.includes(path);
-};