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 | \ No newline at end of file | 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 | \ No newline at end of file | 150 | \ No newline at end of file |
src/components/transfer/search.vue
@@ -12,6 +12,7 @@ | @@ -12,6 +12,7 @@ | ||
12 | import iInput from '../input/input.vue'; | 12 | import iInput from '../input/input.vue'; |
13 | 13 | ||
14 | export default { | 14 | export default { |
15 | + components: { iInput }, | ||
15 | props: { | 16 | props: { |
16 | prefixCls: String, | 17 | prefixCls: String, |
17 | placeholder: String, | 18 | placeholder: String, |
src/index.js
@@ -24,6 +24,7 @@ import Slider from './components/slider'; | @@ -24,6 +24,7 @@ import Slider from './components/slider'; | ||
24 | import Spin from './components/spin'; | 24 | import Spin from './components/spin'; |
25 | import Steps from './components/steps'; | 25 | import Steps from './components/steps'; |
26 | import Switch from './components/switch'; | 26 | import Switch from './components/switch'; |
27 | +import Table from './components/table'; | ||
27 | import Tag from './components/tag'; | 28 | import Tag from './components/tag'; |
28 | import Timeline from './components/timeline'; | 29 | import Timeline from './components/timeline'; |
29 | import Tooltip from './components/tooltip'; | 30 | import Tooltip from './components/tooltip'; |
@@ -69,6 +70,7 @@ const iview = { | @@ -69,6 +70,7 @@ const iview = { | ||
69 | Step: Steps.Step, | 70 | Step: Steps.Step, |
70 | Steps, | 71 | Steps, |
71 | Switch, | 72 | Switch, |
73 | + iTable: Table, | ||
72 | Tag, | 74 | Tag, |
73 | Timeline, | 75 | Timeline, |
74 | TimelineItem: Timeline.Item, | 76 | TimelineItem: Timeline.Item, |
src/utils/assist.js
@@ -75,4 +75,16 @@ export function getStyle (element, styleName) { | @@ -75,4 +75,16 @@ export function getStyle (element, styleName) { | ||
75 | } catch(e) { | 75 | } catch(e) { |
76 | return element.style[styleName]; | 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 | \ No newline at end of file | 91 | \ No newline at end of file |
test/app.vue
@@ -41,6 +41,7 @@ li + li { | @@ -41,6 +41,7 @@ li + li { | ||
41 | <li><a v-link="'/input'">Input</a></li> | 41 | <li><a v-link="'/input'">Input</a></li> |
42 | <li><a v-link="'/cascader'">Cascader</a></li> | 42 | <li><a v-link="'/cascader'">Cascader</a></li> |
43 | <li><a v-link="'/transfer'">Transfer</a></li> | 43 | <li><a v-link="'/transfer'">Transfer</a></li> |
44 | + <li><a v-link="'/table'">Table</a></li> | ||
44 | </ul> | 45 | </ul> |
45 | </nav> | 46 | </nav> |
46 | <router-view></router-view> | 47 | <router-view></router-view> |
test/main.js
@@ -102,6 +102,11 @@ router.map({ | @@ -102,6 +102,11 @@ router.map({ | ||
102 | component: function (resolve) { | 102 | component: function (resolve) { |
103 | require(['./routers/transfer.vue'], resolve); | 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 | \ No newline at end of file | 89 | \ No newline at end of file |