diff --git a/src/components/TreeTable/README.md b/src/components/TreeTable/README.md new file mode 100644 index 00000000..05326dfa --- /dev/null +++ b/src/components/TreeTable/README.md @@ -0,0 +1,220 @@ + +- [Enlgish](#Brief) + +# 中文 + +## 写在前面 + +此组件仅提供一个创建 `TreeTable` 的解决思路。它基于`element-ui`的 table 组件实现,通过`el-table`的`row-style`方法,在里面判断元素是否需要隐藏或者显示,从而实现`TreeTable`的展开与收起。 + +并且本组件充分利用 `vue` 插槽的特性来方便用户自定义。 + +`evel.js` 里面,`addAttrs` 方法会给数据添加几个属性,`treeTotable` 会对数组扁平化。这些操作都不会破坏源数据,只是会新增属性。 + +## Props 说明 + +| Attribute | Description | Type | Default | +| :--------------: | :--------------------------------- | :-----: | :------: | +| data | 原始展示数据 | Array | [] | +| columns | 列属性 | Array | [] | +| defaultExpandAll | 默认是否全部展开 | Boolean | false | +| defaultChildren | 指定子树为节点对象的某个属性值 | String | children | | +| indent | 相邻级节点间的水平缩进,单位为像素 | Number | 50 | + +> 任何 `el-table` 的属性都支持,例如`border`、`fit`、`size`或者`@select`、`@cell-click`等方法。详情属性见`el-table`文档。 + +--- + +### 代码示例 + +```html + +``` + +#### data(**必填**) + +```js +const data = [ + { + name:'1' + children: [ + { + name: '1-1' + }, + { + name: '1-2' + } + ] + }, + { + name: `2` + } +] +``` + +#### columns(**必填**) + +- label: 显示在表头的文字 +- key: 对应 data 的 key。treeTable 将显示相应的 value +- expand: `true` or `false`。若为 true,则在该列显示展开收起图标 +- checkbox: `true` or `false`。若为 true,则在该列显示`checkbox` +- width: 每列的宽度,为一个数字(可选)。例如`200` +- align: 对齐方式 `left/center/right` +- header-align: 表头对齐方式 `left/center/right` + +```javascript +const columns = [ + { + label: 'Checkbox', + checkbox: true + }, + { + label: '', + key: 'id', + expand: true + }, + { + label: 'Event', + key: 'event', + width: 200, + align: 'left' + }, + { + label: 'Scope', + key: 'scope' + } +] +``` + +> 树表组件将会根据 columns 的 key 属性生成具名插槽,如果你需要对列数据进行自定义,通过插槽即可实现 + +```html + +``` + +## Events + +目前提供了几个方法,不过只是`beta`版本,之后很可能会修改。 + +```js +this.$refs.TreeTable.addChild(row, data) //添加子元素 +this.$refs.TreeTable.addBrother(row, data) //添加兄弟元素 +this.$refs.TreeTable.delete(row) //删除该元素 +``` + +## 其他 + +如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的 api 自行修改 index.vue + +# English + +## Brief + +This component only provides a solution for creating `TreeTable`. It is based on the `element-ui` table component. It uses the `row-style` method of `el-table` to determine whether the element needs to be hidden or displayed. + +And this component makes full use of the features of the `vue` slot to make it user-friendly. + +In `evel.js`, the `addAttrs` method adds several properties to the data, and `treeTotable` flattens the array. None of these operations will destroy the source data, just add properties. + +## Props + +| Attribute | Description | Type | Default | +| :--------------: | :----------------------------------------------------------- | :-----: | :------: | +| data | original display data | Array | [] | +| columns | column attribute | Array | [] | +| defaultExpandAll | whether to expand all nodes by default | Boolean | false | +| defaultChildren | specify which node object is used as the node's subtree | String | children | | +| indent | horizontal indentation of nodes in adjacent levels in pixels | Number | 50 | + +> Any of the `el-table` properties are supported, such as `border`, `fit`, `size` or `@select`, `@cell-click`. See the ʻel-table` documentation for details. + +--- + +### Example + +```html + +``` + +#### data(**Required**) + +```js +const data = [ + { + name:'1' + children: [ + { + name: '1-1' + }, + { + name: '1-2' + } + ] + }, + { + name: `2` + } +] +``` + +#### columns(**Required**) + +- label: text displayed in the header +- key: data.key will show in column +- expand: `true` or `false` +- checkbox: `true` or `false` +- width: column width 。such as `200` +- align: alignment `left/center/right` +- header-align: alignment of the table header `left/center/right` + +```javascript +const columns = [ + { + label: 'Checkbox', + checkbox: true + }, + { + label: '', + key: 'id', + expand: true + }, + { + label: 'Event', + key: 'event', + width: 200, + align: 'left' + }, + { + label: 'Scope', + key: 'scope' + } +] +``` + +> The tree table component will generate a named slot based on the key property of columns. If you need to customize the column data, you can do it through the slot. + +```html + +``` + +## Events + +Several methods are currently available, but only the `beta` version, which is likely to be modified later. + +```js +this.$refs.TreeTable.addChild(row, data) //Add child elements +this.$refs.TreeTable.addBrother(row, data) //Add a sibling element +this.$refs.TreeTable.delete(row) //Delete the element +``` + +## Other + +If you have other requirements, please refer to the [el-table](http://element-cn.eleme.io/#/en-US/component/table) api to modify the index.vue 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 deleted file mode 100644 index 5b598e11..00000000 --- a/src/components/TreeTable/readme.md +++ /dev/null @@ -1,89 +0,0 @@ -## 写在前面 -此组件仅提供一个创建TreeTable的解决思路 - -## prop说明 -#### *data* - **必填** - - 原始数据,要求是一个数组或者对象 - ```javascript - [{ - key1: value1, - key2: value2, - children: [{ - key1: value1 - }, - { - key1: value1 - }] - }, - { - key1: value1 - }] - ``` - 或者 - ```javascript - { - key1: value1, - key2: value2, - children: [{ - key1: value1 - }, - { - key1: value1 - }] - } - ``` - -#### columns - 列属性,要求是一个数组 - - 1. text: 显示在表头的文字 - 2. value: 对应data的key。treeTable将显示相应的value - 3. width: 每列的宽度,为一个数字(可选) - - 如果你想要每个字段都有自定义的样式或者嵌套其他组件,columns可不提供,直接像在el-table一样写即可,如果没有自定义内容,提供columns将更加的便捷方便 - - 如果你有几个字段是需要自定义的,几个不需要,那么可以将不需要自定义的字段放入columns,将需要自定义的内容放入到slot中,详情见后文 - ```javascript - [{ - value:string, - text:string, - width:number - },{ - value:string, - text:string, - width:number - }] - ``` - -#### expandAll - 是否默认全部展开,boolean值,默认为false - -#### evalFunc - 解析函数,function,非必须 - - 如果不提供,将使用默认的[evalFunc](./eval.js) - - 如果提供了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) - -#### evalArgs - 解析函数的参数,是一个数组 - - **请注意,自定义的解析函数参数第一个为this.data,第二个参数为, this.expandAll,你不需要在evalArgs填写。一定记住,这两个参数是强制性的,并且位置不可颠倒** *this.data为需要解析的数据,this.expandAll为是否默认展开* - - 如你的解析函数需要的参数为`(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 diff --git a/src/icons/svg/tree-table.svg b/src/icons/svg/tree-table.svg new file mode 100644 index 00000000..8aafdb82 --- /dev/null +++ b/src/icons/svg/tree-table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/mock/table.js b/src/mock/table.js new file mode 100644 index 00000000..e69de29b diff --git a/src/router/index.js b/src/router/index.js index 60524517..a4a71e46 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -10,6 +10,7 @@ import Layout from '@/views/layout/Layout' import componentsRouter from './modules/components' import chartsRouter from './modules/charts' import tableRouter from './modules/table' +import treeTableRouter from './modules/tree-table' import nestedRouter from './modules/nested' /** note: sub-menu only appear when children.length>=1 @@ -162,6 +163,7 @@ export const asyncRouterMap = [ chartsRouter, nestedRouter, tableRouter, + treeTableRouter, { path: '/example', diff --git a/src/router/modules/table.js b/src/router/modules/table.js index a9c4cb44..4d7f55ef 100644 --- a/src/router/modules/table.js +++ b/src/router/modules/table.js @@ -30,18 +30,6 @@ const tableRouter = { name: 'InlineEditTable', meta: { title: 'inlineEditTable' } }, - { - path: 'tree-table', - component: () => import('@/views/table/treeTable/treeTable'), - name: 'TreeTableDemo', - meta: { title: 'treeTable' } - }, - { - path: 'custom-tree-table', - component: () => import('@/views/table/treeTable/customTreeTable'), - name: 'CustomTreeTableDemo', - meta: { title: 'customTreeTable' } - }, { path: 'complex-table', component: () => import('@/views/table/complexTable'), diff --git a/src/router/modules/tree-table.js b/src/router/modules/tree-table.js new file mode 100644 index 00000000..5ee26828 --- /dev/null +++ b/src/router/modules/tree-table.js @@ -0,0 +1,29 @@ +/** When your routing table is too long, you can split it into small modules**/ + +import Layout from '@/views/layout/Layout' + +const treeTableRouter = { + path: '/tree-table', + component: Layout, + redirect: '/table/complex-table', + name: 'TreeTable', + meta: { + title: 'treeTable', + icon: 'tree-table' + }, + children: [ + { + path: 'index', + component: () => import('@/views/tree-table/index'), + name: 'TreeTableDemo', + meta: { title: 'treeTable' } + }, + { + path: 'custom', + component: () => import('@/views/tree-table/custom'), + name: 'CustomTreeTableDemo', + meta: { title: 'customTreeTable' } + } + ] +} +export default treeTableRouter diff --git a/src/views/layout/components/Sidebar/SidebarItem.vue b/src/views/layout/components/Sidebar/SidebarItem.vue index 94275aa8..47ac4b4d 100644 --- a/src/views/layout/components/Sidebar/SidebarItem.vue +++ b/src/views/layout/components/Sidebar/SidebarItem.vue @@ -9,7 +9,7 @@ - + diff --git a/src/components/ScrollPane/index.vue b/src/views/layout/components/TagsView/ScrollPane.vue similarity index 91% rename from src/components/ScrollPane/index.vue rename to src/views/layout/components/TagsView/ScrollPane.vue index 172afccc..820a536e 100644 --- a/src/components/ScrollPane/index.vue +++ b/src/views/layout/components/TagsView/ScrollPane.vue @@ -14,16 +14,21 @@ export default { left: 0 } }, + computed: { + scrollWrapper() { + return this.$refs.scrollContainer.$refs.wrap + } + }, methods: { handleScroll(e) { const eventDelta = e.wheelDelta || -e.deltaY * 40 - const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap + const $scrollWrapper = this.scrollWrapper $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4 }, moveToTarget(currentTag) { const $container = this.$refs.scrollContainer.$el const $containerWidth = $container.offsetWidth - const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap + const $scrollWrapper = this.scrollWrapper const tagList = this.$parent.$refs.tag let firstTag = null @@ -44,6 +49,7 @@ export default { const currentIndex = tagList.findIndex(item => item === currentTag) const prevTag = tagList[currentIndex - 1] const nextTag = tagList[currentIndex + 1] + // the tag's offsetLeft after of nextTag const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing diff --git a/src/views/layout/components/TagsView.vue b/src/views/layout/components/TagsView/index.vue similarity index 97% rename from src/views/layout/components/TagsView.vue rename to src/views/layout/components/TagsView/index.vue index cfac9c7d..53d592bc 100644 --- a/src/views/layout/components/TagsView.vue +++ b/src/views/layout/components/TagsView/index.vue @@ -26,7 +26,7 @@ diff --git a/src/views/table/treeTable/treeTable.vue b/src/views/table/treeTable/treeTable.vue deleted file mode 100644 index d2ecf14d..00000000 --- a/src/views/table/treeTable/treeTable.vue +++ /dev/null @@ -1,129 +0,0 @@ - - - diff --git a/src/views/tree-table/custom/data.js b/src/views/tree-table/custom/data.js new file mode 100644 index 00000000..020f6247 --- /dev/null +++ b/src/views/tree-table/custom/data.js @@ -0,0 +1,51 @@ +const data = [ + { + name: '1', + timeLine: 100, + children: [ + { + name: '1-1', + timeLine: 20 + }, + { + name: '1-2', + timeLine: 60, + children: [ + { + name: '1-2-1', + timeLine: 35 + }, + { + name: '1-2-2', + timeLine: 25 + } + ] + } + ] + }, + { + name: '2', + timeLine: 80, + children: [ + { + name: '2-1', + timeLine: 30 + }, + { + name: '2-2', + timeLine: 50 + }, + { + name: '2-3', + timeLine: 60 + } + ] + }, + { + name: '3', + timeLine: 40 + } +] + +export default data + diff --git a/src/views/tree-table/custom/index.vue b/src/views/tree-table/custom/index.vue new file mode 100644 index 00000000..6f9142a6 --- /dev/null +++ b/src/views/tree-table/custom/index.vue @@ -0,0 +1,166 @@ + + + diff --git a/src/views/tree-table/data.js b/src/views/tree-table/data.js new file mode 100644 index 00000000..67b0137a --- /dev/null +++ b/src/views/tree-table/data.js @@ -0,0 +1,80 @@ + +const data = [ + { + id: 0, + event: 'Event-0', + timeLine: 50 + }, + { + id: 1, + event: 'Event-1', + timeLine: 100, + children: [ + { + id: 2, + event: 'Event-2', + timeLine: 10 + + }, + { + id: 3, + event: 'Event-3', + timeLine: 90, + children: [ + { + id: 4, + event: 'Event-4', + timeLine: 5 + + }, + { + id: 5, + event: 'Event-5', + timeLine: 10 + + }, + { + id: 6, + event: 'Event-6', + timeLine: 75, + + children: [ + { + id: 7, + event: 'Event-7', + timeLine: 50, + + children: [ + { + id: 71, + event: 'Event-7-1', + timeLine: 25 + + }, + { + id: 72, + event: 'Event-7-2', + timeLine: 5 + + }, + { + id: 73, + event: 'Event-7-3', + timeLine: 20 + } + ] + }, + { + id: 8, + event: 'Event-8', + timeLine: 25 + } + ] + } + ] + } + ] + } +] + +export default data diff --git a/src/views/tree-table/index.vue b/src/views/tree-table/index.vue new file mode 100644 index 00000000..2e131157 --- /dev/null +++ b/src/views/tree-table/index.vue @@ -0,0 +1,127 @@ + + + + +