From dc6030bce6c7017a7ea76eee13042305c13814af Mon Sep 17 00:00:00 2001 From: liugq Date: Thu, 7 Mar 2019 11:34:23 +0800 Subject: [PATCH 1/3] perf[Tree-Table]: refactor tree-table (#1587) --- src/components/TreeTable/eval.js | 65 +++-- src/components/TreeTable/index.vue | 228 ++++++++++------- src/components/TreeTable/readme.md | 150 ++++++----- src/mock/table.js | 0 src/views/table/treeTable/customEval.js | 48 ---- src/views/table/treeTable/customTreeTable.vue | 239 ++++++++++-------- src/views/table/treeTable/data.js | 49 ++++ src/views/table/treeTable/treeTable.vue | 158 ++++++++---- 8 files changed, 551 insertions(+), 386 deletions(-) create mode 100644 src/mock/table.js delete mode 100644 src/views/table/treeTable/customEval.js create mode 100644 src/views/table/treeTable/data.js diff --git a/src/components/TreeTable/eval.js b/src/components/TreeTable/eval.js index d9b89e1c..8659ead8 100644 --- a/src/components/TreeTable/eval.js +++ b/src/components/TreeTable/eval.js @@ -1,29 +1,48 @@ -/** -* @Author: jianglei -* @Date: 2017-10-12 12:06:49 -*/ -'use strict' import Vue from 'vue' -export default function treeToArray(data, expandAll, parent = null, level = null) { + +// Flattened array +export default function treeToArray(data, children = 'children') { let tmp = [] - Array.from(data).forEach(function(record) { - if (record._expanded === undefined) { - Vue.set(record, '_expanded', expandAll) - } - let _level = 1 - if (level !== undefined && level !== null) { - _level = level + 1 - } - Vue.set(record, '_level', _level) - // 如果有父元素 - if (parent) { - Vue.set(record, 'parent', parent) - } - tmp.push(record) - if (record.children && record.children.length > 0) { - const children = treeToArray(record.children, expandAll, record, _level) - tmp = tmp.concat(children) + data.forEach((item, index) => { + Vue.set(item, '_index', index) + tmp.push(item) + if (item[children] && item[children].length > 0) { + const res = treeToArray(item[children], children) + tmp = tmp.concat(res) } }) return tmp } + +export function addAttrs(data, { parent = null, preIndex = false, level = 0, expand = false, children = 'children', show = true, select = false } = {}) { + data.forEach((item, index) => { + const _id = (preIndex ? `${preIndex}-${index}` : index) + '' + Vue.set(item, '_id', _id) + Vue.set(item, '_level', level) + Vue.set(item, '_expand', expand) + Vue.set(item, '_parent', parent) + Vue.set(item, '_show', show) + Vue.set(item, '_select', select) + if (item[children] && item[children].length > 0) { + addAttrs(item[children], { + parent: item, + level: level + 1, + expand, + preIndex: _id, + children, + status, + select + }) + } + }) +} + +export function cleanParentAttr(data, children = 'children') { + data.forEach(item => { + item._parent = null + if (item[children] && item[children].length > 0) { + addAttrs(item[children], children) + } + }) + return data +} diff --git a/src/components/TreeTable/index.vue b/src/components/TreeTable/index.vue index a48765c8..cbe1eb60 100644 --- a/src/components/TreeTable/index.vue +++ b/src/components/TreeTable/index.vue @@ -1,127 +1,177 @@ - - diff --git a/src/components/TreeTable/readme.md b/src/components/TreeTable/readme.md index 5b598e11..fe404ed0 100644 --- a/src/components/TreeTable/readme.md +++ b/src/components/TreeTable/readme.md @@ -1,89 +1,99 @@ ## 写在前面 -此组件仅提供一个创建TreeTable的解决思路 -## prop说明 -#### *data* - **必填** +此组件仅提供一个创建 TreeTable 的解决思路,本组件充分利用 vue 插槽的特性,方便用户自定义 - 原始数据,要求是一个数组或者对象 - ```javascript - [{ - key1: value1, - key2: value2, - children: [{ +evel.js 里面 `addAttrs` 方法会给数据添加几个属性,treeTotable 会对数组扁平化。这些操作都不会破坏源数据,只是会新增属性。 + +调用 addAttrs 后,因\_\_parent 属性,会造成数据循环引用,使用 JSON.stringify()报错,所以转成 JSON 之前需要清除\_\_parent 属性。 + +## prop 说明 + +- data(原始数据,要求是一个数组或者对象) +- columns(列属性,要求是一个数组) +- renderContent(数组扁平化方法(可选)) +- defaultExpandAll(默认是否全部展开,默认全部展开) +- defaultChildren(子元素名,默认为 children) +- spreadOffset(扩展符号的偏移量,默认为 50px) +- checkboxOffset(复选框的偏移量,默认为 50px) + +--- + +### 代码示例 + +- data(**必填**) + +原始数据, + +```js +const data = [ + { + key1: value1, + key2: value2, + children: [ + { key1: value1 }, { key1: value1 - }] - }, - { - key1: value1 - }] - ``` - 或者 - ```javascript - { - key1: value1, - key2: value2, - children: [{ - key1: value1 - }, - { - key1: value1 - }] - } - ``` + } + ] + }, + { + key1: value1 + } +] +``` -#### columns - 列属性,要求是一个数组 +或者 - 1. text: 显示在表头的文字 - 2. value: 对应data的key。treeTable将显示相应的value - 3. width: 每列的宽度,为一个数字(可选) +```javascript + { + key1: value1, + key2: value2, + children: [{ + key1: value1 + }, + { + key1: value1 + }] + } +``` - 如果你想要每个字段都有自定义的样式或者嵌套其他组件,columns可不提供,直接像在el-table一样写即可,如果没有自定义内容,提供columns将更加的便捷方便 +- columns - 如果你有几个字段是需要自定义的,几个不需要,那么可以将不需要自定义的字段放入columns,将需要自定义的内容放入到slot中,详情见后文 - ```javascript - [{ - value:string, - text:string, - width:number - },{ - value:string, - text:string, - width:number - }] - ``` +1. label: 显示在表头的文字 +2. key: 对应 data 的 key。treeTable 将显示相应的 value +3. width: 每列的宽度,为一个数字(可选) -#### expandAll - 是否默认全部展开,boolean值,默认为false +树表组件将会根据 columns 的 key 属性生成具名插槽,如果你需要对列数据进行自定义,通过插槽即可实现 -#### evalFunc - 解析函数,function,非必须 +```javascript +const columns = [ + // 建议第一列做展开收缩操作 + { label: '', key: '__spread', width: '200' }, + // 如果添加复选框 + { label: '', key: '__checkbox', width: '200' }, + { + label: string, + key: string, + width: number + }, + { + label: string, + key: string, + width: number + } +] +``` - 如果不提供,将使用默认的[evalFunc](./eval.js) +#### renderContent - 如果提供了evalFunc,那么会用提供的evalFunc去解析data,并返回treeTable渲染所需要的值。如何编写一个evalFunc,请参考[*eval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/components/TreeTable/eval.js)或[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customEval.js) +解析函数,function,非必须 -#### evalArgs - 解析函数的参数,是一个数组 +如果不提供,将使用默认的[evalFunc](./eval.js) - **请注意,自定义的解析函数参数第一个为this.data,第二个参数为, this.expandAll,你不需要在evalArgs填写。一定记住,这两个参数是强制性的,并且位置不可颠倒** *this.data为需要解析的数据,this.expandAll为是否默认展开* +如果提供了 evalFunc,那么会用提供的 evalFunc 去解析 data,并返回 treeTable 渲染所需要的值。 - 如你的解析函数需要的参数为`(this.data, this.expandAll,1,2,3,4)`,那么你只需要将`[1,2,3,4]`赋值给`evalArgs`就可以了 +## 其他 - 如果你的解析函数参数只有`(this.data, this.expandAll)`,那么就可以不用填写evalArgs了 - - 具体可参考[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customEval.js)的函数参数和[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customTreeTable.vue)的`evalArgs`属性值 - - ## slot - 这是一个自定义列的插槽。 - - 默认情况下,treeTable只有一行行展示数据的功能。但是一般情况下,我们会要给行加上一个操作按钮或者根据当行数据展示不同的样式,这时我们就需要自定义列了。请参考[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customTreeTable.vue),[实例效果](https://panjiachen.github.io/vue-element-admin/#/table/tree-table) - - `slot`和`columns属性`可同时存在,columns里面的数据列会在slot自定义列的左边展示 - - ## 其他 - 如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的api自行修改index.vue +如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的 api 自行修改 index.vue diff --git a/src/mock/table.js b/src/mock/table.js new file mode 100644 index 00000000..e69de29b diff --git a/src/views/table/treeTable/customEval.js b/src/views/table/treeTable/customEval.js deleted file mode 100644 index 73badb68..00000000 --- a/src/views/table/treeTable/customEval.js +++ /dev/null @@ -1,48 +0,0 @@ -/** -* @Author: jianglei -* @Date: 2017-10-12 12:06:49 -*/ -'use strict' -import Vue from 'vue' -export default function treeToArray(data, expandAll, parent, level, item) { - const marLTemp = [] - let tmp = [] - Array.from(data).forEach(function(record) { - if (record._expanded === undefined) { - Vue.set(record, '_expanded', expandAll) - } - let _level = 1 - if (level !== undefined && level !== null) { - _level = level + 1 - } - Vue.set(record, '_level', _level) - // 如果有父元素 - if (parent) { - Vue.set(record, 'parent', parent) - // 如果父元素有偏移量,需要计算在this的偏移量中 - // 偏移量还与前面同级元素有关,需要加上前面所有元素的长度和 - if (!marLTemp[_level]) { - marLTemp[_level] = 0 - } - Vue.set(record, '_marginLeft', marLTemp[_level] + parent._marginLeft) - Vue.set(record, '_width', record[item] / parent[item] * parent._width) - // 在本次计算过偏移量后加上自己长度,以供下一个元素使用 - marLTemp[_level] += record._width - } else { - // 如果为根 - // 初始化偏移量存储map - marLTemp[record.id] = [] - // map中是一个数组,存储的是每级的长度和 - // 初始情况下为0 - marLTemp[record.id][_level] = 0 - Vue.set(record, '_marginLeft', 0) - Vue.set(record, '_width', 1) - } - tmp.push(record) - if (record.children && record.children.length > 0) { - const children = treeToArray(record.children, expandAll, record, _level, item) - tmp = tmp.concat(children) - } - }) - return tmp -} diff --git a/src/views/table/treeTable/customTreeTable.vue b/src/views/table/treeTable/customTreeTable.vue index 2a216171..1ed346fb 100644 --- a/src/views/table/treeTable/customTreeTable.vue +++ b/src/views/table/treeTable/customTreeTable.vue @@ -1,137 +1,162 @@