正常情况下如果我们需要在
element-ui
或element-plus
的表格中实现行内编辑功能,需要使用,然后通过
v-if
去判断显示span
还是el-input
。如果行内多处需要进行行内编辑时,就明显的增加了代码量。(当然最原始的初衷就是摸鱼时看看能不能实现)
优点:template的代码量减少,每个table自己维护自己的编辑方法,代码逻辑统一封装,使用也很方便。
缺点: 目前只支持input,支持其他的直接手写一个createElement
就好了。
代码中提供了注释,目前只支持
input
输入类型。思考过动态插入element
的表单组件,实力有限试过很多方法后始终有问题。
/**
* 表格编辑指令
* @param {Array} value - [editableList, props, save, getList]
* @param {Array} editableList - 可编辑列的索引
* @param {Array} props - 可编辑列对应的prop名称列表
* @param {Function} save - 保存编辑的方法
* @param {Function} getList - 获取列表的方法
* @example
*
*
*
*
* 只需要完成编辑按钮的逻辑
* 具体可参考/geneGenieDx/dataset代码
*
*
* @description
* 待更新功能:
* a. 支持其他类型的编辑列的输入框,目前只支持文本框
* b. 分页会带着编辑的状态,需要优化;(已解决:应该有些状态需要通过store去管理)
* c. edit-cancel然后分页之后,会导致数据错乱(已解决:设置font-size=0px)
*/
import _ from 'lodash'
import { parse, stringify } from 'flatted'
import modal from '@/plugins/modal'
import store from '@/store'
import i18n from '@/lang'
const { t } = i18n.global
let editableList = []
let targetRow = null
// 连续点击了多行的编辑按钮后,只有最后一行的编辑状态会被保存,multiRowEdit用于区分这种情况下的调用
function updateRowDom(targetRow, editableList, multiRowEdit = true) {
Array.from(targetRow.children).forEach((item, i) => {
if (editableList.includes(i)) {
item.children[0].style.fontSize = '14px'
item.children[0].lastElementChild?.tagName === 'INPUT' && item.children[0].lastElementChild.remove()
}
})
if (multiRowEdit) {
targetRow = null
store.dispatch('table/setEditId', '')
}
}
export default {
mounted(el, bind) {
const { value } = bind
const [indexList, props, save, getList] = value
let newRow = {}
editableList = indexList
el.addEventListener('click', e => {
if (['Edit', 'edit', '编辑'].includes(e.target.innerText)) {
// 多次点击不同行的编辑清空上一次的编辑状态
store.state.table.editId && targetRow && updateRowDom(targetRow, editableList, false)
// 编辑
const rowData = store.state.table.rowData
newRow = parse(stringify(rowData.row))
targetRow = document.querySelectorAll('.el-table tbody .el-table__row')[rowData.index]
Array.from(targetRow.children).forEach((item, i) => {
if (editableList.includes(i)) {
const input = document.createElement('input')
input.type = rowData.inputType
input.value = newRow[props[i]]
input.style.width = '100%'
input.style.padding = '4px 6px'
input.style.boxSizing = 'border-box'
input.style.outline = 'none'
input.style.fontSize = '14px'
input.onblur = e => {
newRow[props[i]] = e.target.value
}
item.children[0].style.fontSize = '0px'
// item.children[0].innerHTML = ''
item.children[0].appendChild(input)
}
})
} else if (['Save', 'save', '保存'].includes(e.target.innerText)) {
// 保存编辑
save(newRow).then(() => {
updateRowDom(targetRow, editableList)
modal.msgSuccess(t('pub.operationSuc'))
getList()
}).catch(() => {
updateRowDom(targetRow, editableList)
})
} else if (['Cancel', 'cancel', '取消'].includes(e.target.innerText)) {
// 取消编辑
updateRowDom(targetRow, editableList)
}
})
},
beforeUpdate() {
if (store.state.table.pageChange && targetRow) {
updateRowDom(targetRow, editableList)
store.dispatch('table/setPageChange', false)
}
}
};
// store.js
const state = {
pageChange: false,
editId: '',
rowData: {},
}
const mutations = {
SET_PAGE_CHANGE: (state, view) => {
state.pageChange = view
},
SET_EDIT_ID: (state, view) => {
state.editId = view
},
SET_ROW_DATA: (state, view) => {
state.rowData = view
},
}
const actions = {
setPageChange({ commit }, view) {
commit('SET_PAGE_CHANGE', view)
},
setEditId({ commit }, view) {
commit('SET_EDIT_ID', view)
},
setRowData({ commit }, view) {
commit('SET_ROW_DATA', view)
}
}
export const useTableEdit = (keyName) => {
const store = useStore()
const editId = computed(() => store.state.table.editId)
const handleEdit = (row, index, inputType = 'text') => {
store.dispatch('table/setEditId', row[keyName])
store.dispatch('table/setRowData', { row, index, inputType })
}
return {
editId,
handleEdit,
}
}
参数说明见指令的注释
// template
<el-table v-tableEdit="[[0, 1], ['caseSetName', 'descInfo'], saveRow, getCases]" :data="tableData">
<el-table-column :label="t('task.dataset')" prop="caseSetName" min-width="100">
</el-table-column>
<el-table-column :label="t('sample.description')" prop="descInfo" min-width="200">
<template #default="{ row }">
<span>{{ row.descInfo || '-' }}</span>
</template>
</el-table-column>
<el-table-column :label="t('sample.number')" prop="caseNum" />
<el-table-column :label="t('sample.creator')" prop="createBy" />
<el-table-column :label="t('pub.operates')" width="200">
<template #default="{ row, $index }">
<el-button link class="del-text" @click="delCase(row.caseSetId, $index)">{{
t('pub.delete')
}}</el-button>
<el-button
v-if="editId !== row.caseSetId"
link
@click="handleEdit(row, $index)"
>{{t('pub.edit')}}</el-button>
<template v-else>
<el-button type="primary" link>{{ t('pub.save') }}</el-button>
<el-button link>{{ t('pub.cancel') }}</el-button>
</template>
</template>
</el-table-column>
</el-table>
// script
// 使用hooks提供的useTableEdit
const { editId, handleEdit } = useTableEdit('caseSetId')
看看能不能支持动态插入
element
的表单组件(有想法可以留言互相探讨)。
save
的按钮其实可以通过input
的回车事件实现,如果有其他表单可能就不行了,比如说select
之类的。