效果如下
实现原理,结合elementUI table 的 span-method方法。
1.table.vue
<template>
<div>
<div class="topbox">
<div class="topinfo">
<el-select v-model="status">
<el-option
v-for="item in statusoptions"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</div>
<div class="topinfo">
<el-button type="primary" plain>搜索</el-button>
</div>
</div>
<el-table
:data="tableData"
style="width: 100%; margin-top: 10px"
height="calc(100vh - 280px)"
border
ref="table"
:span-method="spanMethod"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
>
<el-table-column prop="ddinfo" label="督导内容">
<template slot-scope="scope">
<div class="scopebox">
<el-input
v-model="scope.row.date"
placeholder="请输入内容"
></el-input>
<div class="scopebtnbox">
<div class="el-icon-circle-plus-outline" @click="addtable"></div>
<div
v-if="tableData.length > 1"
class="el-icon-remove-outline"
@click="deteletable(scope.$index)"
></div>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="ddbz" label="督导标准">
<template slot-scope="scope">
<div class="scopebox">
<el-input
v-model="scope.row.ddinfo"
placeholder="请输入内容"
></el-input>
<div class="scopebtnbox">
<div
class="el-icon-circle-plus-outline"
@click="addrow(scope.row)"
></div>
<div
class="el-icon-remove-outline"
v-if="tableData.length > 1"
@click="detelerow(scope.row, scope.$index)"
></div>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="info" label="详情"></el-table-column>
</el-table>
<el-pagination
style="margin-top: 10px"
background
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-size="pagesize"
layout="total, prev, pager, next"
:total="total"
></el-pagination>
</div>
</template>
<script>
import { uuid } from '@/utils'
import { getSpanMethod } from './method.js'
export default {
data() {
return {
status: '全部',
statusoptions: [
{
label: '全部',
value: '全部'
},
{
label: '未整改',
value: '未整改'
},
{
label: '已整改',
value: '已整改'
}
],
currentPage: 1,
pagesize: 15,
total: 0,
tableData: [
{
id: '1',
ddinfo: '',
ddbz: '',
info: ''
}
]
}
},
computed: {
spanMethod: {
get() {
return (this.spanMethod = getSpanMethod(
this.tableData,
['id'],
['ddinfo']
))
},
set(val) {
return val
}
}
},
created() {},
methods: {
//排序
sortArr(arr, str) {
var _arr = [] //最终的二维数组
//取出所有要合并的属性名
let strArr = arr.map((item) => {
return item[str]
})
//对属性名进行一次去重
strArr = Array.from(new Set(strArr))
//先遍历属性名数组
strArr.map((strItem) => {
let temporaryArr = []
//遍历所有的数组
arr.forEach((objectItem) => {
//如果 strItem 和数组中的属性名相同就push进临时数组
if (strItem == objectItem[str]) {
temporaryArr.push(objectItem)
}
})
//在数组循环结束后,将临时数组push进二维数组中
_arr.push(temporaryArr)
})
return _arr
},
//新增表格某项
addrow(e) {
console.log(e)
console.log(this.tableData)
let obj = {
ddbz: '',
ddinfo: '',
id: e.id,
info: ''
}
this.tableData.push(obj)
//排序
let arr = this.sortArr(this.tableData, 'id')
console.log(arr.flat())
this.tableData = arr.flat()
},
//删除表格项
detelerow(e, i) {
this.tableData.splice(i, 1)
},
//新增表格行
addtable() {
this.tableData.push({
id: uuid(),
ddinfo: '',
ddbz: '',
info: ''
})
//排序
let arr = this.sortArr(this.tableData, 'id')
this.tableData = arr.flat()
},
//删除表格行
deteletable(e) {
this.tableData.splice(e, 1)
},
handleCurrentChange(e) {
this.currentPage = e
}
}
}
</script>
<style lang="less" scoped>
.scopebox {
display: flex;
align-items: center;
.scopebtnbox {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-left: 5px;
div {
cursor: pointer;
margin: 5px 0px;
}
}
}
.topbox {
display: flex;
align-items: center;
.topinfo {
margin-right: 10px;
}
}
</style>
2.合并单元格 method.js 文件
/**
* 合并相同数据,导出合并行所需的方法(只适合el-table)
* @param {Array} dataArray el-table表数据源
* @param {Array} mergeRowProp 合并行的列prop
* @param {Array} sameRuleRowProp 相同合并规则行的列prop
*/
export function getSpanMethod(dataArray, mergeRowProp, sameRuleRowProp) {
/**
* 要合并行的数据
*/
const rowspanNumObject = {}
//初始化 rowspanNumObject
mergeRowProp.map((item) => {
rowspanNumObject[item] = new Array(dataArray.length)
.fill(1, 0, 1)
.fill(0, 1)
rowspanNumObject[`${item}-index`] = 0
})
//计算相关的合并信息
for (let i = 1; i < dataArray.length; i++) {
mergeRowProp.map((key) => {
const index = rowspanNumObject[`${key}-index`]
if (dataArray[i][key] === dataArray[i - 1][key]) {
rowspanNumObject[key][index]++
} else {
rowspanNumObject[`${key}-index`] = i
rowspanNumObject[key][i] = 1
}
})
}
/**
* 添加同规则合并行的数据
*/
if (sameRuleRowProp !== undefined) {
let k = Object.keys(rowspanNumObject).filter((key) => {
if (!key.includes('index')) {
return key
}
})[0]
for (let prop of sameRuleRowProp) {
rowspanNumObject[prop] = rowspanNumObject[k]
rowspanNumObject[`${prop}-index`] = rowspanNumObject[`${k}-index`]
mergeRowProp.push(prop)
}
}
/**
* 导出合并方法
*/
const spanMethod = function ({ row, column, rowIndex, columnIndex }) {
if (mergeRowProp.includes(column['property'])) {
const rowspan = rowspanNumObject[column['property']][rowIndex]
if (rowspan > 0) {
return { rowspan: rowspan, colspan: 1 }
}
return { rowspan: 0, colspan: 0 }
}
return { rowspan: 1, colspan: 1 }
}
return spanMethod
}