前面提到,为了实现权限相关的需求而选择了React-admin
框架,该框架已经对列做了封装,先看一下它封装的成果:
import { Operator } from 'src/library/components';
....
{
title: '操作', dataIndex: 'operator',
render: (value, record) => {
const { id, name } = record;
let items = []
if (checkAuth("systemmanage_authmange_data_role_detail")) {
items.push({
label: '查看',
icon: 'eye',
onClick: (e) => {
e.stopPropagation();
this.getPropertyAuth(record)
},
})
}
if (checkAuth("systemmanage_authmange_data_role_modify")) {
items.push({
label: '修改',
icon: 'form',
onClick: (e) => {
e.stopPropagation();
this.setState({ visible: true, id, record, type: "edit", drawerTitle: "修改资产" });
},
})
}
if (checkAuth("systemmanage_authmange_data_role_delete")) {
items.push({
label: '删除',
color: 'red',
icon: 'delete',
confirm: {
title: `您确定删除"${name}"?`,
onConfirm: (e) => {
e.stopPropagation();
this.handleDelete(id);
},
},
})
}
return <Operator items={items} />;
},
},
上面是一个完整的Operator
列,最后只要把我们需要的操作放到数据中就可以了。效果如下
用起来确实不错。但是试想一下,每个模块基本上都是这种,或者类似这种的,就需要每个模块都要定义那一坨,看起来很不舒服。所以打算在框架上再做一次封装。既然框架定义的Operator
组件只需要一个参数,那么我就自己定义动态生成items
,然后将items
传递给组件就可以了。
所谓的封装组件就是共性的封装,看了看每个item
的定义,几乎都是一样的,那么第一层封装应该是把这些共有的东西全拿出去。
从上面的代码可以看出对于查/改/删
的操作,会需要不同的数据及不同的操作,那么在封装的时候,我们先提取第一层公有的属性。
record
:{}, 必须,keys
:[], 必须,
无论是删改查哪个从操作似乎都需要record
相关的属性,不管是删除时往后端发送的id
还是用于删除提示的name
,都离不开record
,所以第一个要提取的属性就应该是record
。
因为这个框架主要是为了做权限操作的,所以对于任何操作都会设置一个key
用来标识操作,比如create
操作的key可能是这样的xxxx-create
,只要根据这个key去登陆用户的权限列表中判断是否存在即可,所以key
是必须的。但是还不满足,由于是程序去执行代码,他并不知道你的key
对应的是什么操作,所以,你需要给key
绑定额外的属性,那么可能就是这样的[{key:“xxx_create”,type:“create”}]。因为要作为所有模块的通用组件,所以不能单独根据_create
这种东西去判断。这些其实还是不够,因为你会发现每个操作项都会有其独特函数去执行一些操作,比如点击查看显示一个抽屉去显示详情,点击删除会显示是否删除的提示框…。所以我们需要额外的属性callback
去处理函数回调。
综上所述我们的定义的一个完整的keys
应该是这样的:
{ key: "systemmanage_authmange_department_detail", type: "detail", callback:()=> this.onOperator(record, "detail") },
- key:用于判断是否有该权限
- type:操作类型,根据操作类型添加不同的操作
- callback:回调函数
在模块下调用函数:
const items = getOperatorMenus([
{ key: "systemmanage_authmange_department_detail", type: "detail", callback:()=> this.onOperator(record, "detail") },
{ key: "systemmanage_authmange_department_modify", type: "modify", callback:()=> this.onOperator(record, "modify"), },
{ key: "systemmanage_authmange_department_delete", type: "delete", callback:()=> this.onOperator(record, "delete") },
], record)
那么在封装的组件或者函数中就可以这么去实现了:
import { checkAuth } from 'src/utils/auth'
/**
* 根据路由的操作按钮对应的key生成operator列
* @param {} keys 路由下的keys值
*/
export const getOperatorMenus = (keys, record) => {
const { name } = record;
let items = []
keys && keys.map(({key, type, callback}) => {
if (checkAuth(key)) {// 判断该key值是否在权限列表中,如果在则添加,不在则不添加
switch (type) {
case "detail":
items.push({
label: '查看',
icon: 'eye',
onClick: (e) => {
e.stopPropagation();
callback()//回调函数
},
})
break;
case "modify":
items.push({
label: '修改',
icon: 'form',
onClick: (e) => {
e.stopPropagation();
callback()
},
})
break;
case "delete":
items.push({
label: '删除',
color: 'red',
icon: 'delete',
confirm: {
title: `您确定删除"${name}"?`,
onConfirm: (e) => {
e.stopPropagation();
callback()
},
},
})
break;
default:
break;
}
}
return ""
})
return items
}
这样做的一层封装,只要在模块下调用这个函数就行了,不需要像刚开始的时候那样要在每个模块都需要写一大坨。这需要简化成这样就可以了:
{
title: '操作', dataIndex: 'operator',
render: (value, record) => {
const items = getOperatorMenus([
{ key: "systemmanage_authmange_department_detail", type: "detail", callback:()=> this.onOperator(record, "detail") },
{ key: "systemmanage_authmange_department_modify", type: "modify", callback:()=> this.onOperator(record, "modify"), },
{ key: "systemmanage_authmange_department_delete", type: "delete", callback:()=> this.onOperator(record, "delete") },
], record)
return <Operator items={items} />;
},
},
是不是看起来清爽多了。
有人肯定认为还能再次封装,比如在每个操作中我们可以看出有很多共同的东西:
{
label: '查看',
icon: 'eye',
onClick: (e) => {
e.stopPropagation();
callback()
},
}
比如上面这段代码,删改查
几乎都一样,通用的属性:
- label:显示的名称
- icon:图标
- onClick:操作事件
- color:颜色
- …
可以抽离出来一个组件,然后动态生成这些组件。
当然如果你感兴趣,需要再次封装,那我并不反对。只要是具有共性的我们都可以封装,只是根据需求,根据场景,根据时间选择自己的封装方式。
当然也并不是封装的越彻底越好,比如antd.pro
的封装,已经算是很彻底了,机会我们不需要做任何的配置,比如webpack
的配置,你只要专心做业务模块的开发就可以了。用起来是很爽,但是v4
版本使用了dva
的方式,给我们带来了大量的模板,在开发的时候尤其复杂,如果是新手接触会发现操作起来难度很高。但是v5
版本就废弃了dva
的方式管理数据流,改用了react
自带的redux
技术,让技术回归最初。
有人说榨汁机多方便,能帮我去掉果皮和一些不需要的部分,直接可以拿来喝。但是有调查表明,榨汁机在水果打碎的过程中,会破坏里面的营养成分,反而得不到我们需要的营养成分。我们吃水果有时是为了补充营养,结果发现用完榨汁机就跟和白开水一样。那我们投入了榨汁机的成本就反而得不偿失。
开发中没有所谓的最好
,只有最合适
。