一、分析需求
- 这里先上一张图说明
需求
:
根据后端返回的数据 (res
是一个数组,它的元素是一个对象,对象里面的ext
属性是一个对象,它又包含了,default
、free
和pay
三个属性,且这三个都是数组格式。):
- 渲染出一个这样子的
表格
:
res
数据:
res
的每一个元素的直接属性name
(即为邮费模板名称,比如成都运费模板),res
的ext
属性下的三个数组default
、free
、pay
,每一个数组要大的一行(这一行中,第一列是运送到的地址的名字,这里定义的是area
属性,但后端是未给到这个字段的,可自己处理数据添加该字段 ,这里就不细说了。) 这个area
属性占据的这一列,在页面的展示效果 应该是多行合并的效果。
二、代码实现:
<template>
<div class="layout">
<el-table :data="res" >
<el-table-column prop="name">
<template slot-scope="scope">
<div class="tab_header">
<span style="font-weight:600;">{{scope.row.name}}</span>
<div class="operate">
<span @click="handleEdit(scope.$index, scope.row)">修改</span>
<span @click="handleDelete(scope.$index, scope.row)">删除</span>
</div>
</div>
<!-- 这里要实现 多个表格共用一个表头,故需做判断,当表格要渲染的数据为default这个数组的时候,才显示表头的label值 -->
<!-- 注意:当label无值的时候,还是会占用空间,故当前表格在页面上会出现一个代表表头的空行,需要手动更改(重写)Element表格的 thead样式 -->
<div v-for="item in (scope.row.ext)" :key="item.id">
<el-table :data="item" border :class="item!==scope.row.ext.default?'tab-thead-style':''" style="box-sizing: border-box;border-top:none;" :span-method="objectSpanMethod">
<el-table-column :label="item===scope.row.ext.default?'运送到':''" prop="area"></el-table-column>
<el-table-column :label="item===scope.row.ext.default?'首重':''" prop="weight"></el-table-column>
<el-table-column :label="item===scope.row.ext.default?'运费':''" prop="first_price"></el-table-column>
<el-table-column :label="item===scope.row.ext.default?'续重':''" prop="weight_incre"></el-table-column>
<el-table-column :label="item===scope.row.ext.default?'最终运费':''" prop="extend_price"></el-table-column>
</el-table>
</div>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data () {
return {
// res 参考的是后端返回的数据格式,
res: [
{
id: 1,
dealer_id: 0,
name: '成都运费模板',
type: 1,
ext: {
default: [{ area: '默认', type: 1, region: '1', weight: '首重d', weight_incre: '续重d', first_price: '运费d', extend_price: '最终运费d' }],
free: [{ area: 'free', type: 1, region: '1', weight: '首重f', weight_incre: '续重f', first_price: '运费f', extend_price: '最终运费f' }, { area: 'free', type: 1, region: '1', weight: '首重f', weight_incre: '续重f', first_price: '运费f', extend_price: '最终运费f' }],
pay: [{ area: 'pay', type: 1, region: '1', weight: '首重p', weight_incre: '续重p', first_price: '运费p', extend_price: '最终运费p' }, { area: 'pay', type: 1, region: '1', weight: '首重p', weight_incre: '续重p', first_price: '运费p', extend_price: '最终运费p' }, { area: 'pay', type: 1, region: '1', weight: '首重p', weight_incre: '续重p', first_price: '运费p', extend_price: '最终运费p' }]
}
},
{
id: 2,
dealer_id: 0,
name: '重庆运费模板',
type: 2,
ext: {
default: [{ area: '默认1', type: 1, region: '1', weight: '首重d', weight_incre: '续重d', first_price: '运费d', extend_price: '最终运费d' }],
free: [{ area: 'free1', type: 1, region: '1', weight: '首重f', weight_incre: '续重f', first_price: '运费f', extend_price: '最终运费f' }, { area: 'free', type: 1, region: '1', weight: '首重f', weight_incre: '续重f', first_price: '运费f', extend_price: '最终运费f' }],
pay: [{ area: 'pay1', type: 1, region: '1', weight: '首重p', weight_incre: '续重p', first_price: '运费p', extend_price: '最终运费p' }, { area: 'pay', type: 1, region: '1', weight: '首重p', weight_incre: '续重p', first_price: '运费p', extend_price: '最终运费p' }, { area: 'pay', type: 1, region: '1', weight: '首重p', weight_incre: '续重p', first_price: '运费p', extend_price: '最终运费p' }, { area: 'pay1', type: 1, region: '1', weight: '首重p', weight_incre: '续重p', first_price: '运费p', extend_price: '最终运费p' }, { area: 'pay', type: 1, region: '1', weight: '首重p', weight_incre: '续重p', first_price: '运费p', extend_price: '最终运费p' }, { area: 'pay', type: 1, region: '1', weight: '首重p', weight_incre: '续重p', first_price: '运费p', extend_price: '最终运费p' }]
}
}
]
}
},
methods: {
handleEdit (index, row) {
console.log(index, row)
},
handleDelete (index, row) {
console.log(index, row)
},
objectSpanMethod ({ row, column, rowIndex, columnIndex }) {
if (columnIndex === 0) {
if (rowIndex === 0) {
let maxLen
this.res.forEach(val => {
const arr = [val.ext.default.length, val.ext.free.length, val.ext.pay.length]
arr.sort((a, b) => a - b)// arr数组 按数字大小从小到大排序
maxLen = arr.pop()// 取出排序后的数组arr中的最后一个元素
})
return {
// 这个rowspan应该据 ext的default,pay,free的长度不同来定,取最大长度
rowspan: maxLen,
colspan: 1
}
} else {
return {
rowspan: 0,
colspan: 0
}
}
}
}
}
}
</script>
<style lang="scss">
.layout{
.tab_header{
color:#333;
padding:0 5px 0 5px;
height:45px;
line-height:45px;
border:1px solid #eee;display:flex;
justify-content: space-between;
background:rgb(233, 225, 225);
}
.operate{
span{
font-size: 14px;
margin-right: 20px;
margin-right:20px;
color:#409EFF;
cursor: pointer;
}
}
/* 处理多个表格共用一个表头时,表头处出现多余空行的问题 (label置空后还是占据空间问题) */
.tab-thead-style{
thead{
display: none;
}
}
}
</style>
三、知识点总结:
-
为什么要采用这种方式解决(渲染)?
① . 项目用的UI组件是
Element
,它的Table表格组件,没有直接处理行的操作。② .
el-table
,它是通过注入data
对象数组,并在el-table-column
中用prop
属性来对应对象中的键名来填入数据,从而渲染出渲染表格。其中el-table-column
表示一个列,label
属性来定义表格的列名,即对象的一个键名代表一列;③ . 没想到更优的解决办法,O(∩_∩)O哈哈~
-
多个表格共用一个表头时,注意:
①. 需做判断,同时注意
label
的值。②. 当
el-table-column
的属性label
无值的时候,还是会占用空间,故当前表格在页面上会出现一个代表表头的空行,需要手动更改(重写)Element
表格的thead
样式 -
table表格嵌套的时候,注意:
① .
Element
的Table
组件可 自定义列模板,主要是利用它实现表格嵌套部分,通过Scoped slot
可以获取到row, column, $index
和store
(table 内部的状态管理)的数据,更多用法参考官网。②.
Element
的Table
组件可 合并行或列 ,多行或多列共用一个数据时,可以合并行或列;通过给table
传入span-method
方法可以实现合并行或列,参考上述代码的 **objectSpanMethod
**方法(该表格的第一列需要合并多行——合并渲染表格的data
数组的长度那么多行) 或者官网。
后期更新
**`更新:`**
代码实现处,基本思路是对的,但绑定tab-thead-style
类以及用三元运算符确定label的地方实属没必要,这里的本质是 是否展示表头的问题
,用show-header
属性即可实现,之前没细看文档,别像我学习emo。。。 所以现将代码实现更改如下:
- 去除了
tab-thead-style
类名的定义和class的绑定 - 去除了label相关的判定
- 给table增加了
show-header
的属性,true,显示表头,false则不显示表头。
<template>
<div class="layout">
<el-table :data="res">
<el-table-column prop="name">
<template slot-scope="scope">
<div class="tab_header">
<span style="font-weight: 600">{{ scope.row.name }}</span>
<div class="operate">
<span @click="handleEdit(scope.$index, scope.row)">修改</span>
<span @click="handleDelete(scope.$index, scope.row)">删除</span>
</div>
</div>
<!-- 这里要实现 多个表格共用一个表头,故需做判断,当表格要渲染的数据为default这个数组的时候,才显示表头的label值,即显示表头,设置show-header属性为true -->
<div v-for="item in scope.row.ext" :key="item.id">
<el-table
:data="item"
border
:class="item !== scope.row.ext.default ? 'tab-thead-style' : ''"
style="box-sizing: border-box; border-top: none"
:span-method="objectSpanMethod"
:show-header="item === scope.row.ext.default"
>
<el-table-column label="运送到" prop="area"></el-table-column>
<el-table-column label="首重" prop="weight"></el-table-column>
<el-table-column
label="运费"
prop="first_price"
></el-table-column>
<el-table-column
label="续重"
prop="weight_incre"
></el-table-column>
<el-table-column
label="最终运费"
prop="extend_price"
></el-table-column>
</el-table>
</div>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
// res 参考的是后端返回的数据格式,
res: [
{
id: 1,
dealer_id: 0,
name: '成都运费模板',
type: 1,
ext: {
default: [
{
area: '默认',
type: 1,
region: '1',
weight: '首重d',
weight_incre: '续重d',
first_price: '运费d',
extend_price: '最终运费d',
},
],
free: [
{
area: 'free',
type: 1,
region: '1',
weight: '首重f',
weight_incre: '续重f',
first_price: '运费f',
extend_price: '最终运费f',
},
{
area: 'free',
type: 1,
region: '1',
weight: '首重f',
weight_incre: '续重f',
first_price: '运费f',
extend_price: '最终运费f',
},
],
pay: [
{
area: 'pay',
type: 1,
region: '1',
weight: '首重p',
weight_incre: '续重p',
first_price: '运费p',
extend_price: '最终运费p',
},
{
area: 'pay',
type: 1,
region: '1',
weight: '首重p',
weight_incre: '续重p',
first_price: '运费p',
extend_price: '最终运费p',
},
{
area: 'pay',
type: 1,
region: '1',
weight: '首重p',
weight_incre: '续重p',
first_price: '运费p',
extend_price: '最终运费p',
},
],
},
},
{
id: 2,
dealer_id: 0,
name: '重庆运费模板',
type: 2,
ext: {
default: [
{
area: '默认1',
type: 1,
region: '1',
weight: '首重d',
weight_incre: '续重d',
first_price: '运费d',
extend_price: '最终运费d',
},
],
free: [
{
area: 'free1',
type: 1,
region: '1',
weight: '首重f',
weight_incre: '续重f',
first_price: '运费f',
extend_price: '最终运费f',
},
{
area: 'free',
type: 1,
region: '1',
weight: '首重f',
weight_incre: '续重f',
first_price: '运费f',
extend_price: '最终运费f',
},
],
pay: [
{
area: 'pay1',
type: 1,
region: '1',
weight: '首重p',
weight_incre: '续重p',
first_price: '运费p',
extend_price: '最终运费p',
},
{
area: 'pay',
type: 1,
region: '1',
weight: '首重p',
weight_incre: '续重p',
first_price: '运费p',
extend_price: '最终运费p',
},
{
area: 'pay',
type: 1,
region: '1',
weight: '首重p',
weight_incre: '续重p',
first_price: '运费p',
extend_price: '最终运费p',
},
{
area: 'pay1',
type: 1,
region: '1',
weight: '首重p',
weight_incre: '续重p',
first_price: '运费p',
extend_price: '最终运费p',
},
{
area: 'pay',
type: 1,
region: '1',
weight: '首重p',
weight_incre: '续重p',
first_price: '运费p',
extend_price: '最终运费p',
},
{
area: 'pay',
type: 1,
region: '1',
weight: '首重p',
weight_incre: '续重p',
first_price: '运费p',
extend_price: '最终运费p',
},
],
},
},
],
}
},
methods: {
handleEdit(index, row) {
console.log(index, row)
},
handleDelete(index, row) {
console.log(index, row)
},
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
if (columnIndex !== 0) return
if (rowIndex !== 0) {
return {
rowspan: 0,
colspan: 0,
}
}
let maxLen
this.res.forEach((val) => {
const arr = [
val.ext.default.length,
val.ext.free.length,
val.ext.pay.length,
]
arr.sort((a, b) => a - b) // arr数组 按数字大小从小到大排序
maxLen = arr.pop() // 取出排序后的数组arr中的最后一个元素
})
return {
// 这个rowspan应该据 ext的default,pay,free的长度不同来定,取最大长度
rowspan: maxLen,
colspan: 1,
}
},
},
}
</script>
<style lang="scss">
.layout {
.tab_header {
color: #333;
padding: 0 5px 0 5px;
height: 45px;
line-height: 45px;
border: 1px solid #eee;
display: flex;
justify-content: space-between;
background: rgb(233, 225, 225);
}
.operate {
span {
font-size: 14px;
margin-right: 20px;
margin-right: 20px;
color: #409eff;
cursor: pointer;
}
}
}
</style>