ElementUI 实现表格可编辑 Editable,增删改查编辑表格Grid
栏目: JavaScript · 发布时间: 5年前
内容简介:查看实现思路:使用 solt 处理编辑和显示切换已经自定义组件渲染,100%兼容 ElTable 所有参数。Vue + ElementUI 扩展的可编辑表格组件,完全支持任意组件渲染。
查看 Github
实现思路:使用 solt 处理编辑和显示切换已经自定义组件渲染,100%兼容 ElTable 所有参数。
Vue + ElementUI 扩展的可编辑表格组件,完全支持任意组件渲染。
-
实现功能:
- 支持单列编辑
- 支持整行编辑
- 支持单击、双击编辑模式
- 支持渲染任意组件
- 支持动态列
- 支持显示编辑状态
- 支持增删改查数据获取
- 支持还原更改数据
- 支持 ElTable 所有功能
API
Editable Attributes
<el-editable ref="editable" edit-config="{trigger: 'click', mode: 'cell'}"> <el-editable-column prop="name" label="名字" edit-render="{name: 'ElInput'}"></el-editable-column> <el-editable-column prop="age" label="年龄" edit-render="{name: 'ElInput'}"></el-editable-column> </el-editable>
属性 | 描述 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
trigger | 触发方式 | String | manual(手动方式) / click(点击触发编辑) / dblclick(双击触发编辑) | click |
mode | 编辑方式 | String | cell(列编辑模式) / row(行编辑模式) | cell |
showIcon | 是否显示列头编辑图标 | Boolean | — | true |
showStatus | 是否显示列的编辑状态 | Boolean | — | true |
Editable Methods
方法名 | 描述 | 参数 |
---|---|---|
reload(datas) | 初始化加载数据 | datas |
revert() | 还原修改之前数据 | |
insert(record) | 新增一行新数据 | record |
insertAt(record, rowIndex) | 指定位置新增一行新数据,如果是-1则从底部新增新数据 | record, rowIndex |
remove(record) | 根据数据删除一行数据 | record |
removes(records) | 根据数据删除多行数据 | records |
removeRow(rowIndex) | 根据行号删除一行数据 | rowIndex |
removeRows(rowIndexs) | 根据行号删除多行数据 | rowIndexs |
removeSelecteds() | 删除选中行数据 | |
clear() | 清空所有数据 | |
clearActive() | 清除所有活动行列为不可编辑状态 | |
setActiveRow(rowIndex) | 设置活动行为可编辑状态(只对mode='row'有效) | rowIndex |
updateStatus(scope) | 更新列状态(当使用自定义组件时,值发生改变时需要调用来更新列状态),如果不传参数则更新整个表格 | scope |
getRecords() | 获取表格数据集 | |
getAllRecords() | 获取表格所有数据 | |
getInsertRecords() | 获取新增数据 | |
getRemoveRecords() | 获取已删除数据 | |
getUpdateRecords() | 获取已修改数据 |
Editable-Column Attributes
<el-editable-column prop="name" label="名字" edit-render="{name: 'ElInput', type: 'default'}"></el-editable-column>
属性 | 描述 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
name | 渲染的组件名称 | String | ElInput / ElSelect / ElCascader / ElDatePicker / ElInputNumber / ElSwitch | ElInput |
type | 渲染类型 | String | default(组件激活后才可视) / visible(组件一直可视) | default |
attrs | 渲染组件附加属性 | Object | — | {} |
optionAttrs | 下拉组件选项附加属性(只对name='ElSelect'有效) | Object | — | {} |
options | 下拉组件选项列表(只对name='ElSelect'有效) | Array | — | [] |
Editable-Column Scoped Slot
name | 说明 |
---|---|
— | 自定义渲染显示内容,参数为 { row, column, $index } |
type | 自定义渲染组件,参数为 { row, column, $index } |
head | 自定义表头的内容,参数为 { column, $index } |
Editable.vue
<template> <el-table ref="refElTable" :class="['editable', {'editable--icon': showIcon}]" :data="datas" :height="height" :maxHeight="maxHeight" :stripe="stripe" :border="border" :size="size" :fit="fit" :showHeader="showHeader" :highlightCurrentRow="highlightCurrentRow" :currentRowKey="currentRowKey" :rowClassName="rowClassName" :rowStyle="rowStyle" :headerRowClassName="headerRowClassName" :headerRowStyle="headerRowStyle" :headerCellClassName="headerCellClassName" :headerCellStyle="headerCellStyle" :rowKey="rowKey" :emptyText="emptyText" :defaultExpandAll="defaultExpandAll" :expandRowKeys="expandRowKeys" :defaultSort="defaultSort" :tooltipEffect="tooltipEffect" :showSummary="showSummary" :sumText="sumText" :summaryMethod="_summaryMethod" :selectOnIndeterminate="selectOnIndeterminate" :spanMethod="_spanMethod" @select="_select" @select-all="_selectAll" @selection-change="_selectionChange" @cell-mouse-enter="_cellMouseEnter" @cell-mouse-leave="_cellMouseLeave" @cell-click="_cellClick" @cell-dblclick="_cellDBLclick" @row-click="_rowClick" @row-contextmenu="_rowContextmenu" @row-dblclick="_rowDBLclick" @header-click="_headerClick" @header-contextmenu="_headerContextmenu" @sort-change="_sortChange" @filter-change="_filterChange" @current-change="_currentChange" @header-dragend="_headerDragend" @expand-change="_expandChange"> <slot></slot> </el-table> </template> <script> import XEUtils from 'xe-utils' import { mapGetters } from 'vuex' export default { name: 'ElEditable', props: { editConfig: Object, data: Array, height: [String, Number], maxHeight: [String, Number], stripe: Boolean, border: Boolean, size: { type: String, default: 'small' }, fit: { type: Boolean, default: true }, showHeader: { type: Boolean, default: true }, highlightCurrentRow: Boolean, currentRowKey: [String, Number], rowClassName: [Function, String], rowStyle: [Function, Object], cellClassName: [Function, String], cellStyle: [Function, Object], headerRowClassName: [Function, String], headerRowStyle: [Function, Object], headerCellClassName: [Function, String], headerCellStyle: [Function, Object], rowKey: [Function, String], emptyText: String, defaultExpandAll: Boolean, expandRowKeys: Array, defaultSort: Object, tooltipEffect: { type: String, default: 'dark' }, showSummary: Boolean, sumText: { type: String, default: '合计' }, summaryMethod: Function, selectOnIndeterminate: { type: Boolean, default: true }, spanMethod: Function }, data () { return { editProto: {}, datas: [], initialStore: [], deleteRecords: [], lastActive: null } }, computed: { ...mapGetters([ 'globalClick' ]), showIcon () { return this.editConfig ? !(this.editConfig.showIcon === false) : true }, showStatus () { return this.editConfig ? !(this.editConfig.showStatus === false) : true }, mode () { return this.editConfig ? (this.editConfig.mode || 'cell') : 'cell' } }, watch: { globalClick (evnt) { if (this.lastActive) { let target = evnt.target let { cell } = this.lastActive while (target && target.nodeType && target !== document) { if (this.mode === 'row' ? target === cell.parentNode : target === cell) { return } target = target.parentNode } this.clearActive() } }, datas () { this.updateStatus() } }, created () { this._initial(this.data, true) }, methods: { _initial (datas, isReload) { if (isReload) { this.initialStore = XEUtils.clone(datas, true) } this.datas = (datas || []).map(item => this._toData(item)) }, _toData (item, status) { return item.editable && item._EDITABLE_PROTO === this.editProto ? item : { _EDITABLE_PROTO: this.editProto, data: item, store: XEUtils.clone(item, true), editStatus: status || 'initial', editActive: null, editable: { size: this.size, showIcon: this.showIcon, showStatus: this.showStatus, mode: this.mode } } }, _updateData () { this.$emit('update:data', this.datas.map(item => item.data)) }, _select (selection, row) { this.$emit('select', selection.map(item => item.data), row.data) }, _selectAll (selection) { this.$emit('select-all', selection.map(item => item.data)) }, _selectionChange (selection) { this.$emit('selection-change', selection.map(item => item.data)) }, _cellMouseEnter (row, column, cell, event) { this.$emit('cell-mouse-enter', row.data, column, cell, event) }, _cellMouseLeave (row, column, cell, event) { this.$emit('cell-mouse-leave', row.data, column, cell, event) }, _cellClick (row, column, cell, event) { if (!this.editConfig || this.editConfig.trigger === 'click') { this._triggerActive(row, column, cell) } this.$emit('cell-click', row.data, column, cell, event) }, _cellDBLclick (row, column, cell, event) { if (this.editConfig && this.editConfig.trigger === 'dblclick') { this._triggerActive(row, column, cell) } this.$emit('cell-dblclick', row.data, column, cell, event) }, _rowClick (row, event, column) { this.$emit('row-click', row.data, event, column) }, _rowContextmenu (row, event) { this.$emit('row-contextmenu', row.data, event) }, _rowDBLclick (row, event) { this.$emit('row-dblclick', row.data, event) }, _headerClick (column, event) { this.$emit('header-click', column, event) }, _headerContextmenu (column, event) { this.$emit('header-contextmenu', column, event) }, _sortChange ({ column, prop, order }) { this.$emit('sort-change', { column, prop, order }) }, _filterChange (filters) { this.$emit('filter-change', filters) }, _currentChange (currentRow, oldCurrentRow) { if (currentRow && oldCurrentRow) { this.$emit('current-change', currentRow.data, oldCurrentRow.data) } else if (currentRow) { this.$emit('current-change', currentRow.data) } else if (oldCurrentRow) { this.$emit('current-change', oldCurrentRow.data) } }, _headerDragend (newWidth, oldWidth, column, event) { this.$emit('header-dragend', newWidth, oldWidth, column, event) }, _expandChange (row, expandedRows) { this.$emit('expand-change', row.data, expandedRows) }, _triggerActive (row, column, cell) { this.clearActive() this.lastActive = { row, column, cell } cell.className += ` editable-col_active` row.editActive = column.property this.$nextTick(() => { let inpElem = cell.querySelector('.el-input>input') if (!inpElem) { inpElem = cell.querySelector('.el-textarea>textarea') if (!inpElem) { inpElem = cell.querySelector('.editable-custom_input') } } if (inpElem) { inpElem.focus() } }) }, _updateColumnStatus (trElem, column, tdElem) { if (column.className.split(' ').includes('editable-col_edit')) { let classList = tdElem.className.split(' ') if (!classList.includes('editable-col_dirty')) { classList.push('editable-col_dirty') tdElem.className = classList.join(' ') } } }, _summaryMethod (param) { let { columns } = param let data = param.data.map(item => item.data) let sums = [] if (this.summaryMethod) { sums = this.summaryMethod({ columns, data }) } else { columns.forEach((column, index) => { if (index === 0) { sums[index] = this.sumText return } let values = data.map(item => Number(item[column.property])) let precisions = [] let notNumber = true values.forEach(value => { if (!isNaN(value)) { notNumber = false let decimal = ('' + value).split('.')[1] precisions.push(decimal ? decimal.length : 0) } }) let precision = Math.max.apply(null, precisions) if (!notNumber) { sums[index] = values.reduce((prev, curr) => { let value = Number(curr) if (!isNaN(value)) { return parseFloat((prev + curr).toFixed(Math.min(precision, 20))) } else { return prev } }, 0) } else { sums[index] = '' } }) } return sums }, _spanMethod ({ row, column, rowIndex, columnIndex }) { let rowspan = 1 let colspan = 1 let fn = this.spanMethod if (XEUtils.isFunction(fn)) { var result = fn({ row: row.data, column: column, rowIndex: rowIndex, columnIndex: columnIndex }) if (XEUtils.isArray(result)) { rowspan = result[0] colspan = result[1] } else if (XEUtils.isPlainObject(result)) { rowspan = result.rowspan colspan = result.colspan } } return { rowspan: rowspan, colspan: colspan } }, clearActive () { this.lastActive = null this.datas.forEach(item => { item.editActive = null }) Array.from(this.$el.querySelectorAll('.editable-col_active.editable-column')).forEach(elem => { elem.className = elem.className.split(' ').filter(name => name !== 'editable-col_active').join(' ') }) }, setActiveRow (rowIndex) { setTimeout(() => { let row = this.datas[rowIndex] if (row && this.mode === 'row') { let column = this.$refs.refElTable.columns.find(column => column.property) let trElemList = this.$el.querySelectorAll('.el-table__body-wrapper .el-table__row') let cell = trElemList[rowIndex].children[0] this._triggerActive(row, column, cell) } }, 5) }, reload (datas) { this.deleteRecords = [] this.clearActive() this._initial(datas, true) this._updateData() }, revert () { this.reload(this.initialStore) }, clear () { this.deleteRecords = [] this.clearActive() this._initial([]) this._updateData() }, insert (record) { this.insertAt(record, 0) }, insertAt (record, rowIndex) { let recordItem = {} let len = this.datas.length this.$refs.refElTable.columns.forEach(column => { if (column.property) { recordItem[column.property] = null } }) recordItem = this._toData(Object.assign(recordItem, record), 'insert') if (rowIndex) { if (rowIndex === -1 || rowIndex >= len) { rowIndex = len this.datas.push(recordItem) } else { this.datas.splice(rowIndex, 0, recordItem) } } else { rowIndex = 0 this.datas.unshift(recordItem) } this._updateData() }, removeRow (rowIndex) { let items = this.datas.splice(rowIndex, 1) items.forEach(item => { if (item.editStatus === 'initial') { this.deleteRecords.push(item) } }) this._updateData() }, removeRows (rowIndexs) { XEUtils.lastEach(this.datas, (item, index) => { if (rowIndexs.includes(index)) { this.removeRow(index) } }) }, remove (record) { this.removeRow(XEUtils.findIndexOf(this.datas, item => item.data === record)) }, removes (records) { XEUtils.lastEach(this.datas, (item, index) => { if (records.includes(item.data)) { this.removeRow(index) } }) }, removeSelecteds () { this.removes(this.$refs.refElTable.selection.map(item => item.data)) }, getRecords (datas) { return (datas || this.datas).map(item => item.data) }, getAllRecords () { return { records: this.getRecords(), insertRecords: this.getInsertRecords(), removeRecords: this.getRemoveRecords(), updateRecords: this.getUpdateRecords() } }, getInsertRecords () { return this.getRecords(this.datas.filter(item => item.editStatus === 'insert')) }, getRemoveRecords () { return this.getRecords(this.deleteRecords) }, getUpdateRecords () { return this.getRecords(this.datas.filter(item => item.editStatus === 'initial' && !XEUtils.isEqual(item.data, item.store))) }, updateStatus (scope) { if (this.showStatus) { if (arguments.length === 0) { this.$nextTick(() => { let trElems = this.$el.querySelectorAll('.el-table__row') if (trElems.length) { let columns = this.$refs.refElTable.columns this.datas.forEach((item, index) => { let trElem = trElems[index] if (trElem.children.length) { if (item.editStatus === 'insert') { columns.forEach((column, cIndex) => this._updateColumnStatus(trElem, column, trElem.children[cIndex])) } else { columns.forEach((column, cIndex) => { let tdElem = trElem.children[cIndex] if (tdElem) { if (XEUtils.isEqual(item.data[column.property], item.store[column.property])) { let classList = tdElem.className.split(' ') tdElem.className = classList.filter(name => name !== 'editable-col_dirty').join(' ') } else { this._updateColumnStatus(trElem, column, trElem.children[cIndex]) } } }) } } }) } }) } else { this.$nextTick(() => { let { $index, _row, column, store } = scope let trElems = store.table.$el.querySelectorAll('.el-table__row') if (trElems.length) { let trElem = trElems[$index] let tdElem = trElem.querySelector(`.${column.id}`) if (tdElem) { let classList = tdElem.className.split(' ') if (XEUtils.isEqual(_row.data[column.property], _row.store[column.property])) { tdElem.className = classList.filter(name => name !== 'editable-col_dirty').join(' ') } else { this._updateColumnStatus(trElem, column, tdElem) } } } }) } } } } } </script>
EditableColumn.vue
<template> <el-table-column v-if="type === 'selection' || group" v-bind="attrs"> <slot></slot> </el-table-column> <el-table-column v-else-if="type === 'index'" v-bind="attrs"> <template slot="header" slot-scope="scope"> <slot name="head" v-bind="{$index: scope.$index, column: scope.column, store: scope.store}">#</slot> </template> <slot></slot> </el-table-column> <el-table-column v-else-if="type === 'expand'" v-bind="attrs"> <template slot="header" slot-scope="scope"> <slot name="head" v-bind="{$index: scope.$index, column: scope.column, store: scope.store}"></slot> </template> <template slot-scope="scope"> <slot v-bind="{$index: scope.$index, row: scope.row.data, column: scope.column, store: scope.store, _row: scope.row}"></slot> </template> </el-table-column> <el-table-column v-else-if="editRender" v-bind="attrs"> <template slot="header" slot-scope="scope"> <slot name="head" v-bind="{$index: scope.$index, column: scope.column, store: scope.store}"> <i class="el-icon-edit-outline editable-header-icon"></i>{{ scope.column.label }} </slot> </template> <template slot-scope="scope"> <template v-if="editRender.type === 'visible'"> <slot name="edit" v-bind="{$index: scope.$index, row: scope.row.data, column: scope.column, store: scope.store, _row: scope.row}"> <template v-if="editRender.name === 'ElSelect'"> <el-select v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"> <el-option v-for="(item, index) in editRender.options" :key="index" :value="item.value" :label="item.label" v-bind="editRender.optionAttrs"></el-option> </el-select> </template> <template v-else-if="editRender.name === 'ElCascader'"> <el-cascader v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-cascader> </template> <template v-else-if="editRender.name === 'ElTimePicker'"> <el-time-picker v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-time-picker> </template> <template v-else-if="editRender.name === 'ElDatePicker'"> <el-date-picker v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-date-picker> </template> <template v-else-if="editRender.name === 'ElInputNumber'"> <el-input-number v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-input-number> </template> <template v-else-if="editRender.name === 'ElSwitch'"> <el-switch v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-switch> </template> <template v-else-if="editRender.name === 'ElRate'"> <el-rate v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-rate> </template> <template v-else-if="editRender.name === 'ElColorPicker'"> <el-color-picker v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-color-picker> </template> <template v-else-if="editRender.name === 'ElSlider'"> <el-slider v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-slider> </template> <template v-else> <el-input v-model="scope.row.data[scope.column.property]" @change="changeEvent(scope)"></el-input> </template> </slot> </template> <template v-else> <template v-if="scope.row.editable.mode === 'row' ? scope.row.editActive : scope.row.editActive === scope.column.property"> <slot name="edit" v-bind="{$index: scope.$index, row: scope.row.data, column: scope.column, store: scope.store, _row: scope.row}"> <template v-if="editRender.name === 'ElSelect'"> <el-select v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"> <el-option v-for="(item, index) in editRender.options" :key="index" :value="item.value" :label="item.label" v-bind="editRender.optionAttrs"></el-option> </el-select> </template> <template v-else-if="editRender.name === 'ElCascader'"> <el-cascader v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-cascader> </template> <template v-else-if="editRender.name === 'ElTimePicker'"> <el-time-picker v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-time-picker> </template> <template v-else-if="editRender.name === 'ElDatePicker'"> <el-date-picker v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-date-picker> </template> <template v-else-if="editRender.name === 'ElInputNumber'"> <el-input-number v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-input-number> </template> <template v-else-if="editRender.name === 'ElSwitch'"> <el-switch v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-switch> </template> <template v-else-if="editRender.name === 'ElRate'"> <el-rate v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-rate> </template> <template v-else-if="editRender.name === 'ElColorPicker'"> <el-color-picker v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-color-picker> </template> <template v-else-if="editRender.name === 'ElSlider'"> <el-slider v-model="scope.row.data[scope.column.property]" v-bind="getRendAttrs(scope)" @change="changeEvent(scope)"></el-slider> </template> <template v-else> <el-input v-model="scope.row.data[scope.column.property]" @change="changeEvent(scope)"></el-input> </template> </slot> </template> <template v-else> <slot v-bind="{$index: scope.$index, row: scope.row.data, column: scope.column, store: scope.store, _row: scope.row}"> <template v-if="editRender.name === 'ElSelect'">{{ getSelectLabel(scope) }}</template> <template v-else-if="editRender.name === 'ElCascader'">{{ getCascaderLabel(scope) }}</template> <template v-else-if="editRender.name === 'ElTimePicker'">{{ getTimePickerLabel(scope) }}</template> <template v-else-if="editRender.name === 'ElDatePicker'">{{ getDatePickerLabel(scope) }}</template> <template v-else>{{ formatter ? formatter(scope.row.data, scope.column, scope.row.data[scope.column.property], scope.$index) : scope.row.data[scope.column.property] }}</template> </slot> </template> </template> </template> </el-table-column> <el-table-column v-else v-bind="attrs"> <template slot-scope="scope"> <slot v-bind="{$index: scope.$index, row: scope.row.data, column: scope.column, store: scope.store, _row: scope.row}">{{ formatter ? formatter(scope.row.data, scope.column, scope.row.data[scope.column.property], scope.$index) : scope.row.data[scope.column.property] }}</slot> </template> </el-table-column> </template> <script> import XEUtils from 'xe-utils' export default { name: 'ElEditableColumn', props: { group: Boolean, editRender: Object, index: [Number, Function], type: String, label: String, columnKey: String, prop: String, width: String, minWidth: String, fixed: [Boolean, String], sortable: [Boolean, String], sortMethod: Function, sortBy: [String, Array, Function], sortOrders: Array, resizable: { type: Boolean, default: true }, formatter: Function, showOverflowTooltip: Boolean, align: { type: String, default: 'left' }, headerAlign: String, className: { type: String, default: '' }, labelClassName: String, selectable: Function, reserveSelection: Boolean, filters: Array, filterPlacement: String, filterMultiple: { type: Boolean, default: true }, filterMethod: Function, filteredValue: Array }, computed: { attrs () { return { index: this.index, type: this.type, label: this.label, columnKey: this.columnKey, prop: this.prop, width: this.width, minWidth: this.minWidth, fixed: this.fixed, sortable: this.sortable, sortMethod: this.sortMethod ? this.sortMethodEvent : this.sortMethod, sortBy: XEUtils.isFunction(this.sortBy) ? this.sortByEvent : this.sortBy, sortOrders: this.sortOrders, resizable: this.resizable, showOverflowTooltip: this.showOverflowTooltip, align: this.align, headerAlign: this.headerAlign, className: `editable-column ${this.editRender ? 'editable-col_edit' : 'editable-col_readonly'}${this.className ? ' ' + this.className : ''}`, labelClassName: this.labelClassName, selectable: this.selectable ? this.selectableEvent : this.selectable, reserveSelection: this.reserveSelection, filters: this.filters, filterPlacement: this.filterPlacement, filterMultiple: this.filterMultiple, filterMethod: this.filterMethod ? this.filterMethodEvent : this.filterMethod, filteredValue: this.filteredValue } } }, methods: { getRendAttrs ({ row }) { let size = row.editable.size return Object.assign({ size }, this.editRender.attrs) }, getSelectLabel (scope) { let value = scope.row.data[scope.column.property] let selectItem = this.editRender.options.find(item => item.value === value) return selectItem ? selectItem.label : null }, matchCascaderData (values, index, list, labels) { let val = values[index] if (list && values.length > index) { list.forEach(item => { if (item.value === val) { labels.push(item.label) this.matchCascaderData(values, ++index, item.children, labels) } }) } }, getCascaderLabel (scope) { let values = scope.row.data[scope.column.property] || [] let labels = [] let attrs = this.editRender.attrs || {} this.matchCascaderData(values, 0, attrs.options || [], labels) return labels.join(attrs.separator || '/') }, getTimePickerLabel (scope) { let value = scope.row.data[scope.column.property] let attrs = this.editRender.attrs || {} return XEUtils.toDateString(value, attrs.format || 'hh:mm:ss') }, getDatePickerLabel (scope) { let value = scope.row.data[scope.column.property] let attrs = this.editRender.attrs || {} if (attrs.type === 'datetimerange') { return XEUtils.toArray(value).map(date => XEUtils.toDateString(date, attrs.format)).join(attrs.rangeSeparator) } return XEUtils.toDateString(value, attrs.format, 'yyyy-MM-dd') }, sortByEvent (row, index) { return this.sortBy(row.data, index) }, sortMethodEvent (a, b) { return this.sortMethod(a.data, b.data) }, selectableEvent (row, index) { return this.selectable(row.data, index) }, filterMethodEvent (value, row, column) { return this.filterMethod(value, row.data, column) }, changeEvent ({ $index, row, column, store }) { if (row.editable.showStatus) { this.$nextTick(() => { let trElem = store.table.$el.querySelectorAll('.el-table__row')[$index] let tdElem = trElem.querySelector(`.${column.id}`) let classList = tdElem.className.split(' ') if (XEUtils.isEqual(row.data[column.property], row.store[column.property])) { tdElem.className = classList.filter(name => name !== 'editable-col_dirty').join(' ') } else { if (!classList.includes('editable-col_dirty')) { classList.push('editable-col_dirty') tdElem.className = classList.join(' ') } } }) } } } } </script> <style lang="scss"> .editable { &.editable--icon { .editable-header-icon { display: inline-block; } } &.el-table--mini { .editable-column { height: 42px; } } &.el-table--small { .editable-column { height: 48px; } } &.el-table--medium { .editable-column { height: 62px; } } .editable-header-icon { display: none; } .editable-column { height: 62px; padding: 0; &.editable-col_dirty { position: relative; &:before { content: ''; top: -5px; left: -5px; position: absolute; border: 5px solid; border-color: transparent #C00000 transparent transparent; transform: rotate(45deg); } } .cell { >.edit-input, >.el-cascader, >.el-autocomplete, >.el-input-number, >.el-date-editor, >.el-select { width: 100%; } } } } </style>
使用
- 全局事件需要依赖 vuex 中的 globalClick 变量 (参考store/modules/event.js)
- 将 Editable.vue 和 EditableColumn.vue 复制到自己项目的 components 目录下
- 然后在 main.js 引入组件即可
import Editable from '@/components/Editable.vue' import EditableColumn from '@/components/EditableColumn.vue' Vue.component(Editable.name, Editable) Vue.component(EditableColumn.name, EditableColumn)
<template> <div> <el-button type="primary" @click="$refs.editable.insert({name: 'new1'})">新增</el-button> <el-button type="danger" @click="$refs.editable.removeSelecteds()">删除选中</el-button> <el-button type="danger" @click="$refs.editable.clear()">清空所有</el-button> <el-editable ref="editable" :data.sync="list"> <el-editable-column type="selection" width="55"></el-editable-column> <el-editable-column type="index" width="55"></el-editable-column> <el-editable-column prop="name" label="名字"></el-editable-column> <el-editable-column prop="sex" label="性别" :editRender="{name: 'ElSelect', options: sexList}"></el-editable-column> <el-editable-column prop="age" label="年龄" :editRender="{name: 'ElInputNumber', attrs: {min: 1, max: 200}}"></el-editable-column> <el-editable-column prop="region" label="地区" :editRender="{name: 'ElCascader', attrs: {options: regionList}}"></el-editable-column> <el-editable-column prop="birthdate" label="出生日期" :editRender="{name: 'ElDatePicker', attrs: {type: 'date', format: 'yyyy-MM-dd'}}"></el-editable-column> <el-editable-column prop="date1" label="选择日期" :editRender="{name: 'ElDatePicker', attrs: {type: 'datetime', format: 'yyyy-MM-dd hh:mm:ss'}}"></el-editable-column> <el-editable-column prop="flag" label="是否启用" :editRender="{name: 'ElSwitch'}"></el-editable-column> <el-editable-column prop="remark" label="备注" :editRender="{name: 'ElInput'}"></el-editable-column> <el-editable-column label="操作"> <template slot-scope="scope"> <el-button size="mini" type="danger" @click="removeEvent(scope.row, scope.$index)">删除</el-button> </template> </el-editable-column> </el-editable> </div> </template> <script> import { MessageBox } from 'element-ui' export default { data () { return { sexList: [ { label: '男', value: '1' }, { label: '女', value: '0' } ], regionList: [ { value: 'bj', label: '北京', children: [ { value: 'bjs', label: '北京市', children: [ { value: 'dcq', label: '东城区' } ] } ] }, { value: 'gds', label: '广东省', children: [ { value: 'szs', label: '深圳市', children: [ { value: 'lhq', label: '罗湖区' } ] }, { value: 'gzs', label: '广州市', children: [ { value: 'thq', label: '天河区' } ] } ] } ], list: [ { name: 'test11', height: 176, age: 26, sex: '1', region: null, birthdate: new Date(1994, 0, 1), date1: new Date(2019, 0, 1, 20, 0, 30), remark: '备注1', flag: false }, { name: 'test22', height: 166, age: 24, sex: '0', region: ['gds', 'szs', 'lhq'], birthdate: new Date(1992, 0, 1), date1: new Date(2019, 0, 1, 12, 10, 30), remark: '备注2', flag: true }, { name: 'test33', height: 172, age: 22, sex: '1', region: ['bj', 'bjs', 'dcq'], birthdate: new Date(1990, 0, 1), date1: new Date(2019, 0, 1, 0, 30, 50), remark: null, flag: false } ] } }, methods: { removeEvent (row, index) { MessageBox.confirm('确定删除该数据?', '温馨提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.$refs.editable.removeRow(index) }).catch(e => e) } } } </script>
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- vue+iview 实现可编辑表格
- vue-split-table【表格合并和编辑插件】
- JFinal-layui v1.4.3 增添新动力--报表设计器、可以编辑表格
- 开源 UI 库中,唯一同时实现了大表格虚拟化和树表格的 Table 组件 原 荐
- html复杂表格
- PHP基础知识(表格)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Blockchain Basics
Daniel Drescher / Apress / 2017-3-16 / USD 20.99
In 25 concise steps, you will learn the basics of blockchain technology. No mathematical formulas, program code, or computer science jargon are used. No previous knowledge in computer science, mathema......一起来看看 《Blockchain Basics》 这本书的介绍吧!
HTML 压缩/解压工具
在线压缩/解压 HTML 代码
HEX CMYK 转换工具
HEX CMYK 互转工具