Commit a3547c1b41b9f411fdc00d327ee093a0cd260dfe
1 parent
abdec99d
update Table
update Table
Showing
4 changed files
with
148 additions
and
65 deletions
Show diff stats
| 1 | +<template> | ||
| 2 | + <div :class="[prefixCls + '-cell']"> | ||
| 3 | + <template v-if="renderType === 'index'">{{index + 1}}</template> | ||
| 4 | + <template v-if="renderType === 'normal'">{{{ row[column.key] }}}</template> | ||
| 5 | + </div> | ||
| 6 | +</template> | ||
| 7 | +<script> | ||
| 8 | + export default { | ||
| 9 | + props: { | ||
| 10 | + prefixCls: String, | ||
| 11 | + row: Object, | ||
| 12 | + column: Object, | ||
| 13 | + index: Number | ||
| 14 | + }, | ||
| 15 | + data () { | ||
| 16 | + return { | ||
| 17 | + renderType: '', | ||
| 18 | + uid: -1 | ||
| 19 | + } | ||
| 20 | + }, | ||
| 21 | + methods: { | ||
| 22 | + compile () { | ||
| 23 | + if (this.column.render) { | ||
| 24 | + const template = this.column.render(this.row, this.column, this.index); | ||
| 25 | + const cell = document.createElement('div'); | ||
| 26 | + cell.innerHTML = template; | ||
| 27 | + const _oldParentChildLen = this.$parent.$parent.$children.length; | ||
| 28 | + this.$parent.$parent.$compile(cell); | ||
| 29 | + const _newParentChildLen = this.$parent.$parent.$children.length; | ||
| 30 | + | ||
| 31 | + if (_oldParentChildLen !== _newParentChildLen) { // if render normal html node, do not tag | ||
| 32 | + this.uid = this.$parent.$parent.$children[this.$parent.$parent.$children.length - 1]._uid; // tag it, and delete when data or columns update | ||
| 33 | + } | ||
| 34 | + this.$el.innerHTML = ''; | ||
| 35 | + this.$el.appendChild(cell); | ||
| 36 | + } | ||
| 37 | + }, | ||
| 38 | + destroy () { | ||
| 39 | + for (let i = 0; i < this.$parent.$parent.$children.length; i++) { | ||
| 40 | + if (this.$parent.$parent.$children[i]._uid === this.uid) { | ||
| 41 | + this.$parent.$parent.$children[i].$destroy(); | ||
| 42 | + } | ||
| 43 | + } | ||
| 44 | + } | ||
| 45 | + }, | ||
| 46 | + compiled () { | ||
| 47 | + if (this.column.type === 'index') { | ||
| 48 | + this.renderType = 'index'; | ||
| 49 | + } else if (this.column.render) { | ||
| 50 | + this.renderType = 'render'; | ||
| 51 | + } else { | ||
| 52 | + this.renderType = 'normal'; | ||
| 53 | + } | ||
| 54 | + }, | ||
| 55 | + ready () { | ||
| 56 | + this.compile(); | ||
| 57 | + }, | ||
| 58 | + beforeDestroy () { | ||
| 59 | + this.destroy(); | ||
| 60 | + }, | ||
| 61 | + watch: { | ||
| 62 | + index () { | ||
| 63 | + this.destroy(); | ||
| 64 | + this.compile(); | ||
| 65 | + } | ||
| 66 | + } | ||
| 67 | + } | ||
| 68 | +</script> | ||
| 0 | \ No newline at end of file | 69 | \ No newline at end of file |
src/components/table/table.vue
| @@ -26,12 +26,19 @@ | @@ -26,12 +26,19 @@ | ||
| 26 | @mouseleave.stop="handleMouseOut(index)" | 26 | @mouseleave.stop="handleMouseOut(index)" |
| 27 | @click.stop="highlightCurrentRow(index)"> | 27 | @click.stop="highlightCurrentRow(index)"> |
| 28 | <td v-for="column in cloneColumns" :class="alignCls(column)"> | 28 | <td v-for="column in cloneColumns" :class="alignCls(column)"> |
| 29 | - <div :class="[prefixCls + '-cell']"> | ||
| 30 | - <template v-if="column.type === 'selection'"> | ||
| 31 | - <Checkbox :checked="cloneData[index] && cloneData[index]._isChecked" @on-change="toggleSelect(index)"></Checkbox> | ||
| 32 | - </template> | ||
| 33 | - <template v-else>{{{ renderRow(row, column, index) }}}</template> | 29 | + <div :class="[prefixCls + '-cell']" v-if="column.type === 'selection'"> |
| 30 | + <Checkbox :checked="cloneData[index] && cloneData[index]._isChecked" @on-change="toggleSelect(index)"></Checkbox> | ||
| 34 | </div> | 31 | </div> |
| 32 | + <Cell v-else :prefix-cls="prefixCls" :row="row" :column="column" :index="index"></Cell> | ||
| 33 | + <!--<div :class="[prefixCls + '-cell']" v-else>--> | ||
| 34 | + <!--{{{ renderRow(row, column, index) }}}--> | ||
| 35 | + <!--</div>--> | ||
| 36 | + <!--<div :class="[prefixCls + '-cell']">--> | ||
| 37 | + <!--<template v-if="column.type === 'selection'">--> | ||
| 38 | + <!--<Checkbox :checked="cloneData[index] && cloneData[index]._isChecked" @on-change="toggleSelect(index)"></Checkbox>--> | ||
| 39 | + <!--</template>--> | ||
| 40 | + <!--<template v-else>{{{ renderRow(row, column, index) }}}</template>--> | ||
| 41 | + <!--</div>--> | ||
| 35 | </td> | 42 | </td> |
| 36 | </tr> | 43 | </tr> |
| 37 | </tbody> | 44 | </tbody> |
| @@ -49,13 +56,14 @@ | @@ -49,13 +56,14 @@ | ||
| 49 | <script> | 56 | <script> |
| 50 | import TableHead from './table-head.vue'; | 57 | import TableHead from './table-head.vue'; |
| 51 | import Checkbox from '../checkbox/checkbox.vue'; | 58 | import Checkbox from '../checkbox/checkbox.vue'; |
| 59 | + import Cell from './cell.vue'; | ||
| 52 | import Mixin from './mixin'; | 60 | import Mixin from './mixin'; |
| 53 | import { oneOf, getStyle, deepCopy } from '../../utils/assist'; | 61 | import { oneOf, getStyle, deepCopy } from '../../utils/assist'; |
| 54 | const prefixCls = 'ivu-table'; | 62 | const prefixCls = 'ivu-table'; |
| 55 | 63 | ||
| 56 | export default { | 64 | export default { |
| 57 | mixins: [ Mixin ], | 65 | mixins: [ Mixin ], |
| 58 | - components: { TableHead, Checkbox }, | 66 | + components: { TableHead, Checkbox, Cell }, |
| 59 | props: { | 67 | props: { |
| 60 | data: { | 68 | data: { |
| 61 | type: Array, | 69 | type: Array, |
| @@ -108,6 +116,9 @@ | @@ -108,6 +116,9 @@ | ||
| 108 | compiledUids: [], | 116 | compiledUids: [], |
| 109 | cloneData: deepCopy(this.data), | 117 | cloneData: deepCopy(this.data), |
| 110 | cloneColumns: deepCopy(this.columns), | 118 | cloneColumns: deepCopy(this.columns), |
| 119 | + leftFixedColumns: [], | ||
| 120 | + rightFixedColumns: [], | ||
| 121 | + centerColumns: [], | ||
| 111 | showSlotHeader: true, | 122 | showSlotHeader: true, |
| 112 | showSlotFooter: true, | 123 | showSlotFooter: true, |
| 113 | bodyHeight: 0 | 124 | bodyHeight: 0 |
| @@ -147,53 +158,25 @@ | @@ -147,53 +158,25 @@ | ||
| 147 | rowClsName (index) { | 158 | rowClsName (index) { |
| 148 | return this.rowClassName(this.data[index], index); | 159 | return this.rowClassName(this.data[index], index); |
| 149 | }, | 160 | }, |
| 150 | - renderRow (row, column, index) { | ||
| 151 | - return column.type === 'index' ? index + 1 : column.render ? '' : row[column.key]; | ||
| 152 | - }, | ||
| 153 | - compileRender (update = false) { | 161 | + handleResize () { |
| 154 | this.$nextTick(() => { | 162 | this.$nextTick(() => { |
| 155 | - if (update) { | ||
| 156 | - for (let i = 0; i < this.$parent.$children.length; i++) { | ||
| 157 | - const index = this.compiledUids.indexOf(this.$parent.$children[i]._uid); | ||
| 158 | - if (index > -1) { | ||
| 159 | - this.$parent.$children[i].$destroy(); | ||
| 160 | - this.compiledUids.splice(index, 1); | ||
| 161 | - i--; | ||
| 162 | - } | ||
| 163 | - } | 163 | + const allWidth = !this.columns.some(cell => !cell.width); |
| 164 | + if (allWidth) { | ||
| 165 | + this.tableWidth = this.columns.map(cell => cell.width).reduce((a, b) => a + b); | ||
| 166 | + } else { | ||
| 167 | + this.tableWidth = parseInt(getStyle(this.$el, 'width')) - 1; | ||
| 164 | } | 168 | } |
| 165 | - | ||
| 166 | - const $el = this.$els.render; | ||
| 167 | - for (let i = 0; i < this.cloneColumns.length; i++) { | ||
| 168 | - const column = this.cloneColumns[i]; | ||
| 169 | - if (column.render) { | ||
| 170 | - for (let j = 0; j < this.data.length; j++) { | ||
| 171 | - // todo 做一个缓存,只在需要改render时再重新编译,data改变时不用再编译 | ||
| 172 | - const row = this.data[j]; | ||
| 173 | - const template = column.render(row, column, j); | ||
| 174 | - const cell = document.createElement('div'); | ||
| 175 | - cell.innerHTML = template; | ||
| 176 | - const _oldParentChildLen = this.$parent.$children.length; | ||
| 177 | - this.$parent.$compile(cell); | ||
| 178 | - const _newParentChildLen = this.$parent.$children.length; | ||
| 179 | - | ||
| 180 | - if (_oldParentChildLen !== _newParentChildLen) { // if render normal html node, do not tag | ||
| 181 | - this.compiledUids.push(this.$parent.$children[this.$parent.$children.length - 1]._uid); // tag it, and delete when data or columns update | ||
| 182 | - } | ||
| 183 | - $el.children[j].children[i].children[0].innerHTML = ''; | ||
| 184 | - $el.children[j].children[i].children[0].appendChild(cell); | 169 | + this.$nextTick(() => { |
| 170 | + this.columnsWidth = []; | ||
| 171 | + let autoWidthIndex = -1 | ||
| 172 | + if (allWidth) autoWidthIndex = this.cloneColumns.findIndex(cell => !cell.width); | ||
| 173 | + this.$els.tbody.querySelectorAll('tbody tr')[0].querySelectorAll('td').forEach((cell, index) => { | ||
| 174 | + if (index === autoWidthIndex) { | ||
| 175 | + this.columnsWidth.push(parseInt(getStyle(cell, 'width')) - 1); | ||
| 176 | + } else { | ||
| 177 | + this.columnsWidth.push(parseInt(getStyle(cell, 'width'))); | ||
| 185 | } | 178 | } |
| 186 | - } | ||
| 187 | - } | ||
| 188 | - this.handleResize(); | ||
| 189 | - }); | ||
| 190 | - }, | ||
| 191 | - handleResize () { | ||
| 192 | - this.tableWidth = parseInt(getStyle(this.$el, 'width')); | ||
| 193 | - this.$nextTick(() => { | ||
| 194 | - this.columnsWidth = []; | ||
| 195 | - this.$els.tbody.querySelectorAll('tbody tr')[0].querySelectorAll('td').forEach((cell) => { | ||
| 196 | - this.columnsWidth.push(parseInt(getStyle(cell, 'width'))); | 179 | + }); |
| 197 | }); | 180 | }); |
| 198 | }); | 181 | }); |
| 199 | }, | 182 | }, |
| @@ -282,6 +265,9 @@ | @@ -282,6 +265,9 @@ | ||
| 282 | center.push(col); | 265 | center.push(col); |
| 283 | } | 266 | } |
| 284 | }); | 267 | }); |
| 268 | + this.leftFixedColumns = left; | ||
| 269 | + this.rightFixedColumns = right; | ||
| 270 | + this.centerColumns = center; | ||
| 285 | this.cloneColumns = left.concat(center).concat(right); | 271 | this.cloneColumns = left.concat(center).concat(right); |
| 286 | } | 272 | } |
| 287 | }, | 273 | }, |
| @@ -291,7 +277,7 @@ | @@ -291,7 +277,7 @@ | ||
| 291 | this.showSlotFooter = this.$els.footer.innerHTML.replace(/\n/g, '').replace(/<!--[\w\W\r\n]*?-->/gmi, '') !== ''; | 277 | this.showSlotFooter = this.$els.footer.innerHTML.replace(/\n/g, '').replace(/<!--[\w\W\r\n]*?-->/gmi, '') !== ''; |
| 292 | }, | 278 | }, |
| 293 | ready () { | 279 | ready () { |
| 294 | - this.compileRender(); | 280 | + this.handleResize(); |
| 295 | this.fixedHeader(); | 281 | this.fixedHeader(); |
| 296 | window.addEventListener('resize', this.handleResize, false); | 282 | window.addEventListener('resize', this.handleResize, false); |
| 297 | }, | 283 | }, |
| @@ -302,14 +288,14 @@ | @@ -302,14 +288,14 @@ | ||
| 302 | data: { | 288 | data: { |
| 303 | handler () { | 289 | handler () { |
| 304 | this.cloneData = deepCopy(this.data); | 290 | this.cloneData = deepCopy(this.data); |
| 305 | - this.compileRender(true); | 291 | + this.handleResize(); |
| 306 | }, | 292 | }, |
| 307 | deep: true | 293 | deep: true |
| 308 | }, | 294 | }, |
| 309 | columns: { | 295 | columns: { |
| 310 | handler () { | 296 | handler () { |
| 311 | this.parseColumns(); | 297 | this.parseColumns(); |
| 312 | - this.compileRender(true); | 298 | + this.handleResize(); |
| 313 | }, | 299 | }, |
| 314 | deep: true | 300 | deep: true |
| 315 | }, | 301 | }, |
src/styles/components/table.less
| @@ -9,10 +9,33 @@ | @@ -9,10 +9,33 @@ | ||
| 9 | background-color: #fff; | 9 | background-color: #fff; |
| 10 | border: 1px solid @border-color-base; | 10 | border: 1px solid @border-color-base; |
| 11 | border-bottom: 0; | 11 | border-bottom: 0; |
| 12 | + border-right: 0; | ||
| 12 | border-collapse: collapse; | 13 | border-collapse: collapse; |
| 13 | box-sizing: border-box; | 14 | box-sizing: border-box; |
| 14 | position: relative; | 15 | position: relative; |
| 15 | 16 | ||
| 17 | + &:before{ | ||
| 18 | + content: ''; | ||
| 19 | + width: 100%; | ||
| 20 | + height: 1px; | ||
| 21 | + position: absolute; | ||
| 22 | + left: 0; | ||
| 23 | + bottom: 0; | ||
| 24 | + background-color: @border-color-base; | ||
| 25 | + z-index: 1; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + &:after{ | ||
| 29 | + content: ''; | ||
| 30 | + width: 1px; | ||
| 31 | + height: 100%; | ||
| 32 | + position: absolute; | ||
| 33 | + top: 0; | ||
| 34 | + right: 0; | ||
| 35 | + background-color: @border-color-base; | ||
| 36 | + z-index: 1; | ||
| 37 | + } | ||
| 38 | + | ||
| 16 | &-with-header{ | 39 | &-with-header{ |
| 17 | border-radius: @border-radius-base @border-radius-base 0 0; | 40 | border-radius: @border-radius-base @border-radius-base 0 0; |
| 18 | } | 41 | } |
| @@ -36,8 +59,9 @@ | @@ -36,8 +59,9 @@ | ||
| 36 | } | 59 | } |
| 37 | 60 | ||
| 38 | &-body{ | 61 | &-body{ |
| 39 | - overflow-x: hidden; | ||
| 40 | - overflow-y: auto; | 62 | + //overflow-x: hidden; |
| 63 | + //overflow-y: auto; | ||
| 64 | + overflow: auto; | ||
| 41 | position: relative; | 65 | position: relative; |
| 42 | } | 66 | } |
| 43 | 67 | ||
| @@ -91,7 +115,8 @@ | @@ -91,7 +115,8 @@ | ||
| 91 | } | 115 | } |
| 92 | 116 | ||
| 93 | & table{ | 117 | & table{ |
| 94 | - width: 100%; | 118 | + //width: 100%; |
| 119 | + table-layout: fixed; | ||
| 95 | } | 120 | } |
| 96 | &-border{ | 121 | &-border{ |
| 97 | th,td{ | 122 | th,td{ |
test/routers/table.vue
| @@ -8,6 +8,7 @@ | @@ -8,6 +8,7 @@ | ||
| 8 | <!--<i-table size="large" border stripe :columns="columns" :data="data"></i-table>--> | 8 | <!--<i-table size="large" border stripe :columns="columns" :data="data"></i-table>--> |
| 9 | <br> | 9 | <br> |
| 10 | <i-table | 10 | <i-table |
| 11 | + style="width:450px" | ||
| 11 | border | 12 | border |
| 12 | highlight-row | 13 | highlight-row |
| 13 | :columns="columns" | 14 | :columns="columns" |
| @@ -57,7 +58,7 @@ | @@ -57,7 +58,7 @@ | ||
| 57 | title: '地址', | 58 | title: '地址', |
| 58 | key: 'address', | 59 | key: 'address', |
| 59 | align: 'center', | 60 | align: 'center', |
| 60 | -// width: 100 | 61 | + width: 100, |
| 61 | // render (row, column, index) { | 62 | // render (row, column, index) { |
| 62 | // if (row.edit) { | 63 | // if (row.edit) { |
| 63 | // return `<i-input :value.sync="data[${index}].name"></i-input>`; | 64 | // return `<i-input :value.sync="data[${index}].name"></i-input>`; |
| @@ -72,7 +73,8 @@ | @@ -72,7 +73,8 @@ | ||
| 72 | fixed: 'right', | 73 | fixed: 'right', |
| 73 | width: 200, | 74 | width: 200, |
| 74 | render (row, column, index) { | 75 | render (row, column, index) { |
| 75 | - return `<i-button @click="edit(${index})">编辑</i-button>` | 76 | + return `<i-button @click="edit(${index})">${row.name}</i-button>` |
| 77 | +// return `<a>${row.name}</a>` | ||
| 76 | } | 78 | } |
| 77 | } | 79 | } |
| 78 | ], | 80 | ], |
| @@ -140,14 +142,16 @@ | @@ -140,14 +142,16 @@ | ||
| 140 | }, | 142 | }, |
| 141 | ready () { | 143 | ready () { |
| 142 | setTimeout(() => { | 144 | setTimeout(() => { |
| 145 | + return; | ||
| 143 | // this.height = 150; | 146 | // this.height = 150; |
| 144 | - return | ||
| 145 | - this.data.push({ | ||
| 146 | - name: '刘天娇2', | ||
| 147 | - age: 272, | ||
| 148 | - address: '北京市东城区2', | ||
| 149 | - edit: false | ||
| 150 | - }); | 147 | +// return |
| 148 | +// this.data.push({ | ||
| 149 | +// name: '刘天娇2', | ||
| 150 | +// age: 272, | ||
| 151 | +// address: '北京市东城区2', | ||
| 152 | +// edit: false | ||
| 153 | +// }); | ||
| 154 | + this.data.splice(1, 1) | ||
| 151 | }, 1000); | 155 | }, 1000); |
| 152 | } | 156 | } |
| 153 | } | 157 | } |