Commit 2cb8a6d93e7705490f5045e737a292360505a0ce
1 parent
d2e82cd7
commit Table component
commit Table component
Showing
12 changed files
with
334 additions
and
0 deletions
Show diff stats
| 1 | +<template> | |
| 2 | + <thead> | |
| 3 | + <tr> | |
| 4 | + <th v-for="column in columns">{{{ renderHeader(column, $index) }}}</th> | |
| 5 | + </tr> | |
| 6 | + </thead> | |
| 7 | +</template> | |
| 8 | +<script> | |
| 9 | + export default { | |
| 10 | + props: { | |
| 11 | + prefixCls: String, | |
| 12 | + columns: Array | |
| 13 | + }, | |
| 14 | + data () { | |
| 15 | + return { | |
| 16 | + | |
| 17 | + } | |
| 18 | + }, | |
| 19 | + computed: { | |
| 20 | + | |
| 21 | + }, | |
| 22 | + methods: { | |
| 23 | + renderHeader (column, $index) { | |
| 24 | + if ('renderHeader' in this.columns[$index]) { | |
| 25 | + return this.columns[$index].renderHeader(column, $index); | |
| 26 | + } else { | |
| 27 | + return column.title || '#'; | |
| 28 | + } | |
| 29 | + } | |
| 30 | + } | |
| 31 | + } | |
| 32 | +</script> | |
| 0 | 33 | \ No newline at end of file | ... | ... |
| 1 | +<template> | |
| 2 | + <div :class="classes"> | |
| 3 | + <div :class="[prefixCls + '-body']"> | |
| 4 | + <table> | |
| 5 | + <colgroup> | |
| 6 | + <col v-for="column in columns" :width="column.width"> | |
| 7 | + </colgroup> | |
| 8 | + <thead | |
| 9 | + is="table-head" | |
| 10 | + :prefix-cls="prefixCls + '-thead'" | |
| 11 | + :columns="columns"></thead> | |
| 12 | + <tbody :class="[prefixCls + '-tbody']" v-el:render> | |
| 13 | + <tr :class="[prefixCls + '-row']" v-for="(index, row) in data"> | |
| 14 | + <td v-for="column in columns">{{{ renderRow(row, column) }}}</td> | |
| 15 | + </tr> | |
| 16 | + </tbody> | |
| 17 | + </table> | |
| 18 | + </div> | |
| 19 | + </div> | |
| 20 | +</template> | |
| 21 | +<script> | |
| 22 | + import TableHead from './table-head.vue'; | |
| 23 | + import { oneOf } from '../../utils/assist'; | |
| 24 | + const prefixCls = 'ivu-table'; | |
| 25 | + | |
| 26 | + export default { | |
| 27 | + components: { TableHead }, | |
| 28 | + props: { | |
| 29 | + data: { | |
| 30 | + type: Array, | |
| 31 | + default () { | |
| 32 | + return [] | |
| 33 | + } | |
| 34 | + }, | |
| 35 | + columns: { | |
| 36 | + type: Array, | |
| 37 | + default () { | |
| 38 | + return [] | |
| 39 | + } | |
| 40 | + }, | |
| 41 | + size: { | |
| 42 | + validator (value) { | |
| 43 | + return oneOf(value, ['small', 'large']); | |
| 44 | + } | |
| 45 | + }, | |
| 46 | + stripe: { | |
| 47 | + type: Boolean, | |
| 48 | + default: false | |
| 49 | + }, | |
| 50 | + border: { | |
| 51 | + type: Boolean, | |
| 52 | + default: false | |
| 53 | + }, | |
| 54 | + fit: { | |
| 55 | + type: Boolean, | |
| 56 | + default: true | |
| 57 | + }, | |
| 58 | + showHeader: { | |
| 59 | + type: Boolean, | |
| 60 | + default: true | |
| 61 | + }, | |
| 62 | + selection: { | |
| 63 | + validator (value) { | |
| 64 | + return oneOf(value, ['single', 'multiple', false]); | |
| 65 | + }, | |
| 66 | + default: false | |
| 67 | + }, | |
| 68 | + showIndex: { | |
| 69 | + type: Boolean, | |
| 70 | + default: false | |
| 71 | + } | |
| 72 | + }, | |
| 73 | + data () { | |
| 74 | + return { | |
| 75 | + prefixCls: prefixCls, | |
| 76 | + compiledUids: [] | |
| 77 | + } | |
| 78 | + }, | |
| 79 | + computed: { | |
| 80 | + classes () { | |
| 81 | + return [ | |
| 82 | + `${prefixCls}`, | |
| 83 | + { | |
| 84 | + [`${prefixCls}-${this.size}`]: !!this.size | |
| 85 | + } | |
| 86 | + ] | |
| 87 | + } | |
| 88 | + }, | |
| 89 | + methods: { | |
| 90 | + renderRow (row, column) { | |
| 91 | + return 'render' in column ? '' : row[column.key]; | |
| 92 | + }, | |
| 93 | + compileRender (update = false) { | |
| 94 | + this.$nextTick(() => { | |
| 95 | + if (update) { | |
| 96 | + for (let i = 0; i < this.$parent.$children.length; i++) { | |
| 97 | + const index = this.compiledUids.indexOf(this.$parent.$children[i]._uid); | |
| 98 | + if (index > -1) { | |
| 99 | + this.$parent.$children[i].$destroy(); | |
| 100 | + this.compiledUids.splice(index, 1); | |
| 101 | + i--; | |
| 102 | + } | |
| 103 | + } | |
| 104 | + } | |
| 105 | + | |
| 106 | + const $el = this.$els.render; | |
| 107 | + for (let i = 0; i < this.columns.length; i++) { | |
| 108 | + const column = this.columns[i]; | |
| 109 | + if (column.render) { | |
| 110 | + for (let j = 0; j < this.data.length; j++) { | |
| 111 | + // todo 做一个深度缓存,只在需要改render时再重新编译,否则data改变时不用再编译 | |
| 112 | + const row = this.data[j]; | |
| 113 | + const template = column.render(row, column, j); | |
| 114 | + const cell = document.createElement('div'); | |
| 115 | + cell.innerHTML = template; | |
| 116 | + const _oldParentChildLen = this.$parent.$children.length; | |
| 117 | + this.$parent.$compile(cell); | |
| 118 | + const _newParentChildLen = this.$parent.$children.length; | |
| 119 | + | |
| 120 | + if (_oldParentChildLen !== _newParentChildLen) { // if render normal html node, do not tag | |
| 121 | + this.compiledUids.push(this.$parent.$children[this.$parent.$children.length - 1]._uid); // tag it, and delete when data or columns update | |
| 122 | + } | |
| 123 | + $el.children[j].children[i].innerHTML = ''; | |
| 124 | + $el.children[j].children[i].appendChild(cell); | |
| 125 | + } | |
| 126 | + } | |
| 127 | + } | |
| 128 | + }); | |
| 129 | + } | |
| 130 | + }, | |
| 131 | + ready () { | |
| 132 | + this.compileRender(); | |
| 133 | + }, | |
| 134 | + watch: { | |
| 135 | + data: { | |
| 136 | + handler () { | |
| 137 | + this.compileRender(true); | |
| 138 | + }, | |
| 139 | + deep: true | |
| 140 | + }, | |
| 141 | + columns: { | |
| 142 | + handler () { | |
| 143 | + this.compileRender(true); | |
| 144 | + }, | |
| 145 | + deep: true | |
| 146 | + } | |
| 147 | + } | |
| 148 | + } | |
| 149 | +</script> | |
| 0 | 150 | \ No newline at end of file | ... | ... |
src/components/transfer/search.vue
src/index.js
| ... | ... | @@ -24,6 +24,7 @@ import Slider from './components/slider'; |
| 24 | 24 | import Spin from './components/spin'; |
| 25 | 25 | import Steps from './components/steps'; |
| 26 | 26 | import Switch from './components/switch'; |
| 27 | +import Table from './components/table'; | |
| 27 | 28 | import Tag from './components/tag'; |
| 28 | 29 | import Timeline from './components/timeline'; |
| 29 | 30 | import Tooltip from './components/tooltip'; |
| ... | ... | @@ -69,6 +70,7 @@ const iview = { |
| 69 | 70 | Step: Steps.Step, |
| 70 | 71 | Steps, |
| 71 | 72 | Switch, |
| 73 | + iTable: Table, | |
| 72 | 74 | Tag, |
| 73 | 75 | Timeline, |
| 74 | 76 | TimelineItem: Timeline.Item, | ... | ... |
src/utils/assist.js
| ... | ... | @@ -75,4 +75,16 @@ export function getStyle (element, styleName) { |
| 75 | 75 | } catch(e) { |
| 76 | 76 | return element.style[styleName]; |
| 77 | 77 | } |
| 78 | +} | |
| 79 | + | |
| 80 | +// firstUpperCase | |
| 81 | +function firstUpperCase(str) { | |
| 82 | + return str.toString()[0].toUpperCase() + str.toString().slice(1); | |
| 83 | +} | |
| 84 | + | |
| 85 | +// Warn | |
| 86 | +export function warnProp(component, prop, correctType, wrongType) { | |
| 87 | + correctType = firstUpperCase(correctType); | |
| 88 | + wrongType = firstUpperCase(wrongType); | |
| 89 | + console.error(`[iView warn]: Invalid prop: type check failed for prop ${prop}. Expected ${correctType}, got ${wrongType}. (found in component: ${component})`); | |
| 78 | 90 | } |
| 79 | 91 | \ No newline at end of file | ... | ... |
test/app.vue
test/main.js
| ... | ... | @@ -102,6 +102,11 @@ router.map({ |
| 102 | 102 | component: function (resolve) { |
| 103 | 103 | require(['./routers/transfer.vue'], resolve); |
| 104 | 104 | } |
| 105 | + }, | |
| 106 | + '/table': { | |
| 107 | + component: function (resolve) { | |
| 108 | + require(['./routers/table.vue'], resolve); | |
| 109 | + } | |
| 105 | 110 | } |
| 106 | 111 | }); |
| 107 | 112 | ... | ... |
| 1 | +<template> | |
| 2 | + <div> | |
| 3 | + <i-table :columns="columns" :data="data"></i-table> | |
| 4 | + </div> | |
| 5 | +</template> | |
| 6 | +<script> | |
| 7 | + export default { | |
| 8 | + props: { | |
| 9 | + | |
| 10 | + }, | |
| 11 | + data () { | |
| 12 | + return { | |
| 13 | + columns: [ | |
| 14 | + { | |
| 15 | + title: '姓名', | |
| 16 | + key: 'name' | |
| 17 | + }, | |
| 18 | + { | |
| 19 | + title: '年龄', | |
| 20 | + key: 'age', | |
| 21 | +// render (row) { | |
| 22 | +// return `<i-button>${row.age}</i-button>` | |
| 23 | +// } | |
| 24 | + }, | |
| 25 | + { | |
| 26 | + title: '地址', | |
| 27 | + key: 'address', | |
| 28 | +// render (row, column, index) { | |
| 29 | +// if (row.edit) { | |
| 30 | +// return `<i-input :value.sync="data[${index}].name"></i-input>`; | |
| 31 | +// } else { | |
| 32 | +// return `<Tooltip content="${row.address}"><i-button @click="show(${index})">${row.name}</i-button></Tooltip>`; | |
| 33 | +// } | |
| 34 | +// } | |
| 35 | + }, | |
| 36 | + { | |
| 37 | + title: '操作', | |
| 38 | + key: 'action', | |
| 39 | + render (row, column, index) { | |
| 40 | + return `<i-button @click="edit(${index})">编辑</i-button>` | |
| 41 | + } | |
| 42 | + } | |
| 43 | + ], | |
| 44 | + data: [ | |
| 45 | + { | |
| 46 | + name: '梁灏', | |
| 47 | + age: 25, | |
| 48 | + address: '北京市朝阳区', | |
| 49 | + edit: false | |
| 50 | + }, | |
| 51 | + { | |
| 52 | + name: '段模', | |
| 53 | + age: 26, | |
| 54 | + address: '北京市海淀区', | |
| 55 | + edit: false | |
| 56 | + }, | |
| 57 | + { | |
| 58 | + name: '刘天娇', | |
| 59 | + age: 27, | |
| 60 | + address: '北京市东城区', | |
| 61 | + edit: true | |
| 62 | + } | |
| 63 | + ] | |
| 64 | + } | |
| 65 | + }, | |
| 66 | + computed: { | |
| 67 | + | |
| 68 | + }, | |
| 69 | + methods: { | |
| 70 | + show (name) { | |
| 71 | + this.$Message.info(name); | |
| 72 | + }, | |
| 73 | + edit (index) { | |
| 74 | + this.data[index].edit = true; | |
| 75 | + } | |
| 76 | + }, | |
| 77 | + ready () { | |
| 78 | + setTimeout(() => { | |
| 79 | + this.data.push({ | |
| 80 | + name: '刘天娇2', | |
| 81 | + age: 272, | |
| 82 | + address: '北京市东城区2', | |
| 83 | + edit: false | |
| 84 | + }); | |
| 85 | + }, 1000); | |
| 86 | + } | |
| 87 | + } | |
| 88 | +</script> | |
| 0 | 89 | \ No newline at end of file | ... | ... |