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 1 <template>
2   - <Table border :columns="columns5" :data="data5"></Table>
  2 + <Table :columns="columns7" :data="data6" @on-expand="expand"></Table>
3 3 </template>
4 4 <script>
  5 + import etable from '../components/table.vue';
5 6 export default {
  7 + components: { etable },
6 8 data () {
7 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 20 title: '姓名',
16 21 key: 'name',
17 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 38 {
27 39 title: '地址',
28 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 80 name: '王小明',
34 81 age: 18,
35   - address: '北京市朝阳区芍药居',
36   - date: '2016-10-03'
  82 + address: '北京市朝阳区芍药居'
37 83 },
38 84 {
39 85 name: '张小刚',
40 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 118 </script>
... ...
src/components/table/cell.vue
... ... @@ -5,16 +5,22 @@
5 5 <Checkbox :value="checked" @on-change="toggleSelect" :disabled="disabled"></Checkbox>
6 6 </template>
7 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 13 </div>
9 14 </template>
10 15 <script>
11 16 import Vue from 'vue';
  17 + import Icon from '../icon/icon.vue';
12 18 import Checkbox from '../checkbox/checkbox.vue';
13 19 import { findComponentUpward } from '../../utils/assist';
14 20  
15 21 export default {
16 22 name: 'TableCell',
17   - components: { Checkbox },
  23 + components: { Icon, Checkbox },
18 24 props: {
19 25 prefixCls: String,
20 26 row: Object,
... ... @@ -23,6 +29,7 @@
23 29 index: Number, // _index of data
24 30 checked: Boolean,
25 31 disabled: Boolean,
  32 + expanded: Boolean,
26 33 fixed: {
27 34 type: [Boolean, String],
28 35 default: false
... ... @@ -41,14 +48,23 @@
41 48 `${this.prefixCls}-cell`,
42 49 {
43 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 65 methods: {
50 66 compile () {
51   - if (this.column.render) {
  67 + if (this.column.render && this.renderType === 'render') {
52 68 // 兼容真 Render,后期废弃旧用法
53 69 let isRealRender = true;
54 70 const Table = findComponentUpward(this, 'Table');
... ... @@ -114,6 +130,9 @@
114 130 },
115 131 toggleSelect () {
116 132 this.$parent.$parent.toggleSelect(this.index);
  133 + },
  134 + toggleExpand () {
  135 + this.$parent.$parent.toggleExpand(this.index);
117 136 }
118 137 },
119 138 created () {
... ... @@ -121,6 +140,8 @@
121 140 this.renderType = 'index';
122 141 } else if (this.column.type === 'selection') {
123 142 this.renderType = 'selection';
  143 + } else if (this.column.type === 'expand') {
  144 + this.renderType = 'expand';
124 145 } else if (this.column.render) {
125 146 this.renderType = 'render';
126 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 38 \ No newline at end of file
... ...
src/components/table/table-body.vue
... ... @@ -4,39 +4,47 @@
4 4 <col v-for="(column, index) in columns" :width="setCellWidth(column, index, false)">
5 5 </colgroup>
6 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 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 35 </tbody>
29 36 </table>
30 37 </template>
31 38 <script>
32 39 // todo :key="row"
33 40 import Cell from './cell.vue';
  41 + import Expand from './expand.vue';
34 42 import Mixin from './mixin';
35 43  
36 44 export default {
37 45 name: 'TableBody',
38 46 mixins: [ Mixin ],
39   - components: { Cell },
  47 + components: { Cell, Expand },
40 48 props: {
41 49 prefixCls: String,
42 50 styleObject: Object,
... ... @@ -49,6 +57,20 @@
49 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 74 methods: {
53 75 rowClasses (_index) {
54 76 return [
... ... @@ -66,6 +88,9 @@
66 88 rowDisabled(_index){
67 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 94 rowClsName (_index) {
70 95 return this.$parent.rowClassName(this.objData[_index], _index);
71 96 },
... ...
src/components/table/table-head.vue
... ... @@ -7,7 +7,8 @@
7 7 <tr>
8 8 <th v-for="(column, index) in columns" :class="alignCls(column)">
9 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 12 <template v-else>
12 13 <span v-html="renderHeader(column, index)"></span>
13 14 <span :class="[prefixCls + '-sort']" v-if="column.sortable">
... ...
src/components/table/table.vue
... ... @@ -404,6 +404,18 @@
404 404 }
405 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 419 selectAll (status) {
408 420 // this.rebuildData.forEach((data) => {
409 421 // if(this.objData[data._index]._isDisabled){
... ... @@ -581,9 +593,9 @@
581 593 this.data.forEach((row, index) => {
582 594 const newRow = deepCopy(row);// todo 直接替换
583 595 newRow._isHover = false;
584   - if(newRow._disabled){
  596 + if (newRow._disabled) {
585 597 newRow._isDisabled = newRow._disabled;
586   - }else{
  598 + } else {
587 599 newRow._isDisabled = false;
588 600 }
589 601 if (newRow._checked) {
... ... @@ -591,6 +603,11 @@
591 603 } else {
592 604 newRow._isChecked = false;
593 605 }
  606 + if (newRow._expanded) {
  607 + newRow._isExpanded = newRow._expanded;
  608 + } else {
  609 + newRow._isExpanded = false;
  610 + }
594 611 if (newRow._highlight) {
595 612 newRow._isHighlight = newRow._highlight;
596 613 } else {
... ...
src/styles/components/table.less
... ... @@ -145,6 +145,24 @@
145 145 overflow: hidden;
146 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 167 &-hidden{
150 168 visibility: hidden;
... ... @@ -184,6 +202,13 @@
184 202 height: 60px;
185 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 214 &-small{
... ... @@ -197,6 +222,10 @@
197 222 height: 40px;
198 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 231 &-row-highlight,
... ...