Commit 08fd628d1f5badec33d62d12198a58a4eb4f710c

Authored by Aresn
1 parent df64fd36

Table support expand

examples/components/table.vue 0 → 100644
  1 +<template>
  2 + <Table width="550" border :columns="columns2" :data="data3"></Table>
  3 +</template>
  4 +<script>
  5 + export default {
  6 + name: 'etable',
  7 + data () {
  8 + return {
  9 + columns2: [
  10 + {
  11 + title: '姓名',
  12 + key: 'name',
  13 + width: 100,
  14 + fixed: 'left'
  15 + },
  16 + {
  17 + title: '年龄',
  18 + key: 'age',
  19 + width: 100
  20 + },
  21 + {
  22 + title: '省份',
  23 + key: 'province',
  24 + width: 100
  25 + },
  26 + {
  27 + title: '市区',
  28 + key: 'city',
  29 + width: 100
  30 + },
  31 + {
  32 + title: '地址',
  33 + key: 'address',
  34 + width: 200
  35 + },
  36 + {
  37 + title: '邮编',
  38 + key: 'zip',
  39 + width: 100
  40 + },
  41 + {
  42 + title: '操作',
  43 + key: 'action',
  44 + fixed: 'right',
  45 + width: 120,
  46 + render: (h, params) => {
  47 + return h('div', [
  48 + h('Button', {
  49 + props: {
  50 + type: 'text',
  51 + size: 'small'
  52 + }
  53 + }, '查看'),
  54 + h('Button', {
  55 + props: {
  56 + type: 'text',
  57 + size: 'small'
  58 + }
  59 + }, '编辑')
  60 + ]);
  61 + }
  62 + }
  63 + ],
  64 + data3: [
  65 + {
  66 + name: '王小明',
  67 + age: 18,
  68 + address: '北京市朝阳区芍药居',
  69 + province: '北京市',
  70 + city: '朝阳区',
  71 + zip: 100000
  72 + },
  73 + {
  74 + name: '张小刚',
  75 + age: 25,
  76 + address: '北京市海淀区西二旗',
  77 + province: '北京市',
  78 + city: '海淀区',
  79 + zip: 100000
  80 + },
  81 + {
  82 + name: '李小红',
  83 + age: 30,
  84 + address: '上海市浦东新区世纪大道',
  85 + province: '上海市',
  86 + city: '浦东新区',
  87 + zip: 100000
  88 + },
  89 + {
  90 + name: '周小伟',
  91 + age: 26,
  92 + address: '深圳市南山区深南大道',
  93 + province: '广东',
  94 + city: '南山区',
  95 + zip: 100000
  96 + }
  97 + ]
  98 + }
  99 + }
  100 + }
  101 +</script>
examples/routers/table.vue
1 <template> 1 <template>
2 - <Table border :columns="columns5" :data="data5"></Table> 2 + <Table :columns="columns7" :data="data6" @on-expand="expand"></Table>
3 </template> 3 </template>
4 <script> 4 <script>
  5 + import etable from '../components/table.vue';
5 export default { 6 export default {
  7 + components: { etable },
6 data () { 8 data () {
7 return { 9 return {
8 - columns5: [ 10 + columns7: [
9 { 11 {
10 - title: '日期',  
11 - key: 'date',  
12 - sortable: true 12 + type: 'expand',
  13 + width: 50,
  14 + render: (h, params) => {
  15 +// return h(etable);
  16 + return h('div', params.row.name)
  17 + }
13 }, 18 },
14 { 19 {
15 title: '姓名', 20 title: '姓名',
16 key: 'name', 21 key: 'name',
17 render: (h, params) => { 22 render: (h, params) => {
18 - return h('div', params.row.name); 23 + return h('div', [
  24 + h('Icon', {
  25 + props: {
  26 + type: 'person'
  27 + }
  28 + }),
  29 + h('strong', params.row.name)
  30 + ]);
19 } 31 }
20 }, 32 },
21 { 33 {
@@ -26,23 +38,81 @@ @@ -26,23 +38,81 @@
26 { 38 {
27 title: '地址', 39 title: '地址',
28 key: 'address' 40 key: 'address'
  41 + },
  42 + {
  43 + title: '操作',
  44 + key: 'action',
  45 + width: 150,
  46 + align: 'center',
  47 + render: (h, params) => {
  48 + return h('div', [
  49 + h('Button', {
  50 + props: {
  51 + type: 'primary',
  52 + size: 'small'
  53 + },
  54 + style: {
  55 + marginRight: '5px'
  56 + },
  57 + on: {
  58 + click: () => {
  59 + this.show(params.index)
  60 + }
  61 + }
  62 + }, '查看'),
  63 + h('Button', {
  64 + props: {
  65 + type: 'error',
  66 + size: 'small'
  67 + },
  68 + on: {
  69 + click: () => {
  70 + this.remove(params.index)
  71 + }
  72 + }
  73 + }, '删除')
  74 + ]);
  75 + }
29 } 76 }
30 ], 77 ],
31 - data5: [ 78 + data6: [
32 { 79 {
33 name: '王小明', 80 name: '王小明',
34 age: 18, 81 age: 18,
35 - address: '北京市朝阳区芍药居',  
36 - date: '2016-10-03' 82 + address: '北京市朝阳区芍药居'
37 }, 83 },
38 { 84 {
39 name: '张小刚', 85 name: '张小刚',
40 age: 25, 86 age: 25,
41 - address: '北京市海淀区西二旗',  
42 - date: '2016-10-01' 87 + address: '北京市海淀区西二旗'
  88 + },
  89 + {
  90 + name: '李小红',
  91 + age: 30,
  92 + address: '上海市浦东新区世纪大道'
  93 + },
  94 + {
  95 + name: '周小伟',
  96 + age: 26,
  97 + address: '深圳市南山区深南大道'
43 } 98 }
44 ] 99 ]
45 } 100 }
  101 + },
  102 + methods: {
  103 + show (index) {
  104 + this.$Modal.info({
  105 + title: '用户信息',
  106 + content: `姓名:${this.data6[index].name}<br>年龄:${this.data6[index].age}<br>地址:${this.data6[index].address}`
  107 + })
  108 + },
  109 + remove (index) {
  110 + this.data6.splice(index, 1);
  111 + },
  112 + expand (row, s) {
  113 +// console.log(row);
  114 +// console.log(s);
  115 + }
46 } 116 }
47 } 117 }
48 </script> 118 </script>
src/components/table/cell.vue
@@ -5,16 +5,22 @@ @@ -5,16 +5,22 @@
5 <Checkbox :value="checked" @on-change="toggleSelect" :disabled="disabled"></Checkbox> 5 <Checkbox :value="checked" @on-change="toggleSelect" :disabled="disabled"></Checkbox>
6 </template> 6 </template>
7 <template v-if="renderType === 'normal'"><span v-html="row[column.key]"></span></template> 7 <template v-if="renderType === 'normal'"><span v-html="row[column.key]"></span></template>
  8 + <template v-if="renderType === 'expand'">
  9 + <div :class="expandCls" @click="toggleExpand">
  10 + <Icon type="ios-arrow-right"></Icon>
  11 + </div>
  12 + </template>
8 </div> 13 </div>
9 </template> 14 </template>
10 <script> 15 <script>
11 import Vue from 'vue'; 16 import Vue from 'vue';
  17 + import Icon from '../icon/icon.vue';
12 import Checkbox from '../checkbox/checkbox.vue'; 18 import Checkbox from '../checkbox/checkbox.vue';
13 import { findComponentUpward } from '../../utils/assist'; 19 import { findComponentUpward } from '../../utils/assist';
14 20
15 export default { 21 export default {
16 name: 'TableCell', 22 name: 'TableCell',
17 - components: { Checkbox }, 23 + components: { Icon, Checkbox },
18 props: { 24 props: {
19 prefixCls: String, 25 prefixCls: String,
20 row: Object, 26 row: Object,
@@ -23,6 +29,7 @@ @@ -23,6 +29,7 @@
23 index: Number, // _index of data 29 index: Number, // _index of data
24 checked: Boolean, 30 checked: Boolean,
25 disabled: Boolean, 31 disabled: Boolean,
  32 + expanded: Boolean,
26 fixed: { 33 fixed: {
27 type: [Boolean, String], 34 type: [Boolean, String],
28 default: false 35 default: false
@@ -41,14 +48,23 @@ @@ -41,14 +48,23 @@
41 `${this.prefixCls}-cell`, 48 `${this.prefixCls}-cell`,
42 { 49 {
43 [`${this.prefixCls}-hidden`]: !this.fixed && this.column.fixed && (this.column.fixed === 'left' || this.column.fixed === 'right'), 50 [`${this.prefixCls}-hidden`]: !this.fixed && this.column.fixed && (this.column.fixed === 'left' || this.column.fixed === 'right'),
44 - [`${this.prefixCls}-cell-ellipsis`]: this.column.ellipsis || false 51 + [`${this.prefixCls}-cell-ellipsis`]: this.column.ellipsis || false,
  52 + [`${this.prefixCls}-cell-with-expand`]: this.renderType === 'expand'
45 } 53 }
46 ]; 54 ];
  55 + },
  56 + expandCls () {
  57 + return [
  58 + `${this.prefixCls}-cell-expand`,
  59 + {
  60 + [`${this.prefixCls}-cell-expand-expanded`]: this.expanded
  61 + }
  62 + ]
47 } 63 }
48 }, 64 },
49 methods: { 65 methods: {
50 compile () { 66 compile () {
51 - if (this.column.render) { 67 + if (this.column.render && this.renderType === 'render') {
52 // 兼容真 Render,后期废弃旧用法 68 // 兼容真 Render,后期废弃旧用法
53 let isRealRender = true; 69 let isRealRender = true;
54 const Table = findComponentUpward(this, 'Table'); 70 const Table = findComponentUpward(this, 'Table');
@@ -114,6 +130,9 @@ @@ -114,6 +130,9 @@
114 }, 130 },
115 toggleSelect () { 131 toggleSelect () {
116 this.$parent.$parent.toggleSelect(this.index); 132 this.$parent.$parent.toggleSelect(this.index);
  133 + },
  134 + toggleExpand () {
  135 + this.$parent.$parent.toggleExpand(this.index);
117 } 136 }
118 }, 137 },
119 created () { 138 created () {
@@ -121,6 +140,8 @@ @@ -121,6 +140,8 @@
121 this.renderType = 'index'; 140 this.renderType = 'index';
122 } else if (this.column.type === 'selection') { 141 } else if (this.column.type === 'selection') {
123 this.renderType = 'selection'; 142 this.renderType = 'selection';
  143 + } else if (this.column.type === 'expand') {
  144 + this.renderType = 'expand';
124 } else if (this.column.render) { 145 } else if (this.column.render) {
125 this.renderType = 'render'; 146 this.renderType = 'render';
126 } else { 147 } else {
src/components/table/expand.vue 0 → 100644
  1 +<template>
  2 + <div ref="cell"></div>
  3 +</template>
  4 +<script>
  5 + import Vue from 'vue';
  6 + export default {
  7 + name: 'TableExpand',
  8 + props: {
  9 + row: Object,
  10 + render: Function,
  11 + index: Number,
  12 + },
  13 + methods: {
  14 + compile () {
  15 + if (this.render) {
  16 + this.$el.innerHTML = '';
  17 + const component = new Vue({
  18 + functional: true,
  19 + render: (h) => {
  20 + return this.render(h, {
  21 + row: this.row,
  22 + index: this.index
  23 + });
  24 + }
  25 + });
  26 + const Cell = component.$mount();
  27 + this.$refs.cell.appendChild(Cell.$el);
  28 + }
  29 + }
  30 + },
  31 + mounted () {
  32 + this.$nextTick(() => {
  33 + this.compile();
  34 + });
  35 + }
  36 + };
  37 +</script>
0 \ No newline at end of file 38 \ No newline at end of file
src/components/table/table-body.vue
@@ -4,39 +4,47 @@ @@ -4,39 +4,47 @@
4 <col v-for="(column, index) in columns" :width="setCellWidth(column, index, false)"> 4 <col v-for="(column, index) in columns" :width="setCellWidth(column, index, false)">
5 </colgroup> 5 </colgroup>
6 <tbody :class="[prefixCls + '-tbody']"> 6 <tbody :class="[prefixCls + '-tbody']">
7 - <tr  
8 - v-for="(row, index) in data"  
9 - :key="row"  
10 - :class="rowClasses(row._index)"  
11 - @mouseenter.stop="handleMouseIn(row._index)"  
12 - @mouseleave.stop="handleMouseOut(row._index)"  
13 - @click.stop="clickCurrentRow(row._index)"  
14 - @dblclick.stop="dblclickCurrentRow(row._index)">  
15 - <td v-for="column in columns" :class="alignCls(column, row)">  
16 - <Cell  
17 - :fixed="fixed"  
18 - :prefix-cls="prefixCls"  
19 - :row="row"  
20 - :column="column"  
21 - :natural-index="index"  
22 - :index="row._index"  
23 - :checked="rowChecked(row._index)"  
24 - :disabled="rowDisabled(row._index)" 7 + <template v-for="(row, index) in data">
  8 + <tr
  9 + :key="row"
  10 + :class="rowClasses(row._index)"
  11 + @mouseenter.stop="handleMouseIn(row._index)"
  12 + @mouseleave.stop="handleMouseOut(row._index)"
  13 + @click.stop="clickCurrentRow(row._index)"
  14 + @dblclick.stop="dblclickCurrentRow(row._index)">
  15 + <td v-for="column in columns" :class="alignCls(column, row)">
  16 + <Cell
  17 + :fixed="fixed"
  18 + :prefix-cls="prefixCls"
  19 + :row="row"
  20 + :column="column"
  21 + :natural-index="index"
  22 + :index="row._index"
  23 + :checked="rowChecked(row._index)"
  24 + :disabled="rowDisabled(row._index)"
  25 + :expanded="rowExpanded(row._index)"
25 ></Cell> 26 ></Cell>
26 - </td>  
27 - </tr> 27 + </td>
  28 + </tr>
  29 + <tr v-if="rowExpanded(row._index)">
  30 + <td :colspan="columns.length">
  31 + <Expand :row="row" :render="expandRender" :index="row._index"></Expand>
  32 + </td>
  33 + </tr>
  34 + </template>
28 </tbody> 35 </tbody>
29 </table> 36 </table>
30 </template> 37 </template>
31 <script> 38 <script>
32 // todo :key="row" 39 // todo :key="row"
33 import Cell from './cell.vue'; 40 import Cell from './cell.vue';
  41 + import Expand from './expand.vue';
34 import Mixin from './mixin'; 42 import Mixin from './mixin';
35 43
36 export default { 44 export default {
37 name: 'TableBody', 45 name: 'TableBody',
38 mixins: [ Mixin ], 46 mixins: [ Mixin ],
39 - components: { Cell }, 47 + components: { Cell, Expand },
40 props: { 48 props: {
41 prefixCls: String, 49 prefixCls: String,
42 styleObject: Object, 50 styleObject: Object,
@@ -49,6 +57,20 @@ @@ -49,6 +57,20 @@
49 default: false 57 default: false
50 } 58 }
51 }, 59 },
  60 + computed: {
  61 + expandRender () {
  62 + let render = function () {
  63 + return '';
  64 + };
  65 + for (let i = 0; i < this.columns.length; i++) {
  66 + const column = this.columns[i];
  67 + if (column.type && column.type === 'expand') {
  68 + if (column.render) render = column.render;
  69 + }
  70 + }
  71 + return render;
  72 + }
  73 + },
52 methods: { 74 methods: {
53 rowClasses (_index) { 75 rowClasses (_index) {
54 return [ 76 return [
@@ -66,6 +88,9 @@ @@ -66,6 +88,9 @@
66 rowDisabled(_index){ 88 rowDisabled(_index){
67 return this.objData[_index] && this.objData[_index]._isDisabled; 89 return this.objData[_index] && this.objData[_index]._isDisabled;
68 }, 90 },
  91 + rowExpanded(_index){
  92 + return this.objData[_index] && this.objData[_index]._isExpanded;
  93 + },
69 rowClsName (_index) { 94 rowClsName (_index) {
70 return this.$parent.rowClassName(this.objData[_index], _index); 95 return this.$parent.rowClassName(this.objData[_index], _index);
71 }, 96 },
src/components/table/table-head.vue
@@ -7,7 +7,8 @@ @@ -7,7 +7,8 @@
7 <tr> 7 <tr>
8 <th v-for="(column, index) in columns" :class="alignCls(column)"> 8 <th v-for="(column, index) in columns" :class="alignCls(column)">
9 <div :class="cellClasses(column)"> 9 <div :class="cellClasses(column)">
10 - <template v-if="column.type === 'selection'"><Checkbox :value="isSelectAll" @on-change="selectAll"></Checkbox></template> 10 + <template v-if="column.type === 'expand'"></template>
  11 + <template v-else-if="column.type === 'selection'"><Checkbox :value="isSelectAll" @on-change="selectAll"></Checkbox></template>
11 <template v-else> 12 <template v-else>
12 <span v-html="renderHeader(column, index)"></span> 13 <span v-html="renderHeader(column, index)"></span>
13 <span :class="[prefixCls + '-sort']" v-if="column.sortable"> 14 <span :class="[prefixCls + '-sort']" v-if="column.sortable">
src/components/table/table.vue
@@ -404,6 +404,18 @@ @@ -404,6 +404,18 @@
404 } 404 }
405 this.$emit('on-selection-change', selection); 405 this.$emit('on-selection-change', selection);
406 }, 406 },
  407 + toggleExpand (_index) {
  408 + let data = {};
  409 +
  410 + for (let i in this.objData) {
  411 + if (parseInt(i) === _index) {
  412 + data = this.objData[i];
  413 + }
  414 + }
  415 + const status = !data._isExpanded;
  416 + this.objData[_index]._isExpanded = status;
  417 + this.$emit('on-expand', JSON.parse(JSON.stringify(this.cloneData[_index])), status);
  418 + },
407 selectAll (status) { 419 selectAll (status) {
408 // this.rebuildData.forEach((data) => { 420 // this.rebuildData.forEach((data) => {
409 // if(this.objData[data._index]._isDisabled){ 421 // if(this.objData[data._index]._isDisabled){
@@ -581,9 +593,9 @@ @@ -581,9 +593,9 @@
581 this.data.forEach((row, index) => { 593 this.data.forEach((row, index) => {
582 const newRow = deepCopy(row);// todo 直接替换 594 const newRow = deepCopy(row);// todo 直接替换
583 newRow._isHover = false; 595 newRow._isHover = false;
584 - if(newRow._disabled){ 596 + if (newRow._disabled) {
585 newRow._isDisabled = newRow._disabled; 597 newRow._isDisabled = newRow._disabled;
586 - }else{ 598 + } else {
587 newRow._isDisabled = false; 599 newRow._isDisabled = false;
588 } 600 }
589 if (newRow._checked) { 601 if (newRow._checked) {
@@ -591,6 +603,11 @@ @@ -591,6 +603,11 @@
591 } else { 603 } else {
592 newRow._isChecked = false; 604 newRow._isChecked = false;
593 } 605 }
  606 + if (newRow._expanded) {
  607 + newRow._isExpanded = newRow._expanded;
  608 + } else {
  609 + newRow._isExpanded = false;
  610 + }
594 if (newRow._highlight) { 611 if (newRow._highlight) {
595 newRow._isHighlight = newRow._highlight; 612 newRow._isHighlight = newRow._highlight;
596 } else { 613 } else {
src/styles/components/table.less
@@ -145,6 +145,24 @@ @@ -145,6 +145,24 @@
145 overflow: hidden; 145 overflow: hidden;
146 text-overflow: ellipsis; 146 text-overflow: ellipsis;
147 } 147 }
  148 +
  149 + &-with-expand{
  150 + height: 47px;
  151 + line-height: 47px;
  152 + padding: 0;
  153 + text-align: center;
  154 + }
  155 +
  156 + &-expand{
  157 + cursor: pointer;
  158 + transition: transform @transition-time @ease-in-out;
  159 + i{
  160 + font-size: @font-size-base;
  161 + }
  162 + &-expanded{
  163 + transform: rotate(90deg);
  164 + }
  165 + }
148 } 166 }
149 &-hidden{ 167 &-hidden{
150 visibility: hidden; 168 visibility: hidden;
@@ -184,6 +202,13 @@ @@ -184,6 +202,13 @@
184 height: 60px; 202 height: 60px;
185 line-height: 60px; 203 line-height: 60px;
186 } 204 }
  205 + .@{table-prefix-cls}-cell-with-expand{
  206 + height: 59px;
  207 + line-height: 59px;
  208 + i{
  209 + font-size: @font-size-base+2;
  210 + }
  211 + }
187 } 212 }
188 213
189 &-small{ 214 &-small{
@@ -197,6 +222,10 @@ @@ -197,6 +222,10 @@
197 height: 40px; 222 height: 40px;
198 line-height: 40px; 223 line-height: 40px;
199 } 224 }
  225 + .@{table-prefix-cls}-cell-with-expand{
  226 + height: 39px;
  227 + line-height: 39px;
  228 + }
200 } 229 }
201 230
202 &-row-highlight, 231 &-row-highlight,