Commit 98832d95e8a8b51d511ba28df8868fc7f5adae65
1 parent
4bdc1d1a
Exists in
master
and in
1 other branch
feat: 优化表格渲染逻辑
Showing
14 changed files
with
858 additions
and
177 deletions
Show diff stats
packages/index.js
| 1 | import Vue from 'vue'; | 1 | import Vue from 'vue'; |
| 2 | -import ZTable from './table/index'; | 2 | +import ZTable from './table/index.vue'; |
| 3 | import ZTableNormal from './table/normal'; | 3 | import ZTableNormal from './table/normal'; |
| 4 | import ZTableEditable from './table/editable'; | 4 | import ZTableEditable from './table/editable'; |
| 5 | import ElImageViewer from './upload/image-viewer'; | 5 | import ElImageViewer from './upload/image-viewer'; |
packages/schema-page/index.vue
| @@ -36,36 +36,35 @@ | @@ -36,36 +36,35 @@ | ||
| 36 | </slot> | 36 | </slot> |
| 37 | </div> | 37 | </div> |
| 38 | <!-- 表格内容 --> | 38 | <!-- 表格内容 --> |
| 39 | - <div v-if="schema.table || $scopedSlots.table" class="z-schema-page__table"> | 39 | + <div v-if="schema.table || $scopedSlots.table" class="z-schema-page__table" v-loading="schema.loading !== false ? tableLoading : false"> |
| 40 | <slot name="table" v-bind="_slotScope"> | 40 | <slot name="table" v-bind="_slotScope"> |
| 41 | - <z-schema-table | ||
| 42 | - :size="_size" | ||
| 43 | - :schema="tableSchemaDefaultProps(schema.table)" | ||
| 44 | - v-model="tableData" | ||
| 45 | - v-loading="schema.loading !== false ? tableLoading : false" | ||
| 46 | - @selection-change="onTableSelectionChange" | ||
| 47 | - > | ||
| 48 | - <template #left> | ||
| 49 | - <el-table-column v-if="schema.selection !== false" type="selection" width="40" align="center"></el-table-column> | 41 | + <z-schema-table :size="_size" :schema="tableSchemaDefaultProps(schema.table)" :data="tableData" @selection-change="onTableSelectionChange"> |
| 42 | + <template #prepend> | ||
| 43 | + <slot name="table-prepend"> | ||
| 44 | + <el-table-column v-if="schema.selection !== false" type="selection" width="40" align="center"></el-table-column> | ||
| 45 | + </slot> | ||
| 50 | </template> | 46 | </template> |
| 51 | <template v-for="item in getSlotKeys('table-')" #[item.name]="slotScope"> | 47 | <template v-for="item in getSlotKeys('table-')" #[item.name]="slotScope"> |
| 52 | <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot> | 48 | <slot :name="item.slot" v-bind="{ ..._slotScope, ...slotScope }"></slot> |
| 53 | </template> | 49 | </template> |
| 54 | - <slot v-if="schema.operation !== false" name="operation" v-bind="_slotScope"> | ||
| 55 | - <el-table-column v-bind="{ label: '操作', width: '90', align: 'center', ...(schema.operation || {}) }"> | ||
| 56 | - <template #default="{ row, column, $index }"> | ||
| 57 | - <div class="z-schema-page__table-operation"> | ||
| 58 | - <slot name="operation-left" v-bind="{ ..._slotScope, row, column, $index }"></slot> | ||
| 59 | - <slot name="operation-button" v-bind="{ ..._slotScope, row, column, $index }"></slot> | ||
| 60 | - <el-button type="text" icon="el-icon-edit" title="编辑" @click="openEdit(row)"></el-button> | ||
| 61 | - <el-popconfirm confirm-button-text="确定" cancel-button-text="取消" title="确定删除吗?" placement="top" @confirm="onDelete([row])"> | ||
| 62 | - <el-button slot="reference" type="text" icon="el-icon-delete" title="删除"></el-button> | ||
| 63 | - </el-popconfirm> | ||
| 64 | - <slot name="operation-right" v-bind="{ ..._slotScope, row, column, $index }"></slot> | ||
| 65 | - </div> | ||
| 66 | - </template> | ||
| 67 | - </el-table-column> | ||
| 68 | - </slot> | 50 | + <template #append> |
| 51 | + <slot name="table-append" /> | ||
| 52 | + <slot v-if="schema.operation !== false" name="operation" v-bind="_slotScope"> | ||
| 53 | + <el-table-column v-bind="{ label: '操作', width: '90', align: 'center', ...(schema.operation || {}) }"> | ||
| 54 | + <template #default="{ row, column, $index }"> | ||
| 55 | + <div class="z-schema-page__table-operation"> | ||
| 56 | + <slot name="operation-left" v-bind="{ ..._slotScope, row, column, $index }"></slot> | ||
| 57 | + <slot name="operation-button" v-bind="{ ..._slotScope, row, column, $index }"></slot> | ||
| 58 | + <el-button type="text" icon="el-icon-edit" title="编辑" @click="openEdit(row)"></el-button> | ||
| 59 | + <el-popconfirm confirm-button-text="确定" cancel-button-text="取消" title="确定删除吗?" placement="top" @confirm="onDelete([row])"> | ||
| 60 | + <el-button slot="reference" type="text" icon="el-icon-delete" title="删除"></el-button> | ||
| 61 | + </el-popconfirm> | ||
| 62 | + <slot name="operation-right" v-bind="{ ..._slotScope, row, column, $index }"></slot> | ||
| 63 | + </div> | ||
| 64 | + </template> | ||
| 65 | + </el-table-column> | ||
| 66 | + </slot> | ||
| 67 | + </template> | ||
| 69 | </z-schema-table> | 68 | </z-schema-table> |
| 70 | </slot> | 69 | </slot> |
| 71 | </div> | 70 | </div> |
packages/schema-table/index.vue
| 1 | <script> | 1 | <script> |
| 2 | +import { ref } from '../utils/vnode'; | ||
| 3 | + | ||
| 2 | export default { | 4 | export default { |
| 3 | name: 'SchemaTable', | 5 | name: 'SchemaTable', |
| 4 | - props: { | ||
| 5 | - value: { | ||
| 6 | - type: Array, | ||
| 7 | - default() { | ||
| 8 | - return []; | ||
| 9 | - }, | ||
| 10 | - }, | ||
| 11 | - schema: { | ||
| 12 | - required: true, | ||
| 13 | - type: Object, | ||
| 14 | - default() { | ||
| 15 | - return {}; | ||
| 16 | - }, | ||
| 17 | - }, | ||
| 18 | - size: String, | ||
| 19 | - }, | ||
| 20 | - data() { | ||
| 21 | - return { | ||
| 22 | - model: this.value, | ||
| 23 | - }; | ||
| 24 | - }, | ||
| 25 | - watch: { | ||
| 26 | - value(val = []) { | ||
| 27 | - this.model = val; | ||
| 28 | - }, | ||
| 29 | - model: { | ||
| 30 | - handler(val) { | ||
| 31 | - this.$emit('input', val); | ||
| 32 | - }, | ||
| 33 | - deep: true, | ||
| 34 | - }, | ||
| 35 | - }, | ||
| 36 | - render(h) { | ||
| 37 | - const schema = this.schema || {}; | ||
| 38 | - const _props = schema.props || {}; | ||
| 39 | - const _on = schema.on || this.$listeners || {}; | ||
| 40 | - return h('z-table', { props: { value: this.model, size: this.size, columns: schema.items, ..._props }, on: _on, scopedSlots: this.$scopedSlots }); | 6 | + functional: true, |
| 7 | + render(h, context) { | ||
| 8 | + const props = context.props || {}; | ||
| 9 | + // 当前函数式组件特有props | ||
| 10 | + const schema = props.schema; | ||
| 11 | + // 解析schema参数,设置到即将生成的组件上下文中 | ||
| 12 | + context.props.columns = props.schema.items; | ||
| 13 | + context.props = Object.assign(context.props, schema.props); | ||
| 14 | + context.listeners = Object.assign(context.listeners, schema.on); | ||
| 15 | + // 渲染组件时移除当前组件特有的props,避免透传不必要的参数 | ||
| 16 | + delete context.props.schema; | ||
| 17 | + return ref('z-table', context); | ||
| 41 | }, | 18 | }, |
| 42 | }; | 19 | }; |
| 43 | </script> | 20 | </script> |
| @@ -0,0 +1,217 @@ | @@ -0,0 +1,217 @@ | ||
| 1 | +<style lang="scss"> | ||
| 2 | +.z-table-column__cell-editable { | ||
| 3 | + display: inline-flex; | ||
| 4 | + align-items: center; | ||
| 5 | + justify-content: space-between; | ||
| 6 | + width: 100%; | ||
| 7 | + .el-icon-edit { | ||
| 8 | + color: rgba(151, 151, 151, 0.5); | ||
| 9 | + &:hover { | ||
| 10 | + color: $primary; | ||
| 11 | + } | ||
| 12 | + } | ||
| 13 | + .el-icon-check { | ||
| 14 | + color: $green; | ||
| 15 | + } | ||
| 16 | + .el-icon-close { | ||
| 17 | + color: $red; | ||
| 18 | + } | ||
| 19 | + .el-icon-edit, | ||
| 20 | + .el-icon-check, | ||
| 21 | + .el-icon-close { | ||
| 22 | + cursor: pointer; | ||
| 23 | + margin-left: 5px; | ||
| 24 | + font-size: 14px; | ||
| 25 | + } | ||
| 26 | +} | ||
| 27 | +</style> | ||
| 28 | + | ||
| 29 | +<template> | ||
| 30 | + <el-table :data="tableData | tableDataFilter" :size="_elSize" v-bind="bindProps" v-on="$listeners" @header-click="onHeaderClick" @cell-click="onCellClick" @cell-dblclick="onCellDblclick"> | ||
| 31 | + <slot name="left"></slot> | ||
| 32 | + <template v-for="(item, index) in columns"> | ||
| 33 | + <el-table-column v-bind="item" :key="index"> | ||
| 34 | + <slot :name="`header-${item.prop}`" slot="header"></slot> | ||
| 35 | + <template #default="{ row, column, $index }"> | ||
| 36 | + <cell-editor | ||
| 37 | + :disabled="item.editalways || editall || disabled || item.editable === false" | ||
| 38 | + :editable="item.editalways || editall || (item.editable !== false && row.$editor && row.$editor.includes(item.prop))" | ||
| 39 | + :component="item.component" | ||
| 40 | + :value="row[column.property]" | ||
| 41 | + @input="value => onCellInput(value, row, column, $index)" | ||
| 42 | + @edit-click="setRowEditor(row, column, $index)" | ||
| 43 | + @edit-confirm="value => onEditConfirm(value, row, column, $index)" | ||
| 44 | + > | ||
| 45 | + <template v-if="$scopedSlots[`editor-${item.prop}`]" slot="editor"> | ||
| 46 | + <slot :name="`editor-${item.prop}`" :value="row[column.property]" :row="row" :index="$index" :onInput="value => onCellInput(value, row, column, $index)"></slot> | ||
| 47 | + </template> | ||
| 48 | + <template v-if="$scopedSlots[`cell-${item.prop}`]"> | ||
| 49 | + <slot :name="`cell-${item.prop}`" :value="row[column.property]" :row="row" :index="$index"></slot> | ||
| 50 | + </template> | ||
| 51 | + <template v-else-if="item.render && typeof item.render === 'function'" #default="{ row, column, $index }"> | ||
| 52 | + <cell-render :item="item" :value="get(row, item.prop)" :row="row" :column="column" :index="$index"></cell-render> | ||
| 53 | + </template> | ||
| 54 | + </cell-editor> | ||
| 55 | + </template> | ||
| 56 | + </el-table-column> | ||
| 57 | + </template> | ||
| 58 | + <slot></slot> | ||
| 59 | + <slot name="append"></slot> | ||
| 60 | + </el-table> | ||
| 61 | +</template> | ||
| 62 | + | ||
| 63 | +<script> | ||
| 64 | +import TableNormal from './normal'; | ||
| 65 | +import tableProps from './props'; | ||
| 66 | +import { cloneDeep, get, set } from '../utils'; | ||
| 67 | + | ||
| 68 | +export default { | ||
| 69 | + name: 'TableEditable', | ||
| 70 | + extends: TableNormal, | ||
| 71 | + components: { | ||
| 72 | + cellEditor: { | ||
| 73 | + props: { | ||
| 74 | + value: [String, Number, Array, Object, Boolean], | ||
| 75 | + component: { type: String, default: 'el-input' }, | ||
| 76 | + editable: Boolean, | ||
| 77 | + disabled: Boolean, | ||
| 78 | + }, | ||
| 79 | + watch: { | ||
| 80 | + editable(val) { | ||
| 81 | + if (!this.disabled && val && this.component === 'el-input') { | ||
| 82 | + this.$nextTick(() => { | ||
| 83 | + this.$children[0] && this.$children[0].focus && this.$children[0].focus(); | ||
| 84 | + }); | ||
| 85 | + } | ||
| 86 | + }, | ||
| 87 | + }, | ||
| 88 | + render(h) { | ||
| 89 | + if (this.editable) { | ||
| 90 | + let editorRender = [ | ||
| 91 | + h(this.component, { | ||
| 92 | + props: { value: this.value, size: 'mini' }, | ||
| 93 | + on: { | ||
| 94 | + input: value => { | ||
| 95 | + this.$emit('input', value); | ||
| 96 | + }, | ||
| 97 | + }, | ||
| 98 | + }), | ||
| 99 | + ]; | ||
| 100 | + if (this.$scopedSlots.editor) { | ||
| 101 | + editorRender = [this.$scopedSlots.editor()]; | ||
| 102 | + } | ||
| 103 | + if (!this.disabled) { | ||
| 104 | + const handlerItems = [h('i', { attrs: { title: '确定', class: 'el-icon-check' }, on: { click: () => this.$emit('edit-confirm', this.value) } })]; | ||
| 105 | + // handlerItems.push(h('i', { attrs: { title: '取消', class: 'el-icon-close' }, on: { click: () => this.$emit('edit-confirm') } })); | ||
| 106 | + const handler = h('span', handlerItems); | ||
| 107 | + editorRender.push(handler); | ||
| 108 | + } | ||
| 109 | + return h('span', { class: 'z-table-column__cell-editable' }, editorRender); | ||
| 110 | + } | ||
| 111 | + let valueRender = [h('span', this.value)]; | ||
| 112 | + if (this.$scopedSlots.default) { | ||
| 113 | + valueRender = [this.$scopedSlots.default()]; | ||
| 114 | + } | ||
| 115 | + if (!this.disabled) { | ||
| 116 | + valueRender.push(h('i', { attrs: { title: '编辑', class: 'el-icon-edit' }, on: { click: () => this.$emit('edit-click') } })); | ||
| 117 | + } | ||
| 118 | + return h('span', { class: 'z-table-column__cell-editable' }, valueRender); | ||
| 119 | + }, | ||
| 120 | + }, | ||
| 121 | + }, | ||
| 122 | + props: { | ||
| 123 | + value: { | ||
| 124 | + type: Array, | ||
| 125 | + default() { | ||
| 126 | + return []; | ||
| 127 | + }, | ||
| 128 | + }, | ||
| 129 | + columns: { | ||
| 130 | + type: Array, | ||
| 131 | + default() { | ||
| 132 | + return []; | ||
| 133 | + }, | ||
| 134 | + }, | ||
| 135 | + editall: Boolean, | ||
| 136 | + clickable: Boolean, | ||
| 137 | + disabled: Boolean, | ||
| 138 | + ...tableProps, | ||
| 139 | + }, | ||
| 140 | + watch: { | ||
| 141 | + value(val) { | ||
| 142 | + this.tableData = val || []; | ||
| 143 | + }, | ||
| 144 | + data(val) { | ||
| 145 | + this.tableData = val || []; | ||
| 146 | + }, | ||
| 147 | + tableData(val) { | ||
| 148 | + this.$emit('input', val || []); | ||
| 149 | + }, | ||
| 150 | + }, | ||
| 151 | + data() { | ||
| 152 | + return { | ||
| 153 | + tableData: this.value, | ||
| 154 | + }; | ||
| 155 | + }, | ||
| 156 | + filters: { | ||
| 157 | + tableDataFilter(value) { | ||
| 158 | + return value.map((item, index) => ({ ...item, $index: index })); | ||
| 159 | + }, | ||
| 160 | + }, | ||
| 161 | + methods: { | ||
| 162 | + onHeaderClick() { | ||
| 163 | + if (this.clickable) { | ||
| 164 | + this.cancelEditCell(); | ||
| 165 | + } | ||
| 166 | + }, | ||
| 167 | + onCellClick(row, column) { | ||
| 168 | + if (this.clickable) { | ||
| 169 | + const prop = column.property; | ||
| 170 | + let tableData = cloneDeep(this.tableData); | ||
| 171 | + tableData.forEach((item, index) => { | ||
| 172 | + if (!(index === row.$index && item.$editor && item.$editor.includes(prop))) { | ||
| 173 | + item.$editor = []; | ||
| 174 | + } | ||
| 175 | + }); | ||
| 176 | + this.tableData = tableData; | ||
| 177 | + } | ||
| 178 | + }, | ||
| 179 | + onCellDblclick(row, column) { | ||
| 180 | + if (this.clickable) { | ||
| 181 | + this.setRowEditor(row, column, row.$index); | ||
| 182 | + } | ||
| 183 | + }, | ||
| 184 | + setRowEditor(row, column, index) { | ||
| 185 | + this.cancelEditCell(); | ||
| 186 | + let tableRow = this.tableData[index]; | ||
| 187 | + if (tableRow) { | ||
| 188 | + if (tableRow.$editor) { | ||
| 189 | + tableRow.$editor = [...tableRow.$editor, column.property]; | ||
| 190 | + } else { | ||
| 191 | + tableRow.$editor = [column.property]; | ||
| 192 | + } | ||
| 193 | + this.$set(this.tableData, index, tableRow); | ||
| 194 | + } | ||
| 195 | + }, | ||
| 196 | + onEditConfirm(value, row, column, index) { | ||
| 197 | + this.$emit('cell-edit-confirm', { row, index, prop: column.property, value }); | ||
| 198 | + this.cancelEditCell(); | ||
| 199 | + }, | ||
| 200 | + cancelEditCell() { | ||
| 201 | + this.tableData = this.tableData.map((item, index) => { | ||
| 202 | + const newItem = cloneDeep(item); | ||
| 203 | + delete newItem.$index; | ||
| 204 | + delete newItem.$editor; | ||
| 205 | + return newItem; | ||
| 206 | + }); | ||
| 207 | + }, | ||
| 208 | + onCellInput(value, row, column, index) { | ||
| 209 | + const tableData = cloneDeep(this.tableData); | ||
| 210 | + const tableRow = tableData[index]; | ||
| 211 | + set(tableRow, column.property, value); | ||
| 212 | + tableData[index] = tableRow; | ||
| 213 | + this.$set(this.tableData, index, tableRow); | ||
| 214 | + }, | ||
| 215 | + }, | ||
| 216 | +}; | ||
| 217 | +</script> |
| @@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
| 1 | +import tableProps from './props'; | ||
| 2 | + | ||
| 3 | +export default { | ||
| 4 | + name: 'Table', | ||
| 5 | + props: { | ||
| 6 | + value: { | ||
| 7 | + type: Array, | ||
| 8 | + default() { | ||
| 9 | + return []; | ||
| 10 | + }, | ||
| 11 | + }, | ||
| 12 | + columns: { | ||
| 13 | + type: Array, | ||
| 14 | + default() { | ||
| 15 | + return []; | ||
| 16 | + }, | ||
| 17 | + }, | ||
| 18 | + editable: Boolean, | ||
| 19 | + editall: Boolean, | ||
| 20 | + clickable: Boolean, | ||
| 21 | + disabled: Boolean, | ||
| 22 | + ...tableProps, | ||
| 23 | + }, | ||
| 24 | + render(h) { | ||
| 25 | + return h(`z-table-${this.editable ? 'editable' : 'normal'}`, { props: { ...this._props }, scopedSlots: this.$scopedSlots, on: this.$listeners }); | ||
| 26 | + }, | ||
| 27 | +}; |
| @@ -0,0 +1,95 @@ | @@ -0,0 +1,95 @@ | ||
| 1 | +<template> | ||
| 2 | + <el-table :data="tableData" :size="_elSize" v-bind="bindProps" v-on="$listeners"> | ||
| 3 | + <slot name="left"></slot> | ||
| 4 | + <template v-for="(item, index) in columns"> | ||
| 5 | + <el-table-column v-bind="item" :key="index"> | ||
| 6 | + <slot :name="`header-${item.prop}`" slot="header"></slot> | ||
| 7 | + <template v-if="$scopedSlots[`cell-${item.prop}`]" #default="{ row, column, $index }"> | ||
| 8 | + <slot :name="`cell-${item.prop}`" :value="get(row, item.prop)" :row="row" :column="column" :index="$index"></slot> | ||
| 9 | + </template> | ||
| 10 | + <template v-else-if="item.render && typeof item.render === 'function'" #default="{ row, column, $index }"> | ||
| 11 | + <cell-render :item="item" :value="get(row, item.prop)" :row="row" :column="column" :index="$index"></cell-render> | ||
| 12 | + </template> | ||
| 13 | + </el-table-column> | ||
| 14 | + </template> | ||
| 15 | + <slot></slot> | ||
| 16 | + <slot name="append"></slot> | ||
| 17 | + </el-table> | ||
| 18 | +</template> | ||
| 19 | + | ||
| 20 | +<script> | ||
| 21 | +import tableProps from './props'; | ||
| 22 | +import { get } from '../utils'; | ||
| 23 | + | ||
| 24 | +export default { | ||
| 25 | + name: 'TableNormal', | ||
| 26 | + components: { | ||
| 27 | + CellRender: { | ||
| 28 | + functional: true, | ||
| 29 | + render(h, context) { | ||
| 30 | + const props = context.props; | ||
| 31 | + const item = props.item || {}; | ||
| 32 | + const content = item.render(props.value, props.row, h, props.index); | ||
| 33 | + return typeof content === 'string' ? h('span', {}, [content]) : content; | ||
| 34 | + }, | ||
| 35 | + }, | ||
| 36 | + }, | ||
| 37 | + inject: { | ||
| 38 | + elForm: { | ||
| 39 | + default: '', | ||
| 40 | + }, | ||
| 41 | + elFormItem: { | ||
| 42 | + default: '', | ||
| 43 | + }, | ||
| 44 | + }, | ||
| 45 | + props: { | ||
| 46 | + value: { | ||
| 47 | + type: Array, | ||
| 48 | + default() { | ||
| 49 | + return []; | ||
| 50 | + }, | ||
| 51 | + }, | ||
| 52 | + columns: { | ||
| 53 | + type: Array, | ||
| 54 | + default() { | ||
| 55 | + return []; | ||
| 56 | + }, | ||
| 57 | + }, | ||
| 58 | + ...tableProps, | ||
| 59 | + }, | ||
| 60 | + data() { | ||
| 61 | + return { | ||
| 62 | + tableData: this.value.length > 0 ? this.value : this.data, | ||
| 63 | + }; | ||
| 64 | + }, | ||
| 65 | + watch: { | ||
| 66 | + value(val) { | ||
| 67 | + this.tableData = val || []; | ||
| 68 | + }, | ||
| 69 | + data(val) { | ||
| 70 | + this.tableData = val || []; | ||
| 71 | + }, | ||
| 72 | + }, | ||
| 73 | + computed: { | ||
| 74 | + _elFormItemSize() { | ||
| 75 | + return (this.elFormItem || {}).elFormItemSize; | ||
| 76 | + }, | ||
| 77 | + _elSize() { | ||
| 78 | + return this.size || this._elFormItemSize || (this.elForm || {}).size || (this.$ELEMENT || {}).size; | ||
| 79 | + }, | ||
| 80 | + bindProps() { | ||
| 81 | + const tablePropsKeys = Object.keys(tableProps); | ||
| 82 | + let props = {}; | ||
| 83 | + Object.keys(this._props).forEach(key => { | ||
| 84 | + if (tablePropsKeys.includes(key)) { | ||
| 85 | + props[key] = this._props[key]; | ||
| 86 | + } | ||
| 87 | + }); | ||
| 88 | + return props; | ||
| 89 | + }, | ||
| 90 | + }, | ||
| 91 | + methods: { | ||
| 92 | + get, | ||
| 93 | + }, | ||
| 94 | +}; | ||
| 95 | +</script> |
| @@ -0,0 +1,62 @@ | @@ -0,0 +1,62 @@ | ||
| 1 | +export default { | ||
| 2 | + data: { | ||
| 3 | + type: Array, | ||
| 4 | + default: function() { | ||
| 5 | + return []; | ||
| 6 | + }, | ||
| 7 | + }, | ||
| 8 | + size: String, | ||
| 9 | + width: [String, Number], | ||
| 10 | + height: [String, Number], | ||
| 11 | + maxHeight: [String, Number], | ||
| 12 | + fit: { | ||
| 13 | + type: Boolean, | ||
| 14 | + default: true, | ||
| 15 | + }, | ||
| 16 | + stripe: Boolean, | ||
| 17 | + border: Boolean, | ||
| 18 | + rowKey: [String, Function], | ||
| 19 | + context: {}, | ||
| 20 | + showHeader: { | ||
| 21 | + type: Boolean, | ||
| 22 | + default: true, | ||
| 23 | + }, | ||
| 24 | + showSummary: Boolean, | ||
| 25 | + sumText: String, | ||
| 26 | + summaryMethod: Function, | ||
| 27 | + rowClassName: [String, Function], | ||
| 28 | + rowStyle: [Object, Function], | ||
| 29 | + cellClassName: [String, Function], | ||
| 30 | + cellStyle: [Object, Function], | ||
| 31 | + headerRowClassName: [String, Function], | ||
| 32 | + headerRowStyle: [Object, Function], | ||
| 33 | + headerCellClassName: [String, Function], | ||
| 34 | + headerCellStyle: [Object, Function], | ||
| 35 | + highlightCurrentRow: Boolean, | ||
| 36 | + currentRowKey: [String, Number], | ||
| 37 | + emptyText: String, | ||
| 38 | + expandRowKeys: Array, | ||
| 39 | + defaultExpandAll: Boolean, | ||
| 40 | + defaultSort: Object, | ||
| 41 | + tooltipEffect: String, | ||
| 42 | + spanMethod: Function, | ||
| 43 | + selectOnIndeterminate: { | ||
| 44 | + type: Boolean, | ||
| 45 | + default: true, | ||
| 46 | + }, | ||
| 47 | + indent: { | ||
| 48 | + type: Number, | ||
| 49 | + default: 16, | ||
| 50 | + }, | ||
| 51 | + treeProps: { | ||
| 52 | + type: Object, | ||
| 53 | + default() { | ||
| 54 | + return { | ||
| 55 | + hasChildren: 'hasChildren', | ||
| 56 | + children: 'children', | ||
| 57 | + }; | ||
| 58 | + }, | ||
| 59 | + }, | ||
| 60 | + lazy: Boolean, | ||
| 61 | + load: Function, | ||
| 62 | +}; |
| @@ -0,0 +1,217 @@ | @@ -0,0 +1,217 @@ | ||
| 1 | +<style lang="scss"> | ||
| 2 | +.z-table-column__cell-editable { | ||
| 3 | + display: inline-flex; | ||
| 4 | + align-items: center; | ||
| 5 | + justify-content: space-between; | ||
| 6 | + width: 100%; | ||
| 7 | + .el-icon-edit { | ||
| 8 | + color: rgba(151, 151, 151, 0.5); | ||
| 9 | + &:hover { | ||
| 10 | + color: $primary; | ||
| 11 | + } | ||
| 12 | + } | ||
| 13 | + .el-icon-check { | ||
| 14 | + color: $green; | ||
| 15 | + } | ||
| 16 | + .el-icon-close { | ||
| 17 | + color: $red; | ||
| 18 | + } | ||
| 19 | + .el-icon-edit, | ||
| 20 | + .el-icon-check, | ||
| 21 | + .el-icon-close { | ||
| 22 | + cursor: pointer; | ||
| 23 | + margin-left: 5px; | ||
| 24 | + font-size: 14px; | ||
| 25 | + } | ||
| 26 | +} | ||
| 27 | +</style> | ||
| 28 | + | ||
| 29 | +<template> | ||
| 30 | + <el-table :data="tableData | tableDataFilter" :size="_elSize" v-bind="bindProps" v-on="$listeners" @header-click="onHeaderClick" @cell-click="onCellClick" @cell-dblclick="onCellDblclick"> | ||
| 31 | + <slot name="left"></slot> | ||
| 32 | + <template v-for="(item, index) in columns"> | ||
| 33 | + <el-table-column v-bind="item" :key="index"> | ||
| 34 | + <slot :name="`header-${item.prop}`" slot="header"></slot> | ||
| 35 | + <template #default="{ row, column, $index }"> | ||
| 36 | + <cell-editor | ||
| 37 | + :disabled="item.editalways || editall || disabled || item.editable === false" | ||
| 38 | + :editable="item.editalways || editall || (item.editable !== false && row.$editor && row.$editor.includes(item.prop))" | ||
| 39 | + :component="item.component" | ||
| 40 | + :value="row[column.property]" | ||
| 41 | + @input="value => onCellInput(value, row, column, $index)" | ||
| 42 | + @edit-click="setRowEditor(row, column, $index)" | ||
| 43 | + @edit-confirm="value => onEditConfirm(value, row, column, $index)" | ||
| 44 | + > | ||
| 45 | + <template v-if="$scopedSlots[`editor-${item.prop}`]" slot="editor"> | ||
| 46 | + <slot :name="`editor-${item.prop}`" :value="row[column.property]" :row="row" :index="$index" :onInput="value => onCellInput(value, row, column, $index)"></slot> | ||
| 47 | + </template> | ||
| 48 | + <template v-if="$scopedSlots[`cell-${item.prop}`]"> | ||
| 49 | + <slot :name="`cell-${item.prop}`" :value="row[column.property]" :row="row" :index="$index"></slot> | ||
| 50 | + </template> | ||
| 51 | + <template v-else-if="item.render && typeof item.render === 'function'" #default="{ row, column, $index }"> | ||
| 52 | + <cell-render :item="item" :value="get(row, item.prop)" :row="row" :column="column" :index="$index"></cell-render> | ||
| 53 | + </template> | ||
| 54 | + </cell-editor> | ||
| 55 | + </template> | ||
| 56 | + </el-table-column> | ||
| 57 | + </template> | ||
| 58 | + <slot></slot> | ||
| 59 | + <slot name="append"></slot> | ||
| 60 | + </el-table> | ||
| 61 | +</template> | ||
| 62 | + | ||
| 63 | +<script> | ||
| 64 | +import TableNormal from './normal'; | ||
| 65 | +import tableProps from './props'; | ||
| 66 | +import { cloneDeep, get, set } from '../utils'; | ||
| 67 | + | ||
| 68 | +export default { | ||
| 69 | + name: 'TableEditable', | ||
| 70 | + extends: TableNormal, | ||
| 71 | + components: { | ||
| 72 | + cellEditor: { | ||
| 73 | + props: { | ||
| 74 | + value: [String, Number, Array, Object, Boolean], | ||
| 75 | + component: { type: String, default: 'el-input' }, | ||
| 76 | + editable: Boolean, | ||
| 77 | + disabled: Boolean, | ||
| 78 | + }, | ||
| 79 | + watch: { | ||
| 80 | + editable(val) { | ||
| 81 | + if (!this.disabled && val && this.component === 'el-input') { | ||
| 82 | + this.$nextTick(() => { | ||
| 83 | + this.$children[0] && this.$children[0].focus && this.$children[0].focus(); | ||
| 84 | + }); | ||
| 85 | + } | ||
| 86 | + }, | ||
| 87 | + }, | ||
| 88 | + render(h) { | ||
| 89 | + if (this.editable) { | ||
| 90 | + let editorRender = [ | ||
| 91 | + h(this.component, { | ||
| 92 | + props: { value: this.value, size: 'mini' }, | ||
| 93 | + on: { | ||
| 94 | + input: value => { | ||
| 95 | + this.$emit('input', value); | ||
| 96 | + }, | ||
| 97 | + }, | ||
| 98 | + }), | ||
| 99 | + ]; | ||
| 100 | + if (this.$scopedSlots.editor) { | ||
| 101 | + editorRender = [this.$scopedSlots.editor()]; | ||
| 102 | + } | ||
| 103 | + if (!this.disabled) { | ||
| 104 | + const handlerItems = [h('i', { attrs: { title: '确定', class: 'el-icon-check' }, on: { click: () => this.$emit('edit-confirm', this.value) } })]; | ||
| 105 | + // handlerItems.push(h('i', { attrs: { title: '取消', class: 'el-icon-close' }, on: { click: () => this.$emit('edit-confirm') } })); | ||
| 106 | + const handler = h('span', handlerItems); | ||
| 107 | + editorRender.push(handler); | ||
| 108 | + } | ||
| 109 | + return h('span', { class: 'z-table-column__cell-editable' }, editorRender); | ||
| 110 | + } | ||
| 111 | + let valueRender = [h('span', this.value)]; | ||
| 112 | + if (this.$scopedSlots.default) { | ||
| 113 | + valueRender = [this.$scopedSlots.default()]; | ||
| 114 | + } | ||
| 115 | + if (!this.disabled) { | ||
| 116 | + valueRender.push(h('i', { attrs: { title: '编辑', class: 'el-icon-edit' }, on: { click: () => this.$emit('edit-click') } })); | ||
| 117 | + } | ||
| 118 | + return h('span', { class: 'z-table-column__cell-editable' }, valueRender); | ||
| 119 | + }, | ||
| 120 | + }, | ||
| 121 | + }, | ||
| 122 | + props: { | ||
| 123 | + value: { | ||
| 124 | + type: Array, | ||
| 125 | + default() { | ||
| 126 | + return []; | ||
| 127 | + }, | ||
| 128 | + }, | ||
| 129 | + columns: { | ||
| 130 | + type: Array, | ||
| 131 | + default() { | ||
| 132 | + return []; | ||
| 133 | + }, | ||
| 134 | + }, | ||
| 135 | + editall: Boolean, | ||
| 136 | + clickable: Boolean, | ||
| 137 | + disabled: Boolean, | ||
| 138 | + ...tableProps, | ||
| 139 | + }, | ||
| 140 | + watch: { | ||
| 141 | + value(val) { | ||
| 142 | + this.tableData = val || []; | ||
| 143 | + }, | ||
| 144 | + data(val) { | ||
| 145 | + this.tableData = val || []; | ||
| 146 | + }, | ||
| 147 | + tableData(val) { | ||
| 148 | + this.$emit('input', val || []); | ||
| 149 | + }, | ||
| 150 | + }, | ||
| 151 | + data() { | ||
| 152 | + return { | ||
| 153 | + tableData: this.value, | ||
| 154 | + }; | ||
| 155 | + }, | ||
| 156 | + filters: { | ||
| 157 | + tableDataFilter(value) { | ||
| 158 | + return value.map((item, index) => ({ ...item, $index: index })); | ||
| 159 | + }, | ||
| 160 | + }, | ||
| 161 | + methods: { | ||
| 162 | + onHeaderClick() { | ||
| 163 | + if (this.clickable) { | ||
| 164 | + this.cancelEditCell(); | ||
| 165 | + } | ||
| 166 | + }, | ||
| 167 | + onCellClick(row, column) { | ||
| 168 | + if (this.clickable) { | ||
| 169 | + const prop = column.property; | ||
| 170 | + let tableData = cloneDeep(this.tableData); | ||
| 171 | + tableData.forEach((item, index) => { | ||
| 172 | + if (!(index === row.$index && item.$editor && item.$editor.includes(prop))) { | ||
| 173 | + item.$editor = []; | ||
| 174 | + } | ||
| 175 | + }); | ||
| 176 | + this.tableData = tableData; | ||
| 177 | + } | ||
| 178 | + }, | ||
| 179 | + onCellDblclick(row, column) { | ||
| 180 | + if (this.clickable) { | ||
| 181 | + this.setRowEditor(row, column, row.$index); | ||
| 182 | + } | ||
| 183 | + }, | ||
| 184 | + setRowEditor(row, column, index) { | ||
| 185 | + this.cancelEditCell(); | ||
| 186 | + let tableRow = this.tableData[index]; | ||
| 187 | + if (tableRow) { | ||
| 188 | + if (tableRow.$editor) { | ||
| 189 | + tableRow.$editor = [...tableRow.$editor, column.property]; | ||
| 190 | + } else { | ||
| 191 | + tableRow.$editor = [column.property]; | ||
| 192 | + } | ||
| 193 | + this.$set(this.tableData, index, tableRow); | ||
| 194 | + } | ||
| 195 | + }, | ||
| 196 | + onEditConfirm(value, row, column, index) { | ||
| 197 | + this.$emit('cell-edit-confirm', { row, index, prop: column.property, value }); | ||
| 198 | + this.cancelEditCell(); | ||
| 199 | + }, | ||
| 200 | + cancelEditCell() { | ||
| 201 | + this.tableData = this.tableData.map((item, index) => { | ||
| 202 | + const newItem = cloneDeep(item); | ||
| 203 | + delete newItem.$index; | ||
| 204 | + delete newItem.$editor; | ||
| 205 | + return newItem; | ||
| 206 | + }); | ||
| 207 | + }, | ||
| 208 | + onCellInput(value, row, column, index) { | ||
| 209 | + const tableData = cloneDeep(this.tableData); | ||
| 210 | + const tableRow = tableData[index]; | ||
| 211 | + set(tableRow, column.property, value); | ||
| 212 | + tableData[index] = tableRow; | ||
| 213 | + this.$set(this.tableData, index, tableRow); | ||
| 214 | + }, | ||
| 215 | + }, | ||
| 216 | +}; | ||
| 217 | +</script> |
packages/table/index.js
| @@ -1,27 +0,0 @@ | @@ -1,27 +0,0 @@ | ||
| 1 | -import tableProps from './props'; | ||
| 2 | - | ||
| 3 | -export default { | ||
| 4 | - name: 'Table', | ||
| 5 | - props: { | ||
| 6 | - value: { | ||
| 7 | - type: Array, | ||
| 8 | - default() { | ||
| 9 | - return []; | ||
| 10 | - }, | ||
| 11 | - }, | ||
| 12 | - columns: { | ||
| 13 | - type: Array, | ||
| 14 | - default() { | ||
| 15 | - return []; | ||
| 16 | - }, | ||
| 17 | - }, | ||
| 18 | - editable: Boolean, | ||
| 19 | - editall: Boolean, | ||
| 20 | - clickable: Boolean, | ||
| 21 | - disabled: Boolean, | ||
| 22 | - ...tableProps, | ||
| 23 | - }, | ||
| 24 | - render(h) { | ||
| 25 | - return h(`z-table-${this.editable ? 'editable' : 'normal'}`, { props: { ...this._props }, scopedSlots: this.$scopedSlots, on: this.$listeners }); | ||
| 26 | - }, | ||
| 27 | -}; |
| @@ -0,0 +1,15 @@ | @@ -0,0 +1,15 @@ | ||
| 1 | +<script> | ||
| 2 | +import { ref } from '../utils/vnode'; | ||
| 3 | + | ||
| 4 | +export default { | ||
| 5 | + name: 'Table', | ||
| 6 | + functional: true, | ||
| 7 | + render(h, context) { | ||
| 8 | + const props = context.props || {}; | ||
| 9 | + if (props.editable) { | ||
| 10 | + return ref('z-table-editable', context); | ||
| 11 | + } | ||
| 12 | + return ref('z-table-normal', context); | ||
| 13 | + }, | ||
| 14 | +}; | ||
| 15 | +</script> |
| @@ -0,0 +1,95 @@ | @@ -0,0 +1,95 @@ | ||
| 1 | +<template> | ||
| 2 | + <el-table :data="tableData" :size="_elSize" v-bind="bindProps" v-on="$listeners"> | ||
| 3 | + <slot name="left"></slot> | ||
| 4 | + <template v-for="(item, index) in columns"> | ||
| 5 | + <el-table-column v-bind="item" :key="index"> | ||
| 6 | + <slot :name="`header-${item.prop}`" slot="header"></slot> | ||
| 7 | + <template v-if="$scopedSlots[`cell-${item.prop}`]" #default="{ row, column, $index }"> | ||
| 8 | + <slot :name="`cell-${item.prop}`" :value="get(row, item.prop)" :row="row" :column="column" :index="$index"></slot> | ||
| 9 | + </template> | ||
| 10 | + <template v-else-if="item.render && typeof item.render === 'function'" #default="{ row, column, $index }"> | ||
| 11 | + <cell-render :item="item" :value="get(row, item.prop)" :row="row" :column="column" :index="$index"></cell-render> | ||
| 12 | + </template> | ||
| 13 | + </el-table-column> | ||
| 14 | + </template> | ||
| 15 | + <slot></slot> | ||
| 16 | + <slot name="append"></slot> | ||
| 17 | + </el-table> | ||
| 18 | +</template> | ||
| 19 | + | ||
| 20 | +<script> | ||
| 21 | +import tableProps from './props'; | ||
| 22 | +import { get } from '../utils'; | ||
| 23 | + | ||
| 24 | +export default { | ||
| 25 | + name: 'TableNormal', | ||
| 26 | + components: { | ||
| 27 | + CellRender: { | ||
| 28 | + functional: true, | ||
| 29 | + render(h, context) { | ||
| 30 | + const props = context.props; | ||
| 31 | + const item = props.item || {}; | ||
| 32 | + const content = item.render(props.value, props.row, h, props.index); | ||
| 33 | + return typeof content === 'string' ? h('span', {}, [content]) : content; | ||
| 34 | + }, | ||
| 35 | + }, | ||
| 36 | + }, | ||
| 37 | + inject: { | ||
| 38 | + elForm: { | ||
| 39 | + default: '', | ||
| 40 | + }, | ||
| 41 | + elFormItem: { | ||
| 42 | + default: '', | ||
| 43 | + }, | ||
| 44 | + }, | ||
| 45 | + props: { | ||
| 46 | + value: { | ||
| 47 | + type: Array, | ||
| 48 | + default() { | ||
| 49 | + return []; | ||
| 50 | + }, | ||
| 51 | + }, | ||
| 52 | + columns: { | ||
| 53 | + type: Array, | ||
| 54 | + default() { | ||
| 55 | + return []; | ||
| 56 | + }, | ||
| 57 | + }, | ||
| 58 | + ...tableProps, | ||
| 59 | + }, | ||
| 60 | + data() { | ||
| 61 | + return { | ||
| 62 | + tableData: this.value.length > 0 ? this.value : this.data, | ||
| 63 | + }; | ||
| 64 | + }, | ||
| 65 | + watch: { | ||
| 66 | + value(val) { | ||
| 67 | + this.tableData = val || []; | ||
| 68 | + }, | ||
| 69 | + data(val) { | ||
| 70 | + this.tableData = val || []; | ||
| 71 | + }, | ||
| 72 | + }, | ||
| 73 | + computed: { | ||
| 74 | + _elFormItemSize() { | ||
| 75 | + return (this.elFormItem || {}).elFormItemSize; | ||
| 76 | + }, | ||
| 77 | + _elSize() { | ||
| 78 | + return this.size || this._elFormItemSize || (this.elForm || {}).size || (this.$ELEMENT || {}).size; | ||
| 79 | + }, | ||
| 80 | + bindProps() { | ||
| 81 | + const tablePropsKeys = Object.keys(tableProps); | ||
| 82 | + let props = {}; | ||
| 83 | + Object.keys(this._props).forEach(key => { | ||
| 84 | + if (tablePropsKeys.includes(key)) { | ||
| 85 | + props[key] = this._props[key]; | ||
| 86 | + } | ||
| 87 | + }); | ||
| 88 | + return props; | ||
| 89 | + }, | ||
| 90 | + }, | ||
| 91 | + methods: { | ||
| 92 | + get, | ||
| 93 | + }, | ||
| 94 | +}; | ||
| 95 | +</script> |
packages/table/normal.vue
| 1 | -<template> | ||
| 2 | - <el-table :data="tableData" :size="_elSize" v-bind="bindProps" v-on="$listeners"> | ||
| 3 | - <slot name="left"></slot> | ||
| 4 | - <template v-for="(item, index) in columns"> | ||
| 5 | - <el-table-column v-bind="item" :key="index"> | ||
| 6 | - <slot :name="`header-${item.prop}`" slot="header"></slot> | ||
| 7 | - <template v-if="$scopedSlots[`cell-${item.prop}`]" #default="{ row, column, $index }"> | ||
| 8 | - <slot :name="`cell-${item.prop}`" :value="get(row, item.prop)" :row="row" :column="column" :index="$index"></slot> | ||
| 9 | - </template> | ||
| 10 | - <template v-else-if="item.render && typeof item.render === 'function'" #default="{ row, column, $index }"> | ||
| 11 | - <cell-render :item="item" :value="get(row, item.prop)" :row="row" :column="column" :index="$index"></cell-render> | ||
| 12 | - </template> | ||
| 13 | - </el-table-column> | ||
| 14 | - </template> | ||
| 15 | - <slot></slot> | ||
| 16 | - <slot name="append"></slot> | ||
| 17 | - </el-table> | ||
| 18 | -</template> | ||
| 19 | - | ||
| 20 | <script> | 1 | <script> |
| 21 | -import tableProps from './props'; | ||
| 22 | import { get } from '../utils'; | 2 | import { get } from '../utils'; |
| 23 | 3 | ||
| 4 | +// 标题渲染 | ||
| 5 | +function headerRender(h, context, item) { | ||
| 6 | + const headerSlot = context.scopedSlots[`header-${item.prop}`]; | ||
| 7 | + return function(scope) { | ||
| 8 | + if (headerSlot) { | ||
| 9 | + return headerSlot(scope); | ||
| 10 | + } | ||
| 11 | + return item.label; | ||
| 12 | + }; | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +// 单元格渲染 | ||
| 16 | +function cellRender(h, context, item) { | ||
| 17 | + const cellSlot = context.scopedSlots[`cell-${item.prop}`]; | ||
| 18 | + return function(scope) { | ||
| 19 | + const value = get(scope.row, item.prop); | ||
| 20 | + // 自定义具名插槽 | ||
| 21 | + if (cellSlot) { | ||
| 22 | + return cellSlot({ | ||
| 23 | + item, | ||
| 24 | + value, | ||
| 25 | + index: scope.$index, | ||
| 26 | + ...scope, | ||
| 27 | + }); | ||
| 28 | + } | ||
| 29 | + // 自定义渲染函数 | ||
| 30 | + if (item.render) { | ||
| 31 | + return item.render(value, scope.row, h, scope.$index); | ||
| 32 | + } | ||
| 33 | + // 默认取值 | ||
| 34 | + return get(scope.row, item.prop); | ||
| 35 | + }; | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +// 跟进columns生成列 | ||
| 39 | +function createElTableColumns(h, context, columns) { | ||
| 40 | + return columns.map((item, index) => { | ||
| 41 | + const { attrs, on, ...props } = item; | ||
| 42 | + // 处理插槽 | ||
| 43 | + const scopedSlots = { | ||
| 44 | + header: headerRender(h, context, item), | ||
| 45 | + default: cellRender(h, context, item), | ||
| 46 | + }; | ||
| 47 | + return h('el-table-column', { key: index, attrs, props, on, scopedSlots }); | ||
| 48 | + }); | ||
| 49 | +} | ||
| 50 | + | ||
| 24 | export default { | 51 | export default { |
| 25 | name: 'TableNormal', | 52 | name: 'TableNormal', |
| 26 | - components: { | ||
| 27 | - CellRender: { | ||
| 28 | - functional: true, | ||
| 29 | - render(h, context) { | ||
| 30 | - const props = context.props; | ||
| 31 | - const item = props.item || {}; | ||
| 32 | - const content = item.render(props.value, props.row, h, props.index); | ||
| 33 | - return typeof content === 'string' ? h('span', {}, [content]) : content; | ||
| 34 | - }, | ||
| 35 | - }, | ||
| 36 | - }, | ||
| 37 | - inject: { | ||
| 38 | - elForm: { | ||
| 39 | - default: '', | ||
| 40 | - }, | ||
| 41 | - elFormItem: { | ||
| 42 | - default: '', | ||
| 43 | - }, | ||
| 44 | - }, | ||
| 45 | - props: { | ||
| 46 | - value: { | ||
| 47 | - type: Array, | ||
| 48 | - default() { | ||
| 49 | - return []; | ||
| 50 | - }, | ||
| 51 | - }, | ||
| 52 | - columns: { | ||
| 53 | - type: Array, | ||
| 54 | - default() { | ||
| 55 | - return []; | ||
| 56 | - }, | ||
| 57 | - }, | ||
| 58 | - ...tableProps, | ||
| 59 | - }, | ||
| 60 | - data() { | ||
| 61 | - return { | ||
| 62 | - tableData: this.value.length > 0 ? this.value : this.data, | ||
| 63 | - }; | ||
| 64 | - }, | ||
| 65 | - watch: { | ||
| 66 | - value(val) { | ||
| 67 | - this.tableData = val || []; | ||
| 68 | - }, | ||
| 69 | - data(val) { | ||
| 70 | - this.tableData = val || []; | ||
| 71 | - }, | ||
| 72 | - }, | ||
| 73 | - computed: { | ||
| 74 | - _elFormItemSize() { | ||
| 75 | - return (this.elFormItem || {}).elFormItemSize; | ||
| 76 | - }, | ||
| 77 | - _elSize() { | ||
| 78 | - return this.size || this._elFormItemSize || (this.elForm || {}).size || (this.$ELEMENT || {}).size; | ||
| 79 | - }, | ||
| 80 | - bindProps() { | ||
| 81 | - const tablePropsKeys = Object.keys(tableProps); | ||
| 82 | - let props = {}; | ||
| 83 | - Object.keys(this._props).forEach(key => { | ||
| 84 | - if (tablePropsKeys.includes(key)) { | ||
| 85 | - props[key] = this._props[key]; | ||
| 86 | - } | ||
| 87 | - }); | ||
| 88 | - return props; | ||
| 89 | - }, | ||
| 90 | - }, | ||
| 91 | - methods: { | ||
| 92 | - get, | 53 | + functional: true, |
| 54 | + render(h, context) { | ||
| 55 | + const props = context.props || {}; | ||
| 56 | + let scopedSlots = context.scopedSlots || {}; | ||
| 57 | + // 如有默认插槽则相当于直接写el-table | ||
| 58 | + if (scopedSlots.default) { | ||
| 59 | + return h('el-table', context); | ||
| 60 | + } | ||
| 61 | + const columns = props.columns || []; | ||
| 62 | + // 通过columns快速生成el-table-column | ||
| 63 | + const elTableColumns = createElTableColumns(h, context, columns); | ||
| 64 | + // 前置插槽 | ||
| 65 | + const prependSlot = scopedSlots.prepend ? scopedSlots.prepend() : ''; | ||
| 66 | + // 后置插槽 | ||
| 67 | + const appendSlot = scopedSlots.append ? scopedSlots.append() : ''; | ||
| 68 | + // 渲染组件时移除当前组件特有的props,避免透传不必要的参数 | ||
| 69 | + delete context.columns; | ||
| 70 | + return h('el-table', context, [prependSlot, ...elTableColumns, appendSlot]); | ||
| 93 | }, | 71 | }, |
| 94 | }; | 72 | }; |
| 95 | </script> | 73 | </script> |
packages/table/props.js
| @@ -5,6 +5,7 @@ export default { | @@ -5,6 +5,7 @@ export default { | ||
| 5 | return []; | 5 | return []; |
| 6 | }, | 6 | }, |
| 7 | }, | 7 | }, |
| 8 | + mode: 'normal', // normal | edit-column | edit-cell | edit-row | edit-all | ||
| 8 | size: String, | 9 | size: String, |
| 9 | width: [String, Number], | 10 | width: [String, Number], |
| 10 | height: [String, Number], | 11 | height: [String, Number], |
| @@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
| 1 | +// 注册函数式组件ref | ||
| 2 | +export function registerRef(vnode, context) { | ||
| 3 | + if (!context.data.ref) { | ||
| 4 | + return vnode; | ||
| 5 | + } | ||
| 6 | + // 备份vnode原有的insert周期函数 | ||
| 7 | + const hackInsert = vnode.data.hook.insert; | ||
| 8 | + // 新的vnode的insert周期函数 | ||
| 9 | + vnode.data.hook.insert = function(config) { | ||
| 10 | + hackInsert(config); | ||
| 11 | + // 当vnode生成实例后,通过上下文反写入父组件的refs; | ||
| 12 | + context.parent.$refs[context.data.ref] = config.componentInstance || config.elm; // ref本身就有组件实例和dom节点两种情况,优先取实例 | ||
| 13 | + }; | ||
| 14 | + return vnode; | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +// 简写注册ref | ||
| 18 | +export function ref(name, context) { | ||
| 19 | + return registerRef(context._c(name, context), context); | ||
| 20 | +} | ||
| 21 | + | ||
| 22 | +export default { | ||
| 23 | + registerRef, | ||
| 24 | + ref, | ||
| 25 | +}; |