什么是可编辑表格呢?简单来说就是在一个表格里面进行表单操作,执行增删改查。这在一些后台管理系统中是尤为常见的。
今天我们根据vue2
+ element-ui
来设计一个表单表格组件。(不涉及完整代码,想要使用完整功能可以看底部连接)
首先我们思考表格里面的表单元素应该如何实现。在用户使用的时候,我们希望用户传入一个指定的类型自动去匹配对应的表单组件。那我们就应该将所有类型做一个集成,并分别给他们一个类型名称。
当然,为了最大程度为他们保留在element-ui
已经实现的属性与方法,formEle
肯定接收的是一个对象,对象里面包含了他要展示的组件类型,一些原element-ui
的属性方法,我们使用$attrs
便可以直接将属性绑定到element-ui的表单组件上。
我们为这个表单元素组件取名为formEle
,并最大程度为他们保留在element-ui
已经实现的属性与方法。当然我们也可以修改一下一些常用属性的默认值,例如打开clearable清除属性,设置placeholder默认值。
下面是部分代码
<template>
<el-input
v-if="formType === 'input'"
v-model="localValue"
v-bind="$attrs"
v-on="$listeners"
:clearable="clearable"
:placeholder="placeholder"
>
<template v-for="(value, name) in $slots" #[name]>
<slot :name="name"> slot>
template>
el-input>
<el-input-number
v-else-if="formType === 'inputNumber'"
v-model="localValue"
v-bind="$attrs"
v-on="$listeners"
:placeholder="placeholder"
>
<template v-for="(value, name) in $slots" #[name]>
<slot :name="name"> slot>
template>
el-input-number>
<el-select
v-else-if="formType === 'select'"
v-model="localValue"
v-bind="$attrs"
v-on="$listeners"
:clearable="clearable"
:placeholder="placeholder"
>
<el-option
v-for="item in options"
v-bind="item"
:key="item.value"
>el-option>
el-select>
<el-date-picker
v-else-if="formType === 'datePicker'"
v-model="localValue"
v-bind="$attrs"
v-on="$listeners"
:placeholder="placeholder"
:endPlaceholder="endPlaceholder"
:startPlaceholder="startPlaceholder"
>
el-date-picker>
<el-time-select
v-else-if="formType === 'timeSelect'"
v-model="localValue"
v-bind="$attrs"
v-on="$listeners"
:placeholder="placeholder"
>
el-time-select>
<el-time-picker
v-else-if="formType === 'timePicker'"
v-model="localValue"
v-bind="$attrs"
v-on="$listeners"
:placeholder="placeholder"
:endPlaceholder="endPlaceholder"
:startPlaceholder="startPlaceholder"
>
el-time-picker>
<el-switch
v-else-if="formType === 'switch'"
v-model="localValue"
v-bind="$attrs"
:placeholder="placeholder"
>
el-switch>
<el-cascader
v-else-if="formType === 'cascader'"
v-model="localValue"
:options="options"
ref="cascader"
v-bind="$attrs"
v-on="$listeners"
:placeholder="placeholder"
>el-cascader>
template>
<script>
export default {
name: 'ClFormEle',
props: {
// 表单类型
formType: {
type: String,
default: 'input'
},
modelValue: null,
options: {
type: Array,
default: () => []
}
},
model: {
prop: 'modelValue',
event: 'editModelValue'
},
computed: {
localValue: {
get() {
return this.modelValue
},
set(val) {
this.$emit('editModelValue', val)
}
},
clearable() {
return this.$attrs.clearable === false ? false : true
},
placeholder() {
let text = '请选择'
if (this.formType === 'input') {
text = '请输入'
}
return this.$attrs.placeholder || text + (this.$attrs.label || '')
},
rangeSeparator() {
return this.$attrs.rangeSeparator || '至'
},
startPlaceholder() {
if (this.formType === 'datePicker') {
return this.$attrs.startPlaceholder || '开始日期'
} else {
return this.$attrs.startPlaceholder || '开始时间'
}
},
endPlaceholder() {
if (this.formType === 'datePicker') {
return this.$attrs.startPlaceholder || '结束日期'
} else {
return this.$attrs.startPlaceholder || '结束时间'
}
}
},
methods: {
// 获取级联组件的回显值
getCasLabelcader() {
this.$nextTick(() => {
return this.$refs.cascader?.inputValue
})
}
}
}
script>
<style lang="less" scoped>
.el-input {
width: 100%;
height: 30px;
}
.el-select {
width: 100%;
}
.el-date-editor {
width: 100%;
}
style>
需要注意的是我们还需要将
element-ui
组件的插槽位置预留出来。
首先我们要想到我们可能会使用表单组件的校验等功能,那么在最外层就一定需要el-form
来包裹表格组件。
其次是表单组件上model的绑定值的选择。可以预料的是我们最终绑定的数据一定是一个数组格式的。因为表格肯定不会只有一条数据。但是我们又无法在表单的model属性上绑定数组,所以我们可以将数组封装为数组对象的格式。
<el-form ref="formRef" :model="{ formData: modelValueCom }">
接下来我们将对列的数据进行处理。列的数据肯定也是一个数组结构的,每列我们分为两个板块,一个是用在el-table-column
上的属性值,一个则是内部表单内容的属性值。为了方便区分,我们将采用一些数据格式:
[
{
prop: 'name',
label: '姓名',
minWidth: '200px',
formEle: {
formType: 'input'
}
}
]
上面的formEle对象就行将来需要用在表单组件上的属性值,而其他属性则是直接作用在el-table-column
上的。
<el-table-column
v-for="columItem in columList"
:key="columItem.prop"
v-bind="getColumnAttr(columItem)"
>
getColumnAttr方法的作用就是剔除formEle属性
接着就是关于表单组件的使用了。既然我们需要表单的校验方法,我们就需要el-form-item
组件来包裹表单组件,而组件上最重要的prop
属性则可以直接在上面的列数据中获取,注意因为数据是数组,我们需要使用下标绑定到具体的哪条数据上,label
属性我们则可以直接省略,因为我们已经有表头来展示了。
<el-form-item
v-if="row.isEdit"
:prop="'formData.' + $index + '.' + columItem.prop"
:rules="columItem.formEle?.rules"
>
说到表头我们可能需要想到为他添加必填校验的表示符,我们在formEle里面加入showRequiredIcon属性判断是否需要展示必填校验。
<el-table-column
v-for="columItem in columList"
:key="columItem.prop"
v-bind="getColumnAttr(columItem)"
>
<template #default="{ row, $index }">
<el-form-item
v-if="row.isEdit"
:prop="'formData.' + $index + '.' + columItem.prop"
:rules="columItem.formEle?.rules"
>
<ClFormEle
v-model="row[columItem.prop]"
v-bind="columItem.formEle"
v-on="eventMap[columItem.prop]"
:label="columItem.label"
>ClFormEle>
el-form-item>
<span v-else>{{
getLabel(
getFormType(columItem),
columItem,
row[columItem.prop],
$index
)
}}span>
template>
<template #header>
<span v-if="columItem.formEle?.showRequiredIcon" class="required_icon"
>*span
>
{{ columItem.label }}
template>
el-table-column>
最后我们应该还需要有一列操作按钮对行数据进行修改,保存,删除等操作。修改,保存就是修改表格数据的isEdit属性,让在表单元素与span展示中切换。
<el-table-column v-bind="btnColCpd">
<template #default="{ row, $index: index, column }">
<slot name="endColumn" :data="{ row, index, column }">
<el-button
v-if="!row.isEdit"
type="text"
icon="el-icon-edit"
@click.native.stop="rowEdit(index)"
>修改el-button
>
<el-button
v-else
type="text"
icon="el-icon-check"
@click.native.stop="saveRow(index)"
>保存el-button
>
<el-popconfirm
v-if="isDelBtnTip"
:title="delBtnTip"
@confirm="delRow(index)"
>
<el-button
type="text"
icon="el-icon-delete"
class="del_btn_text"
slot="reference"
>删除el-button
>
el-popconfirm>
<el-button
v-else
type="text"
icon="el-icon-delete"
class="del_btn_text"
slot="reference"
@click.native.stop="delRow(index)"
>删除el-button
>
slot>
template>
el-table-column>
btnColCpd是一个关于最后一列属性配置对象,我们可以的使用element-ui里面el-table-column组件的属性来控制最后一列。
到这一个简单的表单组件结构就基本实现了。
当然,上面的设计还有很多的缺陷,比如表单组件的方法该如何绑定,各个表单组件插槽该如何抛出,表单的校验可不可以在离开行的时候触发,如果想要使用的组件formEle不包含怎么办等等,想要实现一个完整的表单表格组件这些都是必须要考虑的。我封装了一个较为完整的表单表格组件,大家可以在TableForm查看。