闽公网安备 35020302035485号

vue create kalacloud-vue3-element-plus-table // OR npx vue create kalacloud-vue3-element-plus-table然后安装 UI 框架 Element Plus:
npm install element-plus --save // OR yarn add element-plus安装完成后,在项目里导入 ElementPlus,修改 main.js 如下:
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
导入后,就可以启动项目了,执行以下命令:npm run serve

tableHeader: {
name: "姓名",
birth: "生日",
address: "地址",
age: "年龄",
phone: "电话",
}
这个对象中的 key 对应表格数据中的 prop,value 对应实际显示的 label,这样通过一个简单的对象,就可以连接表头和 表格body 之间的关系。然后还需要后端返回具体的表格数据:tableData: [{
name: '张三',
date: '2016-05-02',
address: '上海市普陀区金沙江路 1518 弄',
age: 18,
phone:"12345678910",
}, {
date: '2016-05-04',
name: '李四',
address: '上海市普陀区金沙江路 1517 弄',
age: 19,
phone:"12345678911",
}, {
date: '2016-05-01',
name: '王五',
address: '上海市普陀区金沙江路 1519 弄',
age: 20,
phone:"12345678912",
}, {
date: '2016-05-03',
name: '赵六',
address: '上海市普陀区金沙江路 1516 弄',
age: 21,
phone:"12345678913",
}]
实现表格列动态渲染的功能,需要用到一个很关键的 vue 指令,那就是 v-for,v-for 不仅可以遍历数据,也可以遍历对象:<div v-for="(value, key) in object">
{{ key }}: {{ value }}
</div>
这里我们就需要用到这个特性,来对 tableHeader 进行遍历,获取 key 和 value。基于以上讲解,现在我们具体实践一下如何实现表格列的动态渲染。在 components 目录中新建 DynamicTable.vue:<template>
<div>
<h2>Vue3 + Element plus 动态表格</h2>
<el-table :data="tableData" style="width: 100%">
<el-table-column
:prop="index"
:label="item"
v-for="(item, index) in tableHeader"
:key="index"
>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: "test",
data() {
return {
tableHeader: {
name: "姓名",
birth: "生日",
address: "地址",
age: "年龄",
phone: "电话",
},
tableData: [{
name: '张三',
address: '上海市普陀区金沙江路 1518 弄',
birth: '2016-05-02',
age: 18,
phone: "12345678910",
}, {
name: '李四',
birth: '2016-05-04',
address: '上海市普陀区金沙江路 1517 弄',
age: 19,
phone: "12345678911",
}, {
name: '王五',
birth: '2016-05-01',
address: '上海市普陀区金沙江路 1519 弄',
age: 20,
phone: "12345678912",
}, {
name: '赵六',
birth: '2016-05-03',
address: '上海市普陀区金沙江路 1516 弄',
age: 21,
phone: "12345678913",
}]
}
},
}
</script>
组件编写完成后,修改 App.vue,导入该组件即可:<template>
<DynamicTable />
</template>
<script>
import DynamicTable from './components/DynamicTable.vue'
export default {
name: 'App',
components: {
DynamicTable
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
保存后页面会自动热更新,实现效果如下:
tableHeader: {
name: {
label: "姓名",
sort: true,
},
birth: {
label: "生日",
sort: false,
},
address: {
label: "地址",
sort: false,
},
age: {
label: "年龄",
sort: true,
},
phone: {
label: "电话",
sort: false,
}
},
然后再改造 el-table 如下:<el-table-column :prop="index" :label="item.label" v-for="(item, index) in tableHeader" :sortable="item.sort" :key="index" >

# NPM npm install @element-plus/icons-vue # Yarn yarn add @element-plus/icons-vue然后在 main.js 中导入:
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
在 components 目录下新建 DynamicModifyTable.vue:<template>
<div>
<h2>Vue3 + Element plus 动态修改表格</h2>
<h3>「duidaima.com」</h3>
<el-table :data="tableData" style="width: 100%">
<el-table-column
:prop="item.prop"
:label="item.label"
v-for="(item, index) in tableHeader"
:key="item.prop"
>
<template #default="scope">
<div
v-show="item.editable || scope.row.editable"
class="editable-row"
>
<template v-if="item.type === 'input'">
<el-input
size="small"
v-model="scope.row[item.prop]"
:placeholder="`请输入${item.label}`"
@change="handleEdit(scope.$index, scope.row)"
/>
</template>
<template v-if="item.type === 'date'">
<el-date-picker
v-model="scope.row[item.prop]"
type="date"
value-format="YYYY-MM-DD"
:placeholder="`请输入${item.label}`"
@change="handleEdit(scope.$index, scope.row)"
/>
</template>
</div>
<div
v-show="!item.editable && !scope.row.editable"
class="editable-row"
>
<span class="editable-row-span">{{ scope.row[item.prop] }}</span>
<el-popover
placement="right"
:width="120"
trigger="hover"
content="this is content, this is content, this is content"
>
<template #reference>
<el-icon class="icon" :size="18">
<Edit />
</el-icon>
</template>
<div class="menu-list">
<div
class="menu-item"
@click="prepend(scope.$index)"
>
上方插入一行
</div>
<div
class="menu-item divider"
@click="append(scope.$index)"
>
下方插入一行
</div>
<div class="menu-item" @click="deleteCurrentColumn(index)">
删除当前列
</div>
<div class="menu-item" @click="insertBefore(index)">
前方插入一列
</div>
<div class="menu-item" @click="insertAfter(index)">
后方插入一列
</div>
</div>
</el-popover>
</div>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button
v-show="!scope.row.editable"
size="small"
@click="scope.row.editable = true"
>编辑</el-button
>
<el-button
v-show="scope.row.editable"
size="small"
type="success"
@click="scope.row.editable = false"
>确定</el-button
>
<el-button
size="small"
type="danger"
@click="handleDelete(scope.$index)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
const item = {
name: '',
birth: '',
province: "",
city: "",
address: '',
phone: "",
}
const header = {
prop: "key",
label: "自定义",
editable: false,
type: "input",
}
export default {
name: "DynamicModifyTable",
data() {
return {
tableHeader: [
{
prop: "name",
label: "姓名",
editable: false,
type: "input",
},
{
prop: "birth",
label: "生日",
editable: false,
type: "date"
},
{
prop: "phone",
label: "电话",
editable: false,
type: "input"
},
{
prop: "province",
label: "省份",
editable: false,
type: "input"
},
{
prop: "city",
label: "市区",
editable: false,
type: "input"
},
{
prop: "address",
label: "详细地址",
editable: false,
type: "input"
}
],
tableData: [{
name: '张三',
province: "上海市",
city: "普陀区",
address: "金沙江路 1518 弄",
birth: '2016-05-02',
phone: "12345678910",
}, {
name: '李四',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1517 弄',
age: 19,
phone: "12345678911",
}, {
name: '王五',
birth: '2016-05-01',
province: "上海市",
city: "普陀区",
address: '金沙江路 1519 弄',
phone: "12345678912",
}, {
name: '赵六',
birth: '2016-05-03',
province: "上海市",
city: "普陀区",
address: '金沙江路 1516 弄',
phone: "12345678913",
}]
}
},
methods: {
handleEdit(row) {
row.editable = true;
},
handleDelete(index) {
this.tableData.splice(index, 1);
},
prepend(index) {
item.editable = true
this.tableData.splice(index, 0, item);
},
append(index) {
item.editable = true
this.tableData.splice(index + 1, 0, item);
},
deleteCurrentColumn(index) {
this.tableHeader.splice(index, 1);
},
insertBefore(index) {
header.editable = true;
this.tableHeader.splice(index, 0, header);
Vue3 + Element Plus 创建动态多级表头<template>
<div>
<h2>Vue3 + Element plus 动态表格</h2>
<el-table :data="tableData" style="width: 100%">
<el-table-column
:prop="item.prop"
:label="item.label"
v-for="(item, index) in tableHeader"
:key="index"
>
<el-table-column
:prop="child.prop"
:label="child.label"
v-for="(child, childIndex) in item.children"
:key="childIndex"
></el-table-column>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: "MultiHeaderTable",
data() {
return {
tableHeader: [
{
prop: "name",
label: "姓名",
},
{
prop: "birth",
label: "生日",
},
{
prop: "phone",
label: "电话",
},
{
label: "地址",
children: [
{
prop: "province",
label: "省份",
},
{
prop: "city",
label: "市区",
},
{
prop: "address",
label: "详细地址",
}
]
}
],
tableData: [{
name: '张三',
province: "上海市",
city: "普陀区",
address: "金沙江路 1518 弄",
birth: '2016-05-02',
phone: "12345678910",
}, {
name: '李四',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1517 弄',
age: 19,
phone: "12345678911",
}, {
name: '王五',
birth: '2016-05-01',
province: "上海市",
city: "普陀区",
address: '金沙江路 1519 弄',
phone: "12345678912",
}, {
name: '赵六',
birth: '2016-05-03',
province: "上海市",
city: "普陀区",
address: '金沙江路 1516 弄',
phone: "12345678913",
}]
}
},
}
</script>
对于 tableHeader 的定义,我们通过 children 字段来指定当前列的二级表头,但是会发现效果并不是我们预期的那样:
<template v-if="item.children">
<el-table-column
:prop="child.prop"
:label="child.label"
v-for="(child, childIndex) in item.children"
:key="childIndex"
></el-table-column>
</template>
如果不这么做,那就只能渲染有二级表头的列,无法正常显示一级表头的数据, 最后的效果如下:
Vue3 + Element Plus 表格中单元格行合并
我们先来看下如何实现行合并,行合并或者是列合并,都需要用到 el-table 中 span-method 这个方法,在官方的例子中,是通过固定返回 rowspan,colspan 来实现行合并的:const objectSpanMethod = ({
row,
column,
rowIndex, // 需要合并的开始行
columnIndex, // 需要合并的列
}) => {
if (columnIndex === 0) {
if (rowIndex % 2 === 0) {
return {
rowspan: 2, //合并的行数
colspan: 1, //合并的列数
}
} else {
return {
rowspan: 0,
colspan: 0,
}
}
}
}
但是在动态数据的场景下,这种方法就不适用了,因为前端的表格数据往往是后端通过接口返回的。比如我们有以下数据结构:tableData: [{
name: '张三',
province: "上海市",
city: "普陀区",
address: "金沙江路 1518 弄",
birth: '2016-05-02',
phone: "12345678910",
}, {
name: '李四',
birth: '2016-05-02',
province: "上海市",
city: "普陀区",
address: '金沙江路 1517 弄',
age: 19,
phone: "12345678911",
}, {
name: '王五',
birth: '2016-05-03',
province: "上海市",
city: "普陀区",
address: '金沙江路 1519 弄',
phone: "12345678912",
}, {
name: '赵六',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1516 弄',
phone: "12345678913",
}, {
name: '孙七',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1516 弄',
phone: "12345678913",
}, {
name: '周八',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1516 弄',
phone: "12345678913",
}, {
name: '吴九',
birth: '2016-05-06',
province: "上海市",
city: "普陀区",
address: '金沙江路 1516 弄',
phone: "12345678913",
}]
我们的需求是把相同 birth 进行合并。在 components 目录下新建 RowMergeTable.vue 文件:<template>
<div>
<h2>Vue3 + Element plus 动态行合并表格</h2>
<el-table
:data="tableData"
style="width: 100%"
:span-method="objectSpanMethod"
border
>
<el-table-column
:prop="item.prop"
:label="item.label"
v-for="(item, index) in tableHeader"
:key="index"
></el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: "RowMergeTable",
data() {
this.spanArr = [];
return {
tableHeader: [
{
prop: "birth",
label: "生日",
},
{
prop: "name",
label: "姓名",
},
{
prop: "phone",
label: "电话",
},
{
prop: "province",
label: "省份",
},
{
prop: "city",
label: "市区",
},
{
prop: "address",
label: "详细地址",
}
],
tableData: [{
name: '张三',
province: "上海市",
city: "普陀区",
address: "金沙江路 1518 弄",
birth: '2016-05-02',
phone: "12345678910",
}, {
name: '李四',
birth: '2016-05-02',
province: "上海市",
city: "普陀区",
address: '金沙江路 1517 弄',
age: 19,
phone: "12345678911",
}, {
name: '王五',
birth: '2016-05-03',
province: "上海市",
city: "普陀区",
address: '金沙江路 1519 弄',
phone: "12345678912",
}, {
name: '赵六',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1520 弄',
phone: "12345678913",
}, {
name: '孙七',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1521 弄',
phone: "12345678913",
}, {
name: '周八',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1522 弄',
phone: "12345678913",
}, {
name: '吴九',
birth: '2016-05-06',
province: "上海市",
city: "普陀区",
address: '金沙江路 1523 弄',
phone: "12345678913",
}]
}
},
created() {
this.getSpanArr(this.tableData);
},
methods: {
getSpanArr(data) {
for (var i = 0; i < data.length; i++) {
if (i === 0) {
this.spanArr.push(1);
this.pos = 0
} else {
if (data[i].birth === data[i - 1].birth) {
this.spanArr[this.pos] += 1;
this.spanArr.push(0);
} else {
this.spanArr.push(1);
this.pos = i;
}
}
}
},
objectSpanMethod({ rowIndex, columnIndex }) {
if (columnIndex === 0) {
const _row = this.spanArr[rowIndex];
const _col = _row > 0 ? 1 : 0;
return {
rowspan: _row,
colspan: _col
}
}
}
}
}
</script>
上面的例子就实现了行合并的功能:
<template>
<div>
<h2>Vue3 + Element plus 动态多行合并表格</h2>
<el-table
:data="tableData"
style="width: 100%"
:span-method="objectSpanMethod"
border
>
<el-table-column
:prop="item.prop"
:label="item.label"
v-for="(item, index) in tableHeader"
:key="index"
></el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: "MultiRowMergeTable",
data() {
this.spanMap = {};
this.mergedColumns = ["birth", "province", "city"]
return {
tableHeader: [
{
prop: "birth",
label: "生日",
},
{
prop: "name",
label: "姓名",
},
{
prop: "phone",
label: "电话",
},
{
prop: "province",
label: "省份",
},
{
prop: "city",
label: "市区",
},
{
prop: "address",
label: "详细地址",
}
],
tableData: [{
name: '张三',
province: "上海市",
city: "普陀区",
address: "金沙江路 1518 弄",
birth: '2016-05-02',
phone: "12345678910",
}, {
name: '李四',
birth: '2016-05-02',
province: "上海市",
city: "普陀区",
address: '金沙江路 1517 弄',
age: 19,
phone: "12345678911",
}, {
name: '王五',
birth: '2016-05-03',
province: "上海市",
city: "普陀区",
address: '金沙江路 1519 弄',
phone: "12345678912",
}, {
name: '赵六',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1520 弄',
phone: "12345678913",
}, {
name: '孙七',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1521 弄',
phone: "12345678913",
}, {
name: '周八',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1522 弄',
phone: "12345678913",
}, {
name: '吴九',
birth: '2016-05-06',
province: "上海市",
city: "普陀区",
address: '金沙江路 1523 弄',
phone: "12345678913",
}]
}
},
created() {
this.getSpanArr(this.tableData);
},
methods: {
getSpanArr(data) {
for (var i = 0; i < data.length; i++) {
if (i === 0) {
this.mergedColumns.forEach(column => {
this.spanMap[column] = {
spanArr: [1],
pos: 0
}
})
} else {
this.mergedColumns.forEach(column => {
if (data[i][column] === data[i - 1][column]) {
this.spanMap[column].spanArr[this.spanMap[column].pos] += 1;
this.spanMap[column].spanArr.push(0)
} else {
this.spanMap[column].spanArr.push(1);
this.spanMap[column].pos = i;
}
})
}
}
},
objectSpanMethod({ column, rowIndex }) {
if (this.spanMap[column.property]) {
const _row = this.spanMap[column.property].spanArr[rowIndex];
const _col = _row > 0 ? 1 : 0;
return {
rowspan: _row,
colspan: _col
}
}
}
}
}
</script>
实现效果如下:
<template>
<div>
<h2>Vue3 + Element plus 动态表格列合并</h2>
<el-table
:data="tableData"
style="width: 100%"
:span-method="objectSpanMethod"
border
>
<el-table-column
:prop="item.prop"
:label="item.label"
v-for="(item, index) in tableHeader"
:key="index"
></el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: "ColumnMergeTable",
data() {
return {
tableHeader: [
{
prop: "birth",
label: "生日",
},
{
prop: "name",
label: "姓名",
},
{
prop: "phone",
label: "电话",
},
{
prop: "province",
label: "省份",
},
{
prop: "city",
label: "市区",
},
{
prop: "address",
label: "详细地址",
}
],
tableData: [{
name: '张三',
province: "上海市",
city: "普陀区",
address: "金沙江路 1518 弄",
birth: '2016-05-02',
phone: "12345678910",
}, {
name: '李四',
birth: '2016-05-02',
province: "上海市",
city: "普陀区",
address: '金沙江路 1517 弄',
age: 19,
phone: "12345678911",
}, {
name: '王五',
birth: '2016-05-03',
province: "上海市",
city: "普陀区",
address: '金沙江路 1519 弄',
phone: "12345678912",
}, {
name: '赵六',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1520 弄',
phone: "12345678913",
}, {
name: '孙七',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1521 弄',
phone: "12345678913",
}, {
name: '周八',
birth: '2016-05-04',
province: "上海市",
city: "普陀区",
address: '金沙江路 1522 弄',
phone: "12345678913",
}, {
name: '吴九',
birth: '2016-05-06',
province: "上海市",
city: "普陀区",
address: '金沙江路 1523 弄',
phone: "12345678913",
}]
}
},
methods: {
objectSpanMethod({ rowIndex, columnIndex }) {
// 隐藏第二行或者第三行的列
if (rowIndex === 1 || rowIndex === 2) {
// 合并第二行
if (columnIndex === 1) {
// 从第二列开始
return [1, 3]
//或者返回如下形式也可以
// return {
// rowspan: 1,
// colspan: 3
// }
// 这里的 else if 即使用来处理被合并列的原始数据的情况,需要隐藏原始单元格
} else if (columnIndex === 2 || columnIndex === 3) {
return [0, 0]
}
}
}
}
}
</script>
可以看出来,列合并其实比行合并简单一些,返回一个数组更容易理解一些,数组的第一项表示合并的起始列,第二项表示合并的终止列,其区间的所有列都会合并成一列,被合并的列还需要通过 [0, 0] 来隐藏对应的单元格,这个是和行合并不同的地方。