表格内的单元格是可编辑的。
点击增加增加一条数据,点击删除删掉本条数据。
基本原理:
<el-form ref="FormRef" :model="tableDataGroup" >
<el-table :data="tableDataGroup.tableData">
<template
v-for="(item, colIndex) in tableSchema.tableColum"
:key="colIndex"
>
<el-table-column
:label="item.label"
:width="item.width ? item.width : ''"
>
<el-form-item
...
表单是表格的父级。
数据格式说明:
const tableDataGroup = reactive({
tableData: [
{
id: '1',
name: 'ces1',
},
{
id: '2',
name: 'ces1',
}
]
});
tableDataGroup是表单绑定的数据,tableDataGroup.tableData是表格绑定的数据,每一个数据项表示表格的每一行。
tableSchema.tableColum是表头,表头并不是一项一项地写,仍是采用数据渲染。之所以嵌套是因为虽然tableColumn是主要数据,但还会在tableSchema里增加许多表格整体的配置,比如需不需要操作栏,需不需要单选/多选栏等。
tableColum: [
{
label: '编号',
prop: 'id',
type: 'input',
width: '50px',
disabled: true
},
{
label: '开始时间',
prop: 'size',
type: 'datetime'
},
......
(这里不需要校验规则,所以不考虑那种复杂的情况,如果需要校验,那么CSDN)
表头tableSchema.tableColum里写了几个字段,就会渲染几列,
表单根据scope渲染数据,可以理解为表格的每一行,对应每一个子表单(tableDataGroup是所有子表单的父级)。
<el-table-column
v-if="item.type"
:label="item.label"
:width="item.width ? item.width : ''"
>
<template #default="scope">
<el-form-item>
<FormItem :formData="scope.row" :data="item" :editable="true" />
</el-form-item>
</template>
</el-table-column>
操作列的表头和单元格都采用插槽的形式,在父组件中写的增加按钮和删除按钮,分别对应tableDataGroup.tableData里每一项的增加和删除。
插槽,或者说具名插槽,就不多说了。
父组件中:
<el-button @click="dataListAdd(rowData)">增加</el-button>
...
function dataListAdd(data) {
tableDataGroup.tableData.push({ id: '', name: '增加一条' });
}
...
<template #tableAction="{ rowData, index }">
<el-button
class="baq-button warn"
@click="dataListDelet(rowData, index)"
>删除</el-button
>
...
function dataListDelet(data, index) {
tableDataGroup.tableData.splice(index, 1);
}
...
子组件中:
<el-table-column v-else-if="item.label === '操作'" :width="100">
<template #header="scope">
<slot name="tableHeader" :rowData="scope.row"></slot>
</template>
<template #default="{ row, $index }">
<slot name="tableAction" :rowData="row" :index="$index"></slot>
</template>
</el-table-column>
下面是部分代码:
MyTable.vue
<template>
<el-form ref="FormRef" :model="tableDataGroup" :inline="true">
<el-table
:header-cell-style="{ 'text-align': 'center' }"
border
:cell-style="{ borderColor: '#3b90d0', 'text-align': 'center' }"
:data="tableDataGroup.tableData"
>
<template
v-for="(item, colIndex) in tableSchema.tableColum"
:key="colIndex"
>
<el-table-column
v-if="item.type"
:label="item.label"
:width="item.width ? item.width : ''"
>
<template #default="scope">
<el-form-item>
<FormItem :formData="scope.row" :data="item" :editable="true" />
</el-form-item>
</template>
</el-table-column>
<el-table-column v-else-if="item.label === '操作'" :width="100">
<template #header="scope">
<slot name="tableHeader" :rowData="scope.row"></slot>
</template>
<template #default="{ row, $index }">
<slot name="tableAction" :rowData="row" :index="$index"></slot>
</template>
</el-table-column>
</template>
</el-table>
</el-form>
<div class="page-style">
<el-pagination
v-model:current-page="pageNum"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
:total="props.pageTotal || 0"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script setup>
import formItem from '@/components/BaqForm/FormItem';
const FormItem = formItem();
const props = defineProps({
tableSchema: Object,
tableDataGroup: Object,
pageTotal: Number
});
const emits = defineEmits(['getFormData']);
const FormRef = ref();
const pageNum = ref(1);
const pageSize = ref(10);
const handleSizeChange = val => {
console.log(`${val} items per page`);
searchForm('search', val, pageNum.value);
};
const handleCurrentChange = val => {
console.log(`current page: ${val}`);
searchForm('search', pageSize.value, val);
};
// 查询/重置
async function searchForm(val, size, num) {
emits('getFormData', {
pageNum: num || pageNum.value,
pageSize: size || pageSize.value
});
}
// 表单提交
async function submitForm() {
return props.tableDataGroup.tableData;
}
// 表单校验
async function validate() {
if (!FormRef.value) return;
const result = await FormRef.value.validate();
return result;
}
defineExpose({ validate, submitForm });
</script>
父组件:
<MyTable
ref="tableRef"
:tableSchema="tableSchema"
:tableDataGroup="tableDataGroup"
:pageTotal="pageTotal"
>
<template #tableHeader="{ rowData }">
<el-button
class="baq-button"
style="margin: 0"
@click="dataListAdd(rowData)"
>增加</el-button
>
</template>
<template #tableAction="{ rowData, index }">
<el-button
class="baq-button warn"
@click="dataListDelet(rowData, index)"
>删除</el-button
>
</template>
</MyTable>
数据:
const tableDataGroup = reactive({
tableData: [
{
id: '1',
name: 'ces1'
},
{
id: '2',
name: 'ces1'
}
]
});
export const tableSchema = {
tableColum: [
{
label: '编号',
prop: 'id',
type: 'input',
width: '50px',
clearable: '1',
disabled: true
},
{
label: '开始时间',
prop: 'size',
type: 'datetime'
},
{
label: '结束时间',
prop: 'type',
type: 'datetime'
},
{
label: '名称',
prop: 'name',
type: 'select',
options: [
{
label: '111',
value: '111'
}
]
},
{
label: '内容',
prop: 'content',
type: 'select',
options: [
{
label: '111',
value: '111'
}
]
},
{
label: '备注',
prop: 'editTime',
type: 'input'
},
{
label: '操作',
prop: 'action'
}
]
};
FormItem.jsx
这是有参考的CSDN
import {
ElCheckbox,
ElCheckboxGroup,
ElDatePicker,
ElInput,
ElInputNumber,
ElOption,
ElRadio,
ElRadioGroup,
ElSelect,
ElTimePicker,
ElTreeSelect
} from 'element-plus';
import { defineComponent } from 'vue';
// 普通显示
const Span = (form, data) => (
<span>
{form[data.prop]}
</span>
);
// 输入框
const Input = (form, data) => (
<div style={ 'display:flex; align-items:center;' }>
<ElInput
v-model={form[data.prop]}
type={data.type}
size="default"
show-password={data.type == 'password'}
clearable={data.clearable==='1'?false:true}
disabled={data.disabled}
placeholder={'请输入' + data.label}
autosize={{
minRows: 3,
maxRows: 4
}}
style={ data.append? 'margin-right: 8px; width:100px':''}
{...data.props}
></ElInput><span v-show={data.append?true:false} style={ 'color: #333; line-height:32px'}>{data.append}</span>
</div>
);
// 数字输入框
const InputNumber = (form, data) => (
<ElInputNumber
size="default"
v-model={form[data.prop]}
disabled={data.disabled}
controls-position="right"
{...data.props}
/>
);
const setLabelValue = (_item, { optionsKey }) => {
return {
label: optionsKey ? _item[optionsKey.label] : _item.label,
value: optionsKey ? _item[optionsKey.value] : _item.value
};
};
// 选择框
const Select = (form, data) => (
<ElSelect
size="default"
v-model={form[data.prop]}
filterable
disabled={data.disabled}
clearable
placeholder={'请选择' + data.label}
{...data.props}
>
{
data.options?data.options.map((item) => {
return <ElOption {...setLabelValue(item, data)} />;
}):''
}
</ElSelect>
);
// 单选/区间日期
const Date = (form, data) => (
<ElDatePicker
size="default"
v-model={form[data.prop]}
type={data.type}
value-format={data.valueFormat}
format={data.format}
disabled={data.disabled}
range-separator="-"
style={ data.width?'width:'+ data.width:'width:200px'}
start-placeholder={data.startPlaceholder}
end-placeholder={data.endPlaceholder}
placeholder={'请选择' + data.label}
startPlaceholder={data.type === 'daterange' ? '开始时间' : ''}
endPlaceholder={data.type==='daterange'?'结束时间':'' }
{...data.props}
/>
);
// 单选/区间时间
const Time = (form, data) => (
<ElTimePicker
size="default"
v-model={[form[data.prop]]}
value-format={data.valueFormat}
format={data.format}
range-separator="至"
disabled={data.disabled}
start-placeholder={data.start}
is-range={data.isRange}
end-placeholder={data.end}
{...data.props}
/>
);
// 单选
const Radio = (form, data) => (
<ElRadioGroup disabled={data.disabled} v-model={form[data.prop]}>
{data.radios?data.radios.map(
(item) => {
return (
<ElRadio label={setLabelValue(item, data.prop).value}>
{setLabelValue(item, data.prop).label}
</ElRadio>
);
}
):''}
</ElRadioGroup>
);
// 多选
const Checkbox = (form, data) => (
<ElCheckboxGroup disabled={data.disabled} size="default" v-model={form[data.prop]}>
{data.checkboxs.map(
(item) => {
return (
<ElCheckbox label={setLabelValue(item, data.prop).value}>
{setLabelValue(item, data.prop).label}
</ElCheckbox>
);
}
)}
</ElCheckboxGroup>
);
const TreeSelect = (form, data) => (
<ElTreeSelect data={data.treeList} v-model={form[data.prop]} props={data.props} check-strictly={data.checkStrictly} />
)
const setFormItem = (
form,
data,
editable
) => {
if (!form) return null;
if (!editable) return Span(form, data);
switch (data.type) {
case 'input':
return Input(form, data);
case 'textarea':
return Input(form, data);
case 'password':
return Input(form, data);
case 'inputNumber':
return InputNumber(form, data);
case 'select':
return Select(form, data);
case 'treeSelect':
return TreeSelect(form,data)
case 'date':
case 'daterange':
case 'datetime':
return Date(form, data);
case 'time':
return Time(form, data);
case 'radio':
return Radio(form, data);
case 'checkbox':
return Checkbox(form, data);
default:
return null;
}
};
export default () =>
defineComponent({
props: {
data: Object,
formData: Object,
editable: Boolean
},
setup(props) {
return () =>
props.data
? setFormItem(props.formData, props.data, props.editable)
: null;
}
});