在现代的vue组件库上实现表格编辑其实不难,它们大都提供了作用域插槽,可以在插槽内插入input
组件来实现。但这样做有个弊端,太重了!在几十上百行的表格内铺满input
卡顿几乎不可避免,而且也很难做到美观。
想实现动态编辑功能其实理论上并不难,我们只需在当前点击的单元格中渲染出input
,光标离开销毁这个input
即可
这里借助 vue自定义指令来实现
因为指令不支持直接数据双向绑定,参数上设计了value
和input
2个钩子,一个用来取值,一个用来赋值
import Vue from 'vue'; const getTargetEl = (el, nodeName = 'TD') => el.nodeName === nodeName ? el : el.parentElement ? getTargetEl(el.parentElement, nodeName) : undefined; function createInput(options) { const input = document.createElement('input'); input.className = options.inputClass || 'td-input'; input.type = options.inputType || 'text'; input.value = typeof options.value === 'function' ? options.value() : options.value; return input; } const TdInput = { inserted(el, binding) { const options = binding.value || {}; const targetEl = getTargetEl(el, options.nodeName || 'TD'); if (targetEl) { let inputEl; targetEl.style.position = 'relative'; targetEl.addEventListener(options.type || 'click', () => { if (!inputEl) { inputEl = createInput(options); targetEl.appendChild(inputEl); inputEl.focus(); inputEl.onblur = () => { options.input && options.input(inputEl.value); targetEl.removeChild(inputEl); inputEl = undefined; }; } }); } }, }; /** * v-td-input="{value: () => row.name, input: v => row.name = v }" */ Vue.directive('td-input', TdInput);
代码写完了,我们需要给这个input
定义样式,我在createInput
的代码中默认给它加了.td-input
这个类,并提供了inputClass
这个配置项方便自定义扩展
<style> .td-input { position: absolute; left: 0; top: 0; width: 100%; height: 100%; padding: 0 10px; box-shadow: 1px 1px 20px rgba(0,0,0,.15); box-sizing: border-box; background-color: #FFF; background-image: none; border: 1px solid #DCDFE6; display: inline-block; outline: 0; } .td-input:focus { border-color: #5FB878!important } </style>
在作用域插槽内通过一个可以绑定指令的dom
来使用
v-td-input="{value: () => row.date, input: v => row.date = v }"
类似下面这个例子
<el-table :data="tableData" style="width: 100%"> <el-table-column prop="date" label="日期" width="180"> <template slot-scope="{row}"> <span v-td-input="{value: () => row.date, input: v => row.date = v }">{{ row.date }}<span> </template> </el-table-column> </el-table>
提供了一个type
配置项,指定触发事件,默认是单击click
,你也可以像下面这样设置成dblclick
来实现双击编辑
<span v-td-input="{ type: 'dblclick', value: () => row.date, input: v => row.date = v }" > {{ row.date }} <span>
我测试了比较热门的几个vue组件库,都能正确工作
element-ui
iview
antd-vue