从资源库组件选择 可配置的资源选择器
a. 配置初始选中路径
b. 配置单选多选
c. 配置资源选择类型
d. 配置资源选择规则,如不可选同名资源,选了⽗禁⽤⼦,选了⽗关联⼦
e. 配置视图模式
组件使⽤
a. 引⼊组件PickResourceModal(有弹窗外壳,直接传⼊visible控制显隐)或者 PickResourcePage(没有弹窗外壳,外层不⼿动控制的话,弹窗直接加载显示)
b. 传⼊属性,属性分为两个部分, 1:配置属性 2.回调函数onOk和onCancel c. 配置属性分为两种情况:
1.有明确使⽤场景的
2.没有明确使⽤场景
d. 1.有明确使⽤场景的,提供属性uploadType和其他可变参数属性。
e. 针对uploadType的具体做法:在UploadType枚举中增加你的场景代号,⽐如现有的上传素材 UploadMaterial为1(传⼊属性就是 uploadType={UploadType.UploadMaterial})。在 usageWithOption中set当前场景的固定配置,⽐如是否可选⼀级⽂件夹,单选还是多选等。示 例:
export const usageWithOption = new Map()
// 在默认配置的基础上,为使⽤场景设置固定的配置属性
const UploadMaterialOption = cloneDeep(basePickResourceOptions)
UploadMaterialOption.checkRules.canOnlyCheckParent = true
UploadMaterialOption.checkRules.canCheckSameResource = false
usageWithOption.set(UploadType.UploadMaterial, UploadMaterialOption)
a. 针对其他可变参数属性,⽐如初次打开组件默认选中路径,查看PickResourceProps使⽤对应的 属性名传⼊即可
b. 没有明确使⽤场景的,为其提供基本配置basePickResourceOptions,其他配置通过 pickResourceOptions传⼊,直接和basePickResourceOptions合并,新传⼊的属性覆盖基础属 性配置
调研
a. antd 树选择
antd的tree⽤的组件是RcTree
RcTree处理选择规则
b.也调⽤了⽆限层级的菜单实现,⼤部分案例使⽤的也是递归实现,资源库选择组件的实现基础也是递归
根据要实现的功能,我们需要存的数据如下(其他⼀些为了处理空⻚⾯或者选择⻚⾯就不展示了):
/** 树资源 */
resources: [],
/** 当前屏⽹格模式下的资源(其实就是⾯包屑对应最后⼀层的资源) */
currentGridResources: [],
/** 当前选中的item(就是当前激活的路径) */
activePaths: [],
/** 当前选择的item */
checkedResource: [],
/** 当前因为⽗被选择⽽禁⽤的item */
checkParentLinkDisabled: [],
/** 还有资源本身带的disabled,是禁⽤了就不可再释放的标识, 在请求资源后,进⾏资源转换的时候就配在资源上了*/
组件的展示⽅案
a. 在列表模式,我们不知道实际上会展示到第⼏层资源,所以创建了⼀个递归组件
a. 在⽹格模式,我们当前屏展示始终是选中最后⼀层的数据,正常的视图组件,但是需要找出当前 最后⼀层数据
b. 最后⼀层数据由两个因素会影响,资源树(⽐如:新加载出来下⼀层)和当前激活的路径(⽐ 如:单击了⾯包屑或者选中了屏幕上某个资源)
c. 在model中找到updateResource和updateActivePath,在更新这两个数据的地⽅同时更新 currentGridResources即可
d. ⼤部分情况下,更新的currentGridResources就是下⼀层加载出来的资源,特殊情况有两个:
e. 1.单击⾯包屑 2.排序(每⼀层都可排序,只需要判断当前的排序是最后⼀层的排序再去改变 currentGridResources) f. 单击⾯包屑可能需要重新加载资源(不必特殊处理),也可能直接改变选中路径。如果直接改变 了选中路径,那么就需要根据选中路径在资源树中找到最后⼀层的资源
g. 这⾥只是找到资源,不需要改变树结构的数据,根据activePaths的深度,3层及其以内⽤⼴度优 先搜索,3层以上⽤深度优先搜索,搜到即⽌⼴度优先和深度优化搜索
深度优先
广度优先
这样做的好处是,如果我们最后⼀层选中的资源位于前⼏层,但⽐较深层的地⽅,就不⽤遍历后⼏层及其 ⼦节点。
如果我们选中的资源位于后⼏层,但⽐较浅层的地⽅,就不⽤遍历前⼏层⽐较深的⼦节点。但如果我们最 后⼀层选中的资源位于最后⼏层⼜⽐较深的资源,那么作⽤不⼤
组件的⼀些重点逻辑实现⽅案
a. 初始化资源
ⅰ. 根据配置拿到初始展示路径
ⅱ. 根据路径请求第⼀屏或第⼀和第⼆屏的资源
ⅲ. 转换资源,⽣成resources
b. 选中资源
ⅰ. 改变activePaths,选中资源数组
ⅱ. 判断下⼀层有没有数据,有资源直接更新⾯包屑即可,展示资源会根据activePaths改变
ⅲ. 下⼀层没有资源,加载完成后,把新加载的资源需要放进资源树resources ⅳ. 我们虽然能知道是哪⼀个⽗加载出来的新资源,但也需要在resources中找到这个⽗,把新资 源加到⽗的children中,也就是要改变树资源的数据,这⾥⽤的递归实现 递归好处是代码简洁易懂,坏处是有调⽤栈溢出的⻛险,但实际场景中我们的压栈次数并不会很多 ⽐如:
调⽤栈情况:
从压栈数量来看,只有 节点.children.length>0 && 节点在当前选中路径 的时候才会压栈,也就是只有⽤ 户选择展开⼀个节点后,在递归中这个节点才会压栈。 结合浏览器引擎可以⽀持调⽤的深度来看调⽤栈溢出的⻛险还是可控的
最后如果有关联选⼦,关联禁⽤⼦,根据规则处理禁⽤或关联选中的资源
c. 跳转⾯包屑
ⅰ. 判断单击的⾯包屑在树资源中有没有对应的数据
ⅱ. 有资源,说明加载过了,直接改变选中数组和⾯包屑,展示资源会根据activePaths改变
ⅲ. 没有资源,说明这层数据之前没有加载,那么从初始化数据开始
d. 选择资源
ⅰ. 拿到选择的资源和现有的checkedResource,根据配置:单选,多选,是否跨⽗来处理好新⼀ 轮的选中资源
ⅱ. 最后如果有关联选⼦,关联禁⽤⼦,根据规则处理禁⽤或关联选中的资源
e.取消选择资源
ⅰ. 先处理更新选中资源
ⅱ. 最后如果有关联选⼦,关联禁⽤⼦,根据规则处理禁⽤或关联选中的资源
f.根据规则处理禁⽤或关联选中的资源
ⅰ. 关联禁⽤或者关联选择分为两种情况,针对新加载出的⼀层资源进⾏处理,这种情况下,只 需要循环新加载出⼀层资源,该
放进禁⽤的放禁⽤,该放进选中资源的放进选中资源
ⅱ. 第⼆种情况,针对选择资源的孩⼦及孙⼦进⾏递归,根据规则,将选择资源或者取消选择资源 的孩⼦及孙⼦放进对应的禁
⽤资源或者选中资源即可
g.禁⽤重名资源
ⅰ. 根据已有的checkedResource,判断当前可⻅的资源是否属于同名同类型的资源,如果是就 禁⽤
在实现过程已经做了的优化:
1.之前choose和checked属性都是附着在资源本身身上,就是每个资源都带着⾃⼰的选择和选中以及禁 ⽤属性,这样,每次进⾏选择,选中以及处理规则连带的的时候都要去遍历所有节点。 现在把选中,选择,禁⽤都剥离和资源剥离开,只有需要改变树结构需要遍历全部节点,其他选择或 选中的时候都只需要遍历⼀层数据或者选择项的⼦及孙⼦
2.资源的展示使⽤了虚拟列表和虚拟⽹格
3.资源的选中选择状态存储在dom元素身上,避免再次进行状态判断
经过这些措施,⽬前展开多层,其中有2000⼀层的数据,整体性能还可以,没有明显卡顿
展示组件: