Ant Design Pro从入门到实战踩坑全记录

一昕彤 框架 阅读 1,267
赞 10 收藏
二维码
手机扫码查看
反馈

终于搞定了,ProTable动态列显示把我折腾惨了

上周遇到个需求,表格需要根据权限动态显示某些列,本来以为Ant Design Pro的ProTable应该很好搞定,结果发现事情没那么简单。这里我踩了不少坑,最后总算找到了靠谱的解决方案。

Ant Design Pro从入门到实战踩坑全记录

一开始我想着直接在columns配置里加个条件判断,比如这样:

const columns = [
  {
    title: '用户名',
    dataIndex: 'username',
  },
  {
    title: '邮箱',
    dataIndex: 'email',
  },
  userHasPermission && {
    title: '管理员操作',
    dataIndex: 'adminAction',
    render: () => <Button>删除用户</Button>,
  },
].filter(Boolean);

这么写看似没问题,但在ProTable里有个bug,就是当你第一次渲染后,如果权限发生变化导致列增减,表格的列宽计算会出现问题,而且排序、筛选等功能也会错乱。折腾了半天发现这是ProTable的一个已知问题,社区里也有不少人反馈过。

真正的解决方案是用state管理columns

后来试了下发现,得把columns作为一个state来管理才行。具体做法是先把所有可能的列定义好,然后根据权限状态来过滤出真正需要显示的列。

import { useState, useEffect } from 'react';
import { ProTable, Table } from '@ant-design/pro-components';

const UserManagement = () => {
  const [permissions, setPermissions] = useState([]);
  const [currentColumns, setCurrentColumns] = useState([]);
  
  // 假设这是从后端获取的权限数据
  useEffect(() => {
    fetchUserPermissions();
  }, []);

  // 所有可能的列定义
  const allColumns = [
    {
      title: 'ID',
      dataIndex: 'id',
      hideInSearch: true,
    },
    {
      title: '用户名',
      dataIndex: 'username',
      width: 120,
    },
    {
      title: '邮箱',
      dataIndex: 'email',
      width: 200,
    },
    {
      title: '注册时间',
      dataIndex: 'createTime',
      valueType: 'dateTime',
      hideInSearch: true,
    },
    // 需要权限才能看到的列
    {
      title: '积分',
      dataIndex: 'points',
      hideInSearch: true,
      permissionKey: 'view_points', // 自定义权限标识
    },
    {
      title: '操作',
      key: 'action',
      valueType: 'option',
      permissionKey: 'manage_users', // 只有管理员才能操作
      render: (text, record) => [
        <a key="edit" onClick={() => handleEdit(record)}>编辑</a>,
        <a key="delete" onClick={() => handleDelete(record)}>删除</a>,
      ],
    },
  ];

  // 根据权限过滤列
  useEffect(() => {
    if (permissions.length > 0) {
      const filteredColumns = allColumns.filter(column => {
        // 如果没有permissionKey,则总是显示
        if (!column.permissionKey) return true;
        // 如果有permissionKey,则检查是否有对应权限
        return permissions.includes(column.permissionKey);
      });
      setCurrentColumns(filteredColumns);
    }
  }, [permissions]);

  const handleEdit = (record) => {
    console.log('edit:', record);
  };

  const handleDelete = (record) => {
    console.log('delete:', record);
  };

  const fetchUserPermissions = async () => {
    try {
      // 模拟API调用
      const response = await fetch('https://jztheme.com/api/user/permissions');
      const data = await response.json();
      setPermissions(data.permissions || []);
    } catch (error) {
      console.error('获取权限失败:', error);
      // 降级处理,给默认权限
      setPermissions(['view_points']); // 只能看积分,不能删用户
    }
  };

  return (
    <ProTable
      columns={currentColumns}
      request={async (params) => {
        const response = await fetch(https://jztheme.com/api/users?page=${params.current}&amp;size=${params.pageSize});
        const data = await response.json();
        return {
          data: data.users,
          success: true,
          total: data.total,
        };
      }}
      rowKey="id"
      pagination={{
        defaultPageSize: 10,
        showSizeChanger: true,
      }}
      search={{
        labelWidth: 'auto',
      }}
      dateFormatter="string"
    />
  );
};

还有个小坑要注意

这里我还遇到个坑,就是ProTable的列配置如果包含render函数的话,在重新渲染时可能会出现React的key警告。为了保险起见,最好给每列都加上key属性,特别是那种包含复杂render的列。

另外就是权限验证那块,我这里用了自定义的permissionKey字段,这样比较灵活,可以根据不同的权限标识来决定是否显示某列。但是要注意一点,权限数据加载前的处理很重要,不然会出现闪屏或者布局错乱的问题。

上面代码里的降级处理就是为了避免权限接口挂了导致整个页面崩掉的情况,虽然用户体验不好,但至少页面还能正常使用。

优化版本,避免重复请求

实际项目中我还加了一些优化,比如权限信息缓存、错误重试机制等:

// 权限钩子,带缓存和重试
const useUserPermissions = () => {
  const [permissions, setPermissions] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const loadPermissions = async () => {
      setLoading(true);
      try {
        // 先尝试从localStorage读取缓存
        const cached = localStorage.getItem('user_permissions');
        if (cached) {
          const { data, timestamp } = JSON.parse(cached);
          // 缓存5分钟内有效
          if (Date.now() - timestamp < 5 * 60 * 1000) {
            setPermissions(data);
            setLoading(false);
            return;
          }
        }

        const response = await fetch('https://jztheme.com/api/user/permissions', {
          headers: {
            Authorization: Bearer ${localStorage.getItem(&#039;token&#039;)},
          },
        });
        
        if (!response.ok) throw new Error('网络请求失败');
        
        const result = await response.json();
        const permissionList = result.permissions || [];
        
        // 存入缓存
        localStorage.setItem('user_permissions', JSON.stringify({
          data: permissionList,
          timestamp: Date.now(),
        }));
        
        setPermissions(permissionList);
      } catch (error) {
        console.error('权限加载失败:', error);
        // 加载失败也给个默认值,保证页面正常显示
        setPermissions(['view_basic']);
      } finally {
        setLoading(false);
      }
    };

    loadPermissions();
  }, []);

  return { permissions, loading };
};

// 在组件中使用
const UserManagementOptimized = () => {
  const { permissions, loading } = useUserPermissions();

  // 其余代码保持不变...
};

这样处理后,即使网络状况不好,也不会影响用户体验。当然这里还可以加更多细节,比如错误重试次数限制、不同角色的权限差异等等。

以上是我踩坑后的总结,ProTable动态列显示确实需要注意不少细节,特别是状态管理和权限缓存这块。如果你有更好的方案欢迎评论区交流。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论