zsmj
2 years ago
10 changed files with 417 additions and 4 deletions
After Width: | Height: | Size: 880 B |
After Width: | Height: | Size: 2.7 KiB |
@ -0,0 +1,3 @@ |
|||||||
|
# 怎么实现一个树形控件 |
||||||
|
|
||||||
|
http://fanruan.design/doc.html?post=8df04cafb0 |
@ -0,0 +1,58 @@ |
|||||||
|
# disabled的父子控制 |
||||||
|
|
||||||
|
绝大多数时候,我们皆认同父组件设置`disabled:true`之后所有子组件都会被disabled |
||||||
|
|
||||||
|
```javascript |
||||||
|
const widget = { |
||||||
|
type: "a", |
||||||
|
disabled: true, |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: "b" |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
``` |
||||||
|
|
||||||
|
但是存在一种场景,有没有可能父组件`disabled:true`,但是某个子组件要可用呢`disabled:false` |
||||||
|
|
||||||
|
由于组件默认的disabled属性就是false,所以没办法通过props控制,有一种方式是在`mounted`中重新`setEnable(true)` |
||||||
|
|
||||||
|
```javascript |
||||||
|
const widget = { |
||||||
|
type: "a", |
||||||
|
disabled: true, |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: "b" |
||||||
|
}, { |
||||||
|
type: "b", |
||||||
|
mounted: function () { |
||||||
|
this.setEnable(true); |
||||||
|
}, |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
``` |
||||||
|
|
||||||
|
disabled控制还有一个特性,当使用者手动控制disabled状态后,会对组件添加一个`_manualSetEnable`标记,之后该组件将不再受父组件的`setEnable`控制 |
||||||
|
|
||||||
|
手动控制disabled状态指设置`dsiabled:true`和手动调用`setEnable`方法 |
||||||
|
|
||||||
|
```javascript |
||||||
|
const widget = { |
||||||
|
type: "a", |
||||||
|
items: [ |
||||||
|
{ |
||||||
|
type: "b" |
||||||
|
}, { |
||||||
|
type: "b", |
||||||
|
disabled: true, |
||||||
|
} |
||||||
|
], |
||||||
|
mounted: function () { |
||||||
|
// 对父组件操作无法再控制到子组件b了 |
||||||
|
this.setEnable(true); |
||||||
|
} |
||||||
|
}; |
||||||
|
``` |
@ -0,0 +1,26 @@ |
|||||||
|
# model的生命周期 |
||||||
|
|
||||||
|
FineUI组件有很多生命周期,`beforeRender`,`mounted`,`destoryed等`. |
||||||
|
|
||||||
|
model同样提供了一些生命周期供我们使用,`init` `created` `destroyed` |
||||||
|
|
||||||
|
`init`生命周期触发时此刻什么都没有,无法获取state与context,适用于初始化一些状态无关的东西 |
||||||
|
`created`生命周期触发时此时已经可以获取state与context的值了 |
||||||
|
`destroyed`生命周期是在model完成销毁后触发,此时可以做一下收尾工作 |
||||||
|
|
||||||
|
```javascript |
||||||
|
class Model extends Model { |
||||||
|
|
||||||
|
init() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
created() { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
destroyed() { |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
``` |
@ -0,0 +1,49 @@ |
|||||||
|
# 前端进行模糊搜索的方法 |
||||||
|
|
||||||
|
数据量小的时候,模糊搜索功能可以由前端来实现. FineUI中提供了模糊搜索的工具方法`BI.Func.getSearchResult` |
||||||
|
|
||||||
|
先看方法描述`getSearchResult: (items: any, keyword: any, param?: string) => { find: any[], match: any[] };` |
||||||
|
|
||||||
|
分别是需要搜索的集合,需要搜索的关键字,搜索的属性(默认是text属性),返回结果为一个带有`match`和`find`属性的对象,分别记录完全匹配和部分匹配的结果 |
||||||
|
|
||||||
|
```javascript |
||||||
|
const items = [ |
||||||
|
{ |
||||||
|
text: "财务部", |
||||||
|
value: 1, |
||||||
|
}, { |
||||||
|
text: "采购部", |
||||||
|
value: 2, |
||||||
|
}, { |
||||||
|
text: "财务", |
||||||
|
value: 3, |
||||||
|
} |
||||||
|
]; |
||||||
|
|
||||||
|
const { match, find } = BI.Func.getSearchResult(items, "cw"); |
||||||
|
|
||||||
|
console.log(match.map(m => m.value)); // [3] |
||||||
|
console.log(find.map(f => f.value)); // [1,2] |
||||||
|
``` |
||||||
|
|
||||||
|
指定搜索属性示例: |
||||||
|
|
||||||
|
```javascript |
||||||
|
const items = [ |
||||||
|
{ |
||||||
|
name: "财务部", |
||||||
|
value: 1, |
||||||
|
}, { |
||||||
|
name: "采购部", |
||||||
|
value: 2, |
||||||
|
}, { |
||||||
|
name: "财务", |
||||||
|
value: 3, |
||||||
|
} |
||||||
|
]; |
||||||
|
|
||||||
|
const { match, find } = BI.Func.getSearchResult(items, "cw","name"); |
||||||
|
|
||||||
|
console.log(match.map(m => m.value)); // [3] |
||||||
|
console.log(find.map(f => f.value)); // [1,2] |
||||||
|
``` |
@ -0,0 +1,121 @@ |
|||||||
|
# 处理树结构常用算法 |
||||||
|
|
||||||
|
常用的算法大多可以在tree.js中找到,下面列举一些常用的 |
||||||
|
|
||||||
|
## transformToArrayFormat 将嵌套结构转换为id,pId解构的一维数组 |
||||||
|
|
||||||
|
```javascript |
||||||
|
const tree = [ |
||||||
|
{ |
||||||
|
id: "root", |
||||||
|
children: [ |
||||||
|
{ |
||||||
|
id: "1-1", |
||||||
|
children: [ |
||||||
|
{ |
||||||
|
id: "1-1-1" |
||||||
|
} |
||||||
|
] |
||||||
|
}, { |
||||||
|
id: "1-2" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
]; |
||||||
|
|
||||||
|
const arrayFormat = BI.Tree.transformToArrayFormat(tree); |
||||||
|
|
||||||
|
// arrayFormat = [ |
||||||
|
// { |
||||||
|
// "id": "root" |
||||||
|
// }, |
||||||
|
// { |
||||||
|
// "id": "1-1", |
||||||
|
// "pId": "root" |
||||||
|
// }, |
||||||
|
// { |
||||||
|
// "id": "1-1-1", |
||||||
|
// "pId": "1-1" |
||||||
|
// }, |
||||||
|
// { |
||||||
|
// "id": "1-2", |
||||||
|
// "pId": "root" |
||||||
|
// } |
||||||
|
// ]; |
||||||
|
``` |
||||||
|
|
||||||
|
## transformToTreeFormat 将id,pId结构的一维数组转换为嵌套结构 |
||||||
|
|
||||||
|
```javascript |
||||||
|
const arrayFormat = [ |
||||||
|
{ |
||||||
|
"id": "root" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "1-1", |
||||||
|
"pId": "root" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "1-1-1", |
||||||
|
"pId": "1-1" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "1-2", |
||||||
|
"pId": "root" |
||||||
|
} |
||||||
|
]; |
||||||
|
|
||||||
|
const tree = BI.Tree.transformToTreeFormat(arrayFormat); |
||||||
|
|
||||||
|
// tree = [ |
||||||
|
// { |
||||||
|
// id: "root", |
||||||
|
// children: [ |
||||||
|
// { |
||||||
|
// id: "1-1", |
||||||
|
// children: [ |
||||||
|
// { |
||||||
|
// id: "1-1-1" |
||||||
|
// } |
||||||
|
// ] |
||||||
|
// }, { |
||||||
|
// id: "1-2" |
||||||
|
// } |
||||||
|
// ] |
||||||
|
// } |
||||||
|
// ]; |
||||||
|
``` |
||||||
|
|
||||||
|
## traversal遍历树结构 |
||||||
|
|
||||||
|
`BI.Tree.traversal`方法深度优先,迭代函数传递索引,当前节点,父节点参数,迭代函数中返回`false`将终止遍历 |
||||||
|
|
||||||
|
```javascript |
||||||
|
const tree = [ |
||||||
|
{ |
||||||
|
id: "root", |
||||||
|
children: [ |
||||||
|
{ |
||||||
|
id: "1-1", |
||||||
|
children: [ |
||||||
|
{ |
||||||
|
id: "1-1-1" |
||||||
|
} |
||||||
|
] |
||||||
|
}, { |
||||||
|
id: "1-2" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
]; |
||||||
|
|
||||||
|
BI.Tree.traversal(tree, (index, item, parentNode) => { |
||||||
|
console.log(index, item.id, parentNode ? parentNode.id : null); |
||||||
|
}); |
||||||
|
|
||||||
|
// 0 'root' null |
||||||
|
// 0 '1-1' 'root' |
||||||
|
// 0 '1-1-1' '1-1' |
||||||
|
// 1 '1-2' 'root' |
||||||
|
|
||||||
|
``` |
@ -0,0 +1,114 @@ |
|||||||
|
# 组件生命周期与Model状态控制 |
||||||
|
|
||||||
|
新接触FineUI的人很容易搞不清组件生命周期,写出不恰当的状态控制代码 |
||||||
|
|
||||||
|
## 误区:在mounted的时候异步初始化数据 |
||||||
|
|
||||||
|
举一个常见的例子: 下面的书写形式,在组件mounted之后再去发送请求初始化状态,而后由watch调用组件的setSelected方法修改状态 |
||||||
|
|
||||||
|
这样有什么不好吗? 首先请求过慢的话会导致switch开关闪一下,其次mounted之后再修改组件会导致无谓的回流重绘 |
||||||
|
|
||||||
|
```javascript |
||||||
|
class Model extends Model { |
||||||
|
state() { |
||||||
|
return { |
||||||
|
clusterOpen: false |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
actions = { |
||||||
|
initConfig: () => { |
||||||
|
// res |
||||||
|
return Promise.resolve(() => { |
||||||
|
this.model.clusterOpen = res.clusterOpen; |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
class Widget extends BI.Widget { |
||||||
|
|
||||||
|
watch = { |
||||||
|
clusterOpen: b => this.switch.setSelected(b) |
||||||
|
}; |
||||||
|
|
||||||
|
mounted() { |
||||||
|
this.store.initConfig(); |
||||||
|
} |
||||||
|
|
||||||
|
render() { |
||||||
|
return { |
||||||
|
type: "bi.switch", |
||||||
|
selected: false |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
恰当的方式是对需要初始化配置的场景采用异步组件形式,在`beforeRender`生命周期中做好状态获取,在组件被挂载到DOM上之前完成组件控制 |
||||||
|
|
||||||
|
这期间也可以根据需要做loading与loaded处理. |
||||||
|
|
||||||
|
```javascript |
||||||
|
class Model extends Model { |
||||||
|
state() { |
||||||
|
return { |
||||||
|
clusterOpen: false |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
actions = { |
||||||
|
initConfig: () => { |
||||||
|
// res |
||||||
|
return Promise.resolve(() => { |
||||||
|
this.model.clusterOpen = res.clusterOpen; |
||||||
|
}); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
class Widget extends BI.Widget { |
||||||
|
|
||||||
|
beforeRender() { |
||||||
|
return this.store.initConfig(); |
||||||
|
} |
||||||
|
|
||||||
|
render() { |
||||||
|
return { |
||||||
|
type: "bi.switch", |
||||||
|
selected: this.model.clusterOpen |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
## 在初始化state的时候可以直接根据options来 |
||||||
|
|
||||||
|
封装一个独立的组件的时候,组件内部用到了model,有时候需要把外部传来的props用做初始状态 |
||||||
|
|
||||||
|
```javascript |
||||||
|
class Model extends Model { |
||||||
|
state() { |
||||||
|
const { clusterOpen } = this.options; |
||||||
|
return { |
||||||
|
clusterOpen: clusterOpen |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@store(Model, { |
||||||
|
props: () => { |
||||||
|
return { |
||||||
|
clusterOpen: this.options.clusterOpen |
||||||
|
}; |
||||||
|
} |
||||||
|
}) |
||||||
|
class Widget extends BI.Widget { |
||||||
|
render() { |
||||||
|
return { |
||||||
|
type: "bi.switch", |
||||||
|
selected: false |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
@ -0,0 +1,39 @@ |
|||||||
|
# label组件文字标红,为什么有时候不管用呢 |
||||||
|
|
||||||
|
处理搜索业务的时候,给组件设置`keyword`属性,组件将自动进行模糊拼音批评标红匹配文字 |
||||||
|
|
||||||
|
```javascript |
||||||
|
const label = { |
||||||
|
type: "bi.label", |
||||||
|
text: "财务部", |
||||||
|
keyword: "cw", |
||||||
|
}; |
||||||
|
``` |
||||||
|
|
||||||
|
![示例](../images/56.png) |
||||||
|
|
||||||
|
但改机制不是万能的,对于超长文本,可能不会完全匹配 |
||||||
|
|
||||||
|
```javascript |
||||||
|
const label = { |
||||||
|
type: "bi.label", |
||||||
|
text: "超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长超长", |
||||||
|
keyword: "cc", |
||||||
|
}; |
||||||
|
``` |
||||||
|
|
||||||
|
![示例](../images/57.png) |
||||||
|
|
||||||
|
如上图所示,并非所有的超长都被标红了,这是为什么呢? |
||||||
|
|
||||||
|
事情还要从标红文字的原理说起,简要描述的话 首先利用`BI.makeFirstPY`方法得到text的拼音首字母组合,然后通过循环遍历去和`keyword`匹配 |
||||||
|
|
||||||
|
以"超长"为例,因为多音字的缘故,有`cc`和`cz`两个组合 |
||||||
|
```javascript |
||||||
|
BI.makeFirstPY("超长") // 'czcc' |
||||||
|
BI.makeFirstPY("超长超长") // 'czczccczczcccccc' |
||||||
|
BI.makeFirstPY("超长超长超长") // 'czczczccczczczccczccccczczczccccczccczcccccccccc' |
||||||
|
``` |
||||||
|
由此可见产生的多音字组合会指数级扩增,若不加限制的进行匹配,那么会导致浏览器假死.因此不会无限制的匹配 |
||||||
|
|
||||||
|
同样的,即是不是多音字,大量的重复内容也会触发限制,例如 `text: "财务财务财务财务财务*100"` |
Loading…
Reference in new issue