实现列级权限的前端设计与踩坑经验分享
列级权限这个需求差点把我整崩溃
最近在做一个后台管理系统,客户提了个需求:不同角色能看到的表格列不一样。一开始觉得挺简单,不就是动态渲染嘛。结果实际做起来才发现,这玩意儿坑太多了。
折腾了大半天,最后发现最靠谱的方案是基于后端返回的权限配置来控制列显示。这里我踩了个坑,一开始想纯前端处理,后来发现根本行不通,因为权限规则太复杂了。
先说说我走过的弯路
最初我是想着直接在前端写死权限规则,比如:
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住大部分场景了。如果你有更好的实现方式,欢迎评论区交流。
最后提醒下,记得做好权限校验,前后端都要防着点,别让用户随便改请求就能看到不该看的数据。

暂无评论