Commit 0d13646576cd1ce46e506c5bd7e1fd1a3d24c72f

Authored by 梁灏
1 parent 744eb0af

update Table

update Table
src/components/table/mixin.js 0 → 100644
  1 +export default {
  2 + methods: {
  3 + alignCls (column) {
  4 + return column.align ? `${this.prefixCls}-column-${column.align}` : '';
  5 + }
  6 + }
  7 +}
0 8 \ No newline at end of file
... ...
src/components/table/table-head.vue
1 1 <template>
2 2 <thead>
3 3 <tr>
4   - <th v-for="column in columns" :class="fixedCls(column)">{{{ renderHeader(column, $index) }}}</th>
  4 + <th v-for="column in columns" :class="alignCls(column)">
  5 + <div :class="[prefixCls + '-cell']">
  6 + <template v-if="column.type === 'selection'"><Checkbox :checked="isSelectAll" @on-change="selectAll"></Checkbox></template>
  7 + <template v-else>{{{ renderHeader(column, $index) }}}</template>
  8 + </div>
  9 + </th>
5 10 </tr>
6 11 </thead>
7 12 </template>
8 13 <script>
  14 + import Checkbox from '../checkbox/checkbox.vue';
  15 + import Mixin from './mixin';
  16 + import { deepCopy } from '../../utils/assist';
  17 +
9 18 export default {
  19 + mixins: [ Mixin ],
  20 + components: { Checkbox },
10 21 props: {
11 22 prefixCls: String,
12   - columns: Array
  23 + columns: Array,
  24 + cloneData: Array
13 25 },
14 26 data () {
15 27 return {
... ... @@ -17,7 +29,9 @@
17 29 }
18 30 },
19 31 computed: {
20   -
  32 + isSelectAll () {
  33 + return !this.cloneData.some(data => !data._isChecked);
  34 + }
21 35 },
22 36 methods: {
23 37 renderHeader (column, $index) {
... ... @@ -27,8 +41,18 @@
27 41 return column.title || '#';
28 42 }
29 43 },
30   - fixedCls (column) {
31   - return column.fixed ? `${this.prefixCls}-${column.fixed}` : '';
  44 + selectAll () {
  45 + const status = !this.isSelectAll;
  46 +
  47 + let tmpData = deepCopy(this.cloneData);
  48 + tmpData.forEach((data) => {
  49 + data._isChecked = status;
  50 + });
  51 + this.cloneData = tmpData;
  52 +
  53 + if (status) {
  54 + this.$parent.selectAll();
  55 + }
32 56 }
33 57 }
34 58 }
... ...
src/components/table/table.vue
1 1 <template>
2 2 <div :class="classes">
3   - <div :class="[prefixCls + '-header']">
4   - <table cellspacing="0" cellpadding="0" border="0" :style="{width: tableWidth + 'px'}">
  3 + <div :class="[prefixCls + '-header']" v-if="showHeader">
  4 + <table cellspacing="0" cellpadding="0" border="0" :style="tableStyle">
5 5 <colgroup>
6 6 <col v-for="column in columns" :width="setCellWidth(column, $index)">
7 7 </colgroup>
8 8 <thead
9 9 is="table-head"
10   - :prefix-cls="prefixCls + '-thead'"
  10 + :prefix-cls="prefixCls"
  11 + :clone-data.sync="cloneData"
11 12 :columns="columns"></thead>
12 13 </table>
13 14 </div>
14 15 <div :class="[prefixCls + '-body']">
15   - <table cellspacing="0" cellpadding="0" border="0" :style="{width: tableWidth + 'px'}" v-el:tbody>
  16 + <table cellspacing="0" cellpadding="0" border="0" :style="tableStyle" v-el:tbody>
16 17 <colgroup>
17 18 <col v-for="column in columns" :width="setCellWidth(column, $index)">
18 19 </colgroup>
19 20 <tbody :class="[prefixCls + '-tbody']" v-el:render>
20   - <tr :class="[prefixCls + '-row']" v-for="(index, row) in data">
21   - <td v-for="column in columns">{{{ renderRow(row, column) }}}</td>
  21 + <tr
  22 + v-for="(index, row) in data"
  23 + :class="[prefixCls + '-row', {[prefixCls + '-row-highlight']: cloneData[index] && cloneData[index]._isHighlight}]"
  24 + @click.stop="highlightCurrentRow(index)">
  25 + <td v-for="column in columns" :class="alignCls(column)">
  26 + <div :class="[prefixCls + '-cell']">
  27 + <template v-if="column.type === 'selection'">
  28 + <Checkbox :checked="cloneData[index] && cloneData[index]._isChecked" @on-change="toggleSelect(index)"></Checkbox>
  29 + </template>
  30 + <template v-else>{{{ renderRow(row, column, index) }}}</template>
  31 + </div>
  32 + </td>
22 33 </tr>
23 34 </tbody>
24 35 </table>
... ... @@ -27,11 +38,14 @@
27 38 </template>
28 39 <script>
29 40 import TableHead from './table-head.vue';
30   - import { oneOf, getStyle } from '../../utils/assist';
  41 + import Checkbox from '../checkbox/checkbox.vue';
  42 + import Mixin from './mixin';
  43 + import { oneOf, getStyle, deepCopy } from '../../utils/assist';
31 44 const prefixCls = 'ivu-table';
32 45  
33 46 export default {
34   - components: { TableHead },
  47 + mixins: [ Mixin ],
  48 + components: { TableHead, Checkbox },
35 49 props: {
36 50 data: {
37 51 type: Array,
... ... @@ -58,21 +72,11 @@
58 72 type: Boolean,
59 73 default: false
60 74 },
61   - fit: {
62   - type: Boolean,
63   - default: true
64   - },
65 75 showHeader: {
66 76 type: Boolean,
67 77 default: true
68 78 },
69   - selection: {
70   - validator (value) {
71   - return oneOf(value, ['single', 'multiple', false]);
72   - },
73   - default: false
74   - },
75   - showIndex: {
  79 + highlightRow: {
76 80 type: Boolean,
77 81 default: false
78 82 }
... ... @@ -82,7 +86,8 @@
82 86 tableWidth: 0,
83 87 columnsWidth: [],
84 88 prefixCls: prefixCls,
85   - compiledUids: []
  89 + compiledUids: [],
  90 + cloneData: deepCopy(this.data)
86 91 }
87 92 },
88 93 computed: {
... ... @@ -90,14 +95,21 @@
90 95 return [
91 96 `${prefixCls}`,
92 97 {
93   - [`${prefixCls}-${this.size}`]: !!this.size
  98 + [`${prefixCls}-${this.size}`]: !!this.size,
  99 + [`${prefixCls}-border`]: this.border,
  100 + [`${prefixCls}-stripe`]: this.stripe
94 101 }
95 102 ]
  103 + },
  104 + tableStyle () {
  105 + let style = {};
  106 + if (this.tableWidth !== 0) style.width = `${this.tableWidth}px`;
  107 + return style;
96 108 }
97 109 },
98 110 methods: {
99   - renderRow (row, column) {
100   - return 'render' in column ? '' : row[column.key];
  111 + renderRow (row, column, index) {
  112 + return column.type === 'index' ? index + 1 : column.render ? '' : row[column.key];
101 113 },
102 114 compileRender (update = false) {
103 115 this.$nextTick(() => {
... ... @@ -117,7 +129,7 @@
117 129 const column = this.columns[i];
118 130 if (column.render) {
119 131 for (let j = 0; j < this.data.length; j++) {
120   - // todo 做一个缓存,只在需要改render时再重新编译,否则data改变时不用再编译
  132 + // todo 做一个缓存,只在需要改render时再重新编译,data改变时不用再编译
121 133 const row = this.data[j];
122 134 const template = column.render(row, column, j);
123 135 const cell = document.createElement('div');
... ... @@ -129,8 +141,8 @@
129 141 if (_oldParentChildLen !== _newParentChildLen) { // if render normal html node, do not tag
130 142 this.compiledUids.push(this.$parent.$children[this.$parent.$children.length - 1]._uid); // tag it, and delete when data or columns update
131 143 }
132   - $el.children[j].children[i].innerHTML = '';
133   - $el.children[j].children[i].appendChild(cell);
  144 + $el.children[j].children[i].children[0].innerHTML = '';
  145 + $el.children[j].children[i].children[0].appendChild(cell);
134 146 }
135 147 }
136 148 }
... ... @@ -148,6 +160,49 @@
148 160 },
149 161 setCellWidth (column, index) {
150 162 return column.width ? column.width : this.columnsWidth[index];
  163 + },
  164 + highlightCurrentRow (index) {
  165 + if (!this.highlightRow || this.cloneData[index]._isHighlight) return;
  166 +
  167 + let oldIndex = -1;
  168 + this.cloneData.forEach((item, index) => {
  169 + if (item._isHighlight) {
  170 + oldIndex = index;
  171 + item._isHighlight = false;
  172 + return true;
  173 + }
  174 + });
  175 + const row = Object.assign({}, this.cloneData[index], {
  176 + _isHighlight: true
  177 + });
  178 + this.cloneData.$set(index, row);
  179 +
  180 + const oldData = oldIndex < 0 ? null : JSON.parse(JSON.stringify(this.data[oldIndex]));
  181 + this.$emit('on-current-change', JSON.parse(JSON.stringify(this.data[index])), oldData);
  182 + },
  183 + getSelection () {
  184 + let selectionIndexes = [];
  185 + this.cloneData.forEach((data, index) => {
  186 + if (data._isChecked) selectionIndexes.push(index);
  187 + });
  188 +
  189 + return JSON.parse(JSON.stringify(this.data.filter((data, index) => selectionIndexes.indexOf(index) > -1)));
  190 + },
  191 + toggleSelect (index) {
  192 + const status = !this.cloneData[index]._isChecked;
  193 + const row = Object.assign({}, this.cloneData[index], {
  194 + _isChecked: status
  195 + });
  196 + this.cloneData.$set(index, row);
  197 +
  198 + const selection = this.getSelection();
  199 + if (status) {
  200 + this.$emit('on-select', selection, JSON.parse(JSON.stringify(this.data[index])));
  201 + }
  202 + this.$emit('on-selection-change', selection);
  203 + },
  204 + selectAll () {
  205 + this.$emit('on-select-all', this.getSelection());
151 206 }
152 207 },
153 208 ready () {
... ... @@ -160,6 +215,7 @@
160 215 watch: {
161 216 data: {
162 217 handler () {
  218 + this.cloneData = deepCopy(this.data);
163 219 this.compileRender(true);
164 220 },
165 221 deep: true
... ...
src/styles/components/table.less
1 1 @table-prefix-cls: ~"@{css-prefix}table";
2 2  
3 3 .@{table-prefix-cls} {
4   - position: relative;
5   - overflow: hidden;
6   - box-sizing: border-box;
  4 + width: 100%;
7 5 max-width: 100%;
8   - background-color: #fff;
9   - border-collapse: collapse;
10   - border: 1px solid @border-color-base;
  6 + overflow: hidden;
11 7 color: @text-color;
12 8 font-size: @font-size-small;
  9 + background-color: #fff;
  10 + border: 1px solid @border-color-base;
  11 + border-bottom: 0;
  12 + border-collapse: collapse;
  13 + box-sizing: border-box;
  14 + position: relative;
13 15  
14   - &-large {
15   - font-size: @font-size-base;
  16 + th, td
  17 + {
  18 + min-width: 0;
  19 + height: 48px;
  20 + box-sizing: border-box;
  21 + text-align: left;
  22 + text-overflow: ellipsis;
  23 + vertical-align: middle;
  24 + position: relative;
  25 + border-bottom: 1px solid @border-color-split;
16 26 }
17 27  
18   - & th {
  28 + th {
  29 + height: 40px;
19 30 white-space: nowrap;
20 31 overflow: hidden;
  32 + background-color: @table-thead-bg;
  33 + }
  34 + td{
  35 + background-color: #fff;
  36 + transition: background-color @transition-time @ease-in-out;
21 37 }
22 38  
23   - & th,
24   - td
  39 + th&-column,
  40 + td&-column
25 41 {
26   - min-width: 0;
27   - height: 40px;
28   - box-sizing: border-box;
29   - text-align: left;
  42 + &-left{
  43 + text-align: left;
  44 + }
  45 + &-center{
  46 + text-align: center;
  47 + }
  48 + &-right{
  49 + text-align: right;
  50 + }
  51 + }
  52 +
  53 + & table{
  54 + width: 100%;
  55 + }
  56 + &-border{
  57 + th,td{
  58 + border-right: 1px solid @border-color-split;
  59 + }
  60 + }
  61 + &-cell{
  62 + padding-left: 18px;
  63 + padding-right: 18px;
  64 + overflow: hidden;
30 65 text-overflow: ellipsis;
31   - vertical-align: middle;
  66 + white-space: normal;
  67 + word-break: break-all;
  68 + box-sizing: border-box;
  69 + }
  70 + th &-cell{
  71 + display: inline-block;
32 72 position: relative;
33   - border-bottom: 1px solid @border-color-base;
  73 + word-wrap: normal;
  74 + vertical-align: middle;
  75 + }
  76 +
  77 + &-stripe &-body{
  78 + tr:nth-child(2n) {
  79 + td{
  80 + background-color: @table-td-stripe-bg;
  81 + }
  82 + }
  83 + }
  84 +
  85 + tr:hover{
  86 + td{
  87 + background-color: @table-td-hover-bg;
  88 + }
  89 + }
  90 +
  91 + &-large {
  92 + font-size: @font-size-base;
  93 + th{
  94 + height: 48px;
  95 + }
  96 + td{
  97 + height: 60px;
  98 + }
  99 + }
  100 +
  101 + &-small{
  102 + th{
  103 + height: 32px;
  104 + }
  105 + td{
  106 + height: 40px;
  107 + }
  108 + }
  109 +
  110 + &-row-highlight,
  111 + tr&-row-highlight:hover,
  112 + &-stripe &-body tr&-row-highlight:nth-child(2n)
  113 + {
  114 + td{
  115 + background-color: @table-td-highlight-bg;
  116 + }
34 117 }
35 118 }
36 119 \ No newline at end of file
... ...
src/styles/themes/default/custom.less
... ... @@ -38,6 +38,10 @@
38 38 @background-color-select-hover: @input-disabled-bg;
39 39 @tooltip-bg : rgba(70, 76, 91, .9);
40 40 @head-bg : #f9fafc;
  41 +@table-thead-bg : #f5f7f9;
  42 +@table-td-stripe-bg : #f5f7f9;
  43 +@table-td-hover-bg : #ebf7ff;
  44 +@table-td-highlight-bg : #ebf7ff;
41 45  
42 46 // Shadow
43 47 @shadow-color : rgba(0, 0, 0, .2);
... ...
src/utils/assist.js
... ... @@ -70,7 +70,7 @@ export function getStyle (element, styleName) {
70 70 styleName = 'cssFloat';
71 71 }
72 72 try {
73   - var computed = document.defaultView.getComputedStyle(element, '');
  73 + const computed = document.defaultView.getComputedStyle(element, '');
74 74 return element.style[styleName] || computed ? computed[styleName] : null;
75 75 } catch(e) {
76 76 return element.style[styleName];
... ... @@ -87,4 +87,48 @@ export function warnProp(component, prop, correctType, wrongType) {
87 87 correctType = firstUpperCase(correctType);
88 88 wrongType = firstUpperCase(wrongType);
89 89 console.error(`[iView warn]: Invalid prop: type check failed for prop ${prop}. Expected ${correctType}, got ${wrongType}. (found in component: ${component})`);
90   -}
91 90 \ No newline at end of file
  91 +}
  92 +
  93 +function typeOf(obj) {
  94 + const toString = Object.prototype.toString;
  95 + const map = {
  96 + '[object Boolean]' : 'boolean',
  97 + '[object Number]' : 'number',
  98 + '[object String]' : 'string',
  99 + '[object Function]' : 'function',
  100 + '[object Array]' : 'array',
  101 + '[object Date]' : 'date',
  102 + '[object RegExp]' : 'regExp',
  103 + '[object Undefined]': 'undefined',
  104 + '[object Null]' : 'null',
  105 + '[object Object]' : 'object'
  106 + };
  107 + return map[toString.call(obj)];
  108 +}
  109 +
  110 +// deepCopy
  111 +function deepCopy(data) {
  112 + const t = typeOf(data);
  113 + let o;
  114 +
  115 + if (t === 'array') {
  116 + o = [];
  117 + } else if ( t === 'object') {
  118 + o = {};
  119 + } else {
  120 + return data;
  121 + }
  122 +
  123 + if (t === 'array') {
  124 + for (let i = 0; i < data.length; i++) {
  125 + o.push(deepCopy(data[i]));
  126 + }
  127 + } else if ( t === 'object') {
  128 + for (let i in data) {
  129 + o[i] = deepCopy(data[i]);
  130 + }
  131 + }
  132 + return o;
  133 +}
  134 +
  135 +export {deepCopy}
92 136 \ No newline at end of file
... ...
test/routers/table.vue
1 1 <template>
2 2 <div>
3   - <i-table :columns="columns" :data="data"></i-table>
  3 + <!--<i-table size="large" border stripe :columns="columns" :data="data"></i-table>-->
  4 + <br>
  5 + <i-table border :columns="columns" :data="data" @on-current-change="current" @on-select="select" @on-selection-change="schange" @on-select-all="sall"></i-table>
  6 + <br>
  7 + <!--<i-table size="small" border stripe :columns="columns" :data="data"></i-table>-->
4 8 </div>
5 9 </template>
6 10 <script>
... ... @@ -12,15 +16,19 @@
12 16 return {
13 17 columns: [
14 18 {
  19 + type: 'selection',
  20 + width: 50
  21 + },
  22 + {
15 23 title: '姓名',
16 24 key: 'name',
17   - fixed: 'left',
  25 + align: 'left',
18 26 // width: 100
19 27 },
20 28 {
21 29 title: '年龄',
22 30 key: 'age',
23   - fixed: 'right',
  31 + align: 'right',
24 32 // width: 100
25 33 // render (row) {
26 34 // return `<i-button>${row.age}</i-button>`
... ... @@ -29,7 +37,7 @@
29 37 {
30 38 title: '地址',
31 39 key: 'address',
32   - fixed: 'center',
  40 + align: 'center',
33 41 // width: 100
34 42 // render (row, column, index) {
35 43 // if (row.edit) {
... ... @@ -65,7 +73,13 @@
65 73 name: '刘天娇',
66 74 age: 27,
67 75 address: '北京市东城区',
68   - edit: true
  76 + edit: false
  77 + },
  78 + {
  79 + name: '胡国伟',
  80 + age: 28,
  81 + address: '北京市西城区',
  82 + edit: false
69 83 }
70 84 ]
71 85 }
... ... @@ -78,12 +92,27 @@
78 92 this.$Message.info(name);
79 93 },
80 94 edit (index) {
81   - this.data[index].edit = true;
  95 +// this.data[index].edit = true;
  96 + this.$Message.info(this.data[index].name);
  97 + },
  98 + current (newData, oldData) {
  99 + console.log(newData);
  100 + console.log(oldData);
  101 + },
  102 + select (a,b){
  103 + console.log(a);
  104 + console.log(b);
  105 + },
  106 + schange (a) {
  107 + console.log(a)
  108 + },
  109 + sall (a) {
  110 + console.log(a)
82 111 }
83 112 },
84 113 ready () {
85 114 setTimeout(() => {
86   - return;
  115 +// return
87 116 this.data.push({
88 117 name: '刘天娇2',
89 118 age: 272,
... ...