实现列级权限的前端设计与踩坑经验分享

♫杏花 安全 阅读 828
赞 14 收藏
二维码
手机扫码查看
反馈

列级权限这个需求差点把我整崩溃

最近在做一个后台管理系统,客户提了个需求:不同角色能看到的表格列不一样。一开始觉得挺简单,不就是动态渲染嘛。结果实际做起来才发现,这玩意儿坑太多了。

实现列级权限的前端设计与踩坑经验分享

折腾了大半天,最后发现最靠谱的方案是基于后端返回的权限配置来控制列显示。这里我踩了个坑,一开始想纯前端处理,后来发现根本行不通,因为权限规则太复杂了。

先说说我走过的弯路

最初我是想着直接在前端写死权限规则,比如:

const userRole = 'admin'; // 假设当前用户是管理员
const columns = [
  { key: 'name', title: '姓名' },
  { key: 'age', title: '年龄' },
  { key: 'salary', title: '薪资', visible: ['admin'] }
];

const visibleColumns = columns.filter(col => {
  return !col.visible || col.visible.includes(userRole);
});

看起来很美好对吧?但实际情况是,权限规则经常变,而且有些列的显示还跟其他条件挂钩,比如部门、职级这些。每次改权限规则就得改代码,维护成本太高了。

后来试了下把权限配置写在配置文件里,但问题又来了:每次更新权限配置还得重新打包部署,客户都快被我搞疯了。

最终解决方案:后端驱动的列权限控制

最后还是得靠后端来控制。思路其实很简单:后端根据用户角色返回对应的列配置,前端只需要按照配置渲染就行。

这里是核心代码部分:

// 模拟从后端获取列配置
async function fetchColumnConfig() {
  const response = await fetch('https://jztheme.com/api/column-config');
  return response.json();
}

// 表格组件
function DataTable({ data }) {
  const [columns, setColumns] = useState([]);

  useEffect(() => {
    fetchColumnConfig().then(config => {
      setColumns(config);
    });
  }, []);

  return (
    <table>
      <thead>
        <tr>
          {columns.map(col => (
            <th key={col.key}>{col.title}</th>
          ))}
        </tr>
      </thead>
      <tbody>
        {data.map((row, index) => (
          <tr key={index}>
            {columns.map(col => (
              <td key={col.key}>{row[col.key]}</td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

几个关键点要注意

这里有几个坑是我踩过的,给大家提个醒:

  • 列宽自适应问题:动态列会导致表格宽度计算异常,需要设置min-width
  • 排序和筛选功能:这些功能要根据实际显示的列来动态生成
  • 缓存问题:权限变更后要及时刷新列配置

特别说下缓存这个问题,我在这里栽了个大跟头。一开始没处理好,导致用户切换角色后看到的还是原来的列。最后加了个版本号机制解决了:

// 后端返回的配置带上版本号
{
  "version": "1.0.1",
  "columns": [...]
}

// 前端缓存时加上版本号校验
const cachedVersion = localStorage.getItem('columnConfigVersion');
if (cachedVersion !== config.version) {
  localStorage.setItem('columnConfig', JSON.stringify(config));
  localStorage.setItem('columnConfigVersion', config.version);
}

技术细节补充

关于权限配置的具体格式,我们最后定的是这样的:

[
  {
    "key": "name",
    "title": "姓名",
    "visibleRoles": ["admin", "manager"],
    "sortable": true,
    "filterable": true
  },
  {
    "key": "age",
    "title": "年龄",
    "visibleRoles": ["admin"],
    "sortable": false,
    "filterable": false
  }
]

这里有个小技巧:我们在后端用位运算来处理权限判断,这样可以大幅提升性能。比如:

const roleMap = {
  admin: 1 << 0,  // 1
  manager: 1 << 1, // 2
  editor: 1 << 2   // 4
};

// 判断是否包含某个角色
function hasRole(roles, targetRole) {
  return (roles & targetRole) === targetRole;
}

// 示例:8表示包含editor和manager (2+4=6)
console.log(hasRole(6, roleMap.editor)); // true

还有个小瑕疵

虽然整体方案跑通了,但现在还有个小问题:当列特别多的时候,首次加载会有明显的闪烁。暂时还没找到特别好的解决办法,目前的workaround是加了个loading状态:

function DataTable({ data }) {
  const [columns, setColumns] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchColumnConfig()
      .then(config => {
        setColumns(config);
        setLoading(false);
      });
  }, []);

  if (loading) return <div>加载中...</div>;

  return (
    // 省略其他代码
  );
}

以上是我踩坑后的总结

列级权限这个需求看似简单,但实际做起来要考虑的东西还挺多。我的这个方案也不是完美无缺,但至少能cover住大部分场景了。如果你有更好的实现方式,欢迎评论区交流。

最后提醒下,记得做好权限校验,前后端都要防着点,别让用户随便改请求就能看到不该看的数据。

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

暂无评论