el-teable表格动态列数渲染及详解

📝 个人简介

⭐ 个人主页:我是段段🙋‍
🍊 博客领域:编程基础、前端💻
🍅 写作风格:干货!干货!都是干货!
🍑 精选专栏:Vue
🛸 支持段段:点赞👍、收藏⭐、留言💬

前言

在项目中编写数据大屏的时候,除了柱状图、折线图、散点图…,还有表格展示,首先想到的是Elementel-table表格,但是在后续的实现过程中,后端范围的列数是动态的,刚开始没有考虑到el-table的slot,然后采用了第一种方式,但是数据处理方式不太好,后来在Element官网查看了文档,就使用solt方式进行实现(第二种方式),不过两种方式都需要对数据进行处理

第一种方式

第一种方式主要使用v-for循环实现,页面逻辑比较简单,具体布局如下:

<template>
    <div class="echarts-table">
        <!-- 用来渲染表格头部 -->
        <div class="table-title">
            <div 
                v-for="(item, index) in colsArr" 
                :key="index"
                :style="initStyle()"
                :title="item"
            >
                <span>{{ item }}</span>
            </div>
        </div>
        <!-- 此处for循环用来渲染行数 -->
        <div 
            v-for="(item, index) in initDataList" 
            :key="index"
            class="table-content"
            :class="{ 'odd-number': index % 2 != 0 }"
        >
            <!-- 此处for循环用来渲染列数 -->
            <div 
                v-for="(prop, idx) in item" 
                :key="idx"
                :style="initStyle()" 
                :title="idx != 0 ? prop : ''"
            >
                <div v-if="idx == 0">
                    <span class="icon">{{ prop }}</span>
                </div>
                <span v-else>{{ prop }}</span>
            </div>
        </div>
    </div>
</template>

data中数据如下:

data() {
    return {
        // 表头 单独存放一个数组
        colsArr: ['排名', '区域', '集备主题', '评论次数', '参与教师数'],
        // 数据 需要将数据处理成此种形式 dataList中的每一个子项即是一行数据
        dataList: [
            ['xxx区域区域', '春/朱自清', '1', '5'],
            ['xxx区域', '济南的冬天/老舍', '8', '24'],
            ['xxx区域', '大榕树', '2', '18'],
            ['xxx区域', '你猜', '20', '18'],
            ['xxx区域', '大榕树', '12', '18'],
        ]
    };
}

使用计算属性来处理数据,将数据处理成渲染需要的格式,如下:

computed: {
    // 在数据的第一列是排名,项目中使用的是图片,且不是后端返回的,需要对每一行的数据进行处理
    initDataList() {
        this.dataList.forEach( (item, index) => {
            item.unshift(index + 1);
        })
        return this.dataList;
    }
}

因为列数不固定,需要每一列的宽度也需要动态的计算,通过动态style来实现,具体如下:

/*
 * 通过表头的长度来动态计算每一个列的宽度,此种方式在小程序端不可使用
 */
initStyle() {
    return { 
        width: `calc(100% / ${this.colsArr.length})`,
        margin: '0 0.4%',
        overflow: 'hidden',
        whiteSpace: 'nowrap',
        textOverflow: 'ellipsis'
    }
}

实现效果图如下:
在这里插入图片描述
完整代码附上:

<template>
    <div class="echarts-table">
        <!-- 用来渲染表格头部 -->
        <div class="table-title">
            <div 
                v-for="(item, index) in colsArr" 
                :key="index"
                :style="initStyle()"
                :title="item"
            >
                <span>{{ item }}</span>
            </div>
        </div>
        <!-- 此处for循环用来渲染行数 -->
        <div 
            v-for="(item, index) in initDataList" 
            :key="index"
            class="table-content"
            :class="{ 'odd-number': index % 2 != 0 }"
        >
            <!-- 此处for循环用来渲染列数 -->
            <div 
                v-for="(prop, idx) in item" 
                :key="idx"
                :style="initStyle()" 
                :title="idx != 0 ? prop : ''"
            >
                <div v-if="idx == 0">
                    <span class="icon">{{ prop }}</span>
                </div>
                <span v-else>{{ prop }}</span>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            // 表头 单独存放一个数组
            colsArr: ['排名', '区域', '集备主题', '评论次数', '参与教师数'],
            // 数据 需要将数据处理成此种形式 dataList中的每一个子项即是一行数据
            dataList: [
                ['xxx区域区域', '春/朱自清', '1', '5'],
                ['xxx区域', '济南的冬天/老舍', '8', '24'],
                ['xxx区域', '大榕树', '2', '18'],
                ['xxx区域', '你猜', '20', '18'],
                ['xxx区域', '大榕树', '12', '18'],
            ]
        };
    },
    computed: {
        // 在数据的第一列是排名,项目中使用的是图片,且不是后端返回的,需要对每一行的数据进行处理
        initDataList() {
            this.dataList.forEach( (item, index) => {
                item.unshift(index + 1);
            })
            return this.dataList;
        }
    },
    methods: {
        /*
         * 通过表头的长度来动态计算每一个列的宽度,此种方式在小程序端不可使用
         */
        initStyle() {
            return { 
                width: `calc(100% / ${this.colsArr.length})`,
                margin: '0 0.4%',
                overflow: 'hidden',
                whiteSpace: 'nowrap',
                textOverflow: 'ellipsis'
            }
        }
    }
};
</script>

<style lang="stylus" scoped>
.echarts-table 
    color #FFF
    cursor default
    .table-title 
        width 100%
        height 40px
        font-size 14px
        background #212949
        display flex
        align-items center
        text-align center
    .table-content 
        width 100%
        height 40px
        font-size 14px
        display flex
        align-items center
        text-align center
        img 
            height 20px
            vertical-align middle
        span.icon 
            display inline-block
            width 20px
            height 20px
            line-height 20px
            text-align center
            border 1px solid #FFFFFF
            border-radius 50%
    .odd-number 
        background #212949
</style>

第二种方式

第二种方式主要使用了Element的slot属性,也需要把数据处理成渲染需要的格式,相对来说比第一种方式稍复杂,页面布局如下:

<template>
    <div class="element-table">
        <el-table
            :data="initDataList"
            style="width: 100%"
            :header-cell-style="{ background: '#212949' }"
        >
            <el-table-column
                v-for="(item, index) in colsArr"
                :key="index"
                :label="item.colName"
                align="center"
                :show-overflow-tooltip="true"
                min-width="60"
            >
                <template slot-scope="scope">
                    <div v-if="index == 0">
                        <span class="icon">{{ scope.row[item.key].value }}</span>
                    </div>
                    <span v-else>{{ scope.row[item.key].value }}</span>
                </template>
            </el-table-column>
        </el-table>
    </div>
</template>

data中的数据如下:

data() {
    return {
        /**
          * @params
          * colName 表头名称
          * key 自定义列名 处理数据是使用
          */
        colsArr: [
            { colName: '排名', key: 'cols0' },
            { colName: '区域', key: 'cols1' },
            { colName: '主题名称', key: 'cols2' },
            { colName: '次数', key: 'cols3' },
            { colName: '人数', key: 'cols4' }
        ],
        /**
          * @params
          * 此处的列名必须和colsArr中定义的一致
          */
        dataList: [
            { cols1: '区域1', cols2: '春', cols3: 1, cols4: 5 },
            { cols1: '区域2', cols2: '春', cols3: 8, cols4: 24 },
            { cols1: '区域3', cols2: '春', cols3: 18, cols4: 52 },
            { cols1: '区域4', cols2: '春', cols3: 1, cols4: 35 },
            { cols1: '区域5', cols2: '春', cols3: 1, cols4: 20 }
        ]
    };
}

也需要计算属性来处理数据:如下:

computed: {
    // 处理数据
    initDataList() {
        let arr = [];
        this.dataList.forEach((item, index) => {
            let obj = {};
            this.colsArr.forEach((prop, idx) => {
                if(idx == 0) {
                    obj[prop.key] = { value: index + 1, name: prop.colName };
                } else {
                    obj[prop.key] = { value: item[prop.key], name: prop.colName };
                }
            })
            arr.push(obj);
        })
        return arr;
    }
}

实现效果图如下:
在这里插入图片描述

完整代码附上:

<template>
    <div class="element-table">
        <el-table
            :data="initDataList"
            style="width: 100%"
            :header-cell-style="{ background: '#212949' }"
        >
            <el-table-column
                v-for="(item, index) in colsArr"
                :key="index"
                :label="item.colName"
                align="center"
                :show-overflow-tooltip="true"
                min-width="60"
            >
                <template slot-scope="scope">
                    <div v-if="index == 0">
                        <span class="icon">{{ scope.row[item.key].value }}</span>
                    </div>
                    <span v-else>{{ scope.row[item.key].value }}</span>
                </template>
            </el-table-column>
        </el-table>
    </div>
</template>

<script>
export default {
    data() {
        return {
            /**
             * @params
             * colName 表头名称
             * key 自定义列名 处理数据是使用
             */
            colsArr: [
                { colName: '排名', key: 'cols0' },
                { colName: '区域', key: 'cols1' },
                { colName: '主题名称', key: 'cols2' },
                { colName: '次数', key: 'cols3' },
                { colName: '人数', key: 'cols4' }
            ],
            /**
             * @params
             * 此处的列名必须和colsArr中定义的一致
             */
            dataList: [
                { cols1: '区域1', cols2: '春', cols3: 1, cols4: 5 },
                { cols1: '区域2', cols2: '春', cols3: 8, cols4: 24 },
                { cols1: '区域3', cols2: '春', cols3: 18, cols4: 52 },
                { cols1: '区域4', cols2: '春', cols3: 1, cols4: 35 },
                { cols1: '区域5', cols2: '春', cols3: 1, cols4: 20 }
            ]
        };
    },
    computed: {
        // 处理数据
        initDataList() {
            let arr = [];
            this.dataList.forEach((item, index) => {
                let obj = {};
                this.colsArr.forEach((prop, idx) => {
                    if(idx == 0) {
                        obj[prop.key] = { value: index + 1, name: prop.colName };
                    } else {
                        obj[prop.key] = { value: item[prop.key], name: prop.colName };
                    }
                })
                arr.push(obj);
            })
            return arr;
        }
    }
};
</script>

<style lang="stylus" scoped>
.element-table {
    /deep/ .el-table {
        background-color: rgba(0, 0, 0, 0);
    }
    /deep/ .el-table::before {
        height: 0;
    } 
    /deep/ .el-table th.el-table__cell,
    /deep/ .el-table td.el-table__cell {
        border-bottom: none;
        padding: 0;
        color: #FFF;
        font-size: 14px;
    }
    /deep/ .el-table .el-table__header-wrapper {
        height: 40px;
        line-height: 40px;
    }

    /deep/ .el-table .el-table__header-wrapper .cell {
        white-space: nowrap;
        text-overflow: ellipsis;
    }
    /deep/ .el-table .el-table__row {
        height: 40px;
        background: #182041;
        img {
            height: 20px;
            vertical-align: middle;
        }
        &:nth-child(2),
        &:nth-child(4) {
            background: #212949;
        }
    }
    /deep/ .el-table tbody tr:hover > td {
      background: rgba(0, 0, 0, 0);
    }
    /deep/ .el-table__empty-block {
        background: #182041;
    }
    span.icon {
        display: inline-block;
        width: 20px;
        height: 20px;
        line-height: 20px;
        text-align: center;
        border: 1px solid #FFFFFF;
        border-radius: 2px;
    }
}
</style>

总之,两种方式的渲染结果没有明显的差异,具体使用那种方式需要根据业务需求决定

好长时间没更新了,最近太忙了呀⏳~


分割线~~~


来补更一个吧~,在项目中用到了sortable属性进行排序,以上两种方式都存在问题

第三种方式

也是使用了Element的slot属性,需要把数据处理成渲染需要的格式,处理起来相对简单

将表格封装成了组件NewsTable.vue,页面布局如下:

<template>
  <div class="news-table">
    <el-table
      :max-height="config.max_height"
      :data="config.table_data"
      style="width: 100%"
      :header-cell-style="{ background: '#212949' }"
    >
      <el-table-column
        align="center"
        v-for="(item, index, key) in config.table_cols"
        :key="key"
        :index="index"
        :label="item.label"
        :prop="item.prop"
        :width="item.width"
        :fixed="item.fixed"
        :show-overflow-tooltip="true"
        :sortable="item.sort"
      >
        <template slot-scope="scope">
          <span>{{ scope.row[item.prop] }}</span>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

在需要用到表格的地方引入即可

import NewTable from '../components/Common/NewsTable.vue';

然后在页面使用

<NewTable :config="tableConfig"></NewTable>

tableConfig中的数据如下

tableConfig: {
        max_height: 240,
        table_cols: [
          { prop: 'rank', label: '排名', width: '', fixed: false, sort: false },
          { prop: 'area', label: '区域', width: '', fixed: false, sort: false },
          { prop: 'theme', label: '集备主题', width: '', fixed: false, sort: false },
          { prop: 'comment', label: '评论次数', width: '', fixed: false, sort: true },
          { prop: 'join', label: '参与教师数', width: '', fixed: false, sort: true }
        ],
        table_data: [
          { rank: '1', area: '河南区域1', theme: '春', comment: 1, join: 5 },
          { rank: '2', area: '郑州区域2', theme: '春', comment: 8, join: 24 },
          { rank: '3', area: '新疆区域3', theme: '春', comment: 18, join: 52 },
          { rank: '4', area: '江苏区域4', theme: '春', comment: 2, join: 35 },
          { rank: '5', area: '其它区域5', theme: '春', comment: 30, join: 20 }
        ]
      }

实现效果如下:

在这里插入图片描述
按照评论次数升序排序如下:

在这里插入图片描述
完整代码附上:

<template>
  <div class="news-table">
    <el-table
      :max-height="config.max_height"
      :data="config.table_data"
      style="width: 100%"
      :header-cell-style="{ background: '#212949' }"
    >
      <el-table-column
        align="center"
        v-for="(item, index, key) in config.table_cols"
        :key="key"
        :index="index"
        :label="item.label"
        :prop="item.prop"
        :width="item.width"
        :fixed="item.fixed"
        :show-overflow-tooltip="true"
        :sortable="item.sort"
      >
        <template slot-scope="scope">
          <span>{{ scope.row[item.prop] }}</span>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  name: 'NewsTable',
  props: ["config"],
  components: {},
  data() {
    return {};
  },
  watch: {},
  computed: {},
  methods: {},
  created() {},
  mounted() {}
};
</script>

<style lang="stylus" scoped>
.news-table
  /deep/ .el-table {
    background-color: rgba(0, 0, 0, 0);
  }
  /deep/ .el-table::before {
    height: 0;
  } 
  /deep/ .el-table th.el-table__cell,
  /deep/ .el-table td.el-table__cell {
    border-bottom: none;
    padding: 0;
    color: #FFF;
    font-size: 14px;
  }
  /deep/ .el-table .el-table__header-wrapper {
    height: 40px;
    line-height: 40px;
  }

  /deep/ .el-table .el-table__header-wrapper .cell {
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  /deep/ .el-table .el-table__row {
    height: 40px;
    background: #182041;
    img {
        height: 20px;
        vertical-align: middle;
    }
    &:nth-child(2),
    &:nth-child(4) {
        background: #212949;
    }
  }
  /deep/ .el-table tbody tr:hover > td {
    background: rgba(0, 0, 0, 0);
  }
  /deep/ .el-table__empty-block {
    background: #182041;
  }
</style>

后续有新的内容继续更新~

  • 10
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是段段

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值