Skip to content

vue笔记(十一): table表格组件

图片

table表格

场景:后台管理系统的数据展示基本都是由表格构成,原本想做个element那样的表格,无奈能力有限,所以暂时写了这个满足我自己日常使用的表格

思路:

之前看过几篇相关的博客文章,总结了一点思路,因为经验有限,暂时先以实现为主

自定义table数据和表头header数据,header[子数据]里key键的值对应table[子数据]的某个键名字,渲染的时候利用这个联系显示对应的tables数据

另外也实现了一个左边操作列冻结,是另外单独写了一个元素,放在表格外,用绝对定位在右侧,冻结的列是写死的只冻结操作列,默认不显示,props参数开启冻结列,右侧的冻结列就会显示,覆盖在表格上面

先看看运行后的效果

图片

自定义数据

isZebra: true,是否显示斑马线,默认显示

isRightfrozen: false,是否冻结操作栏,默认false

columns:title,表头名字,key指定列值,width设置列宽度,不写默认自动宽度

js
columns: [
      {
        title: "姓名",
        key: "name",
        width: "200"
      },
      {
        title: "年龄",
        key: "age",
      },
      {
        title: "性别",
        key: "sex",
      },
      {
        title: "地址",
        key: "address",
      },
  ],
columns: [
      {
        title: "姓名",
        key: "name",
        width: "200"
      },
      {
        title: "年龄",
        key: "age",
      },
      {
        title: "性别",
        key: "sex",
      },
      {
        title: "地址",
        key: "address",
      },
  ],

data: 表格数据

js
data: [
      {
        id: 0,
        name: "小刘",
        age: 19,
        sex: "男",
        address: "广东深圳"
      },
    
],
data: [
      {
        id: 0,
        name: "小刘",
        age: 19,
        sex: "男",
        address: "广东深圳"
      },
    
],

运行

图片

一定要按照约定配置参数

新建table.vue

html
<template>  
    <div class="table">
        <div class="table-main">
           <table class="table-box" ref="opentablebox">
                <tr>
                    <!-- 选择框 -->
                    <th >
                        <div class="table-check-all" >
                            <input class="check-box" type="checkbox" :checked="selectAlls" @click="checkAll($event)">
                        </div>
                    </th>
                    <!-- 循环输出表头 -->
                    <th v-for="item in columns" :class="[item.width>0 ? ' ':'otbi-item-width']">
                        <div class="otbi-item"  :style="{'width':`${item.width}px`}">
                            {{item.title}}
                        </div>
                    </th>
                    <!-- 操作列表头 -->
                    <th>
                        <div class="otbi-tool">操作</div>
                    </th>
                </tr>
                <tr v-for= "(item,index) in data" :class="['table-box-item',index % 2 == 0&&isZebra ? 'table-item-zebra' : '']" >
                    <!-- 选择框 -->
                    <td>
                       <div class="table-check-item">
                            <input class="check-box" type="checkbox" :checked="selectItems" @click="checkItem(item.id,$event)">
                       </div>
                    </td>
                    <!-- 循环输出表头key值对应的table数据 -->
                    <td v-for="items in columns" :class="[!items.width?'otbi-item-width':'']">
                        <div class="otbi-item"  :style="{'width':`${items.width}px`}">
                            {{item[items.key]}}
                        </div>
                    </td>
                    <!-- 操作列 -->
                    <td>
                        <div class="otbi-tool" >
                            <span class="otbi-tool-edit" @click="onEdit(item.id)">编辑</span>
                            <span class="otbi-tool-remove" @click="onRemove(item.id)">删除</span>
                        </div>
                    </td>
                </tr>
           </table>
       </div>
       <!-- 冻结的操作列 -->
       <div class="otbi-tool-frozen" v-if="isRightfrozen">
            <div class="otbi-tool-header">操作</div>
            <div v-for= "(item,index) in data" :class="['table-line table-box-item',index % 2 == 0&&isZebra ? 'table-item-zebra' : '']" >
                <div>
                    <div :class="['otbi-tool',index % 2 == 0&&isZebra ? 'table-item-zebra' : '']" >
                        <span class="otbi-tool-edit" @click="onEdit(item.id)">编辑</span>
                        <span class="otbi-tool-remove" @click="onRemove(item.id)">删除</span>
                    </div>
                </div>
            </div>
       </div>
    </div>
</template> 

<script> 
    export default {
        name: 'Table',
        props: {
            //斑马线,默认true
            isZebra: {
                type: Boolean,
                default: true
            },
            //冻结右侧操作列,默认不冻结
            isRightfrozen: {
                type: Boolean,
                default: false
            },
            columns: {
                type: Array,
                default: function(data){
                    return data;
                }
            },
            data: {
                type: Array,
                default: function(data){
                    return data;
                }
            },
        },
        data () {
            return {
              selectAlls: false,//全选框绑定的chacked
              selectItems: false,//单选框绑定的chacked
              selectList: [],//选中的id数组
            }
        },
        methods: {
            //点击单选框
            checkItem(id,e){
                if(e.target.checked){
                    this.selectList.push(id);
                    if(this.selectList.length == this.data.length){
                        this.selectAlls = true;
                    }
                }else{
                    this.selectAlls = false;
                    let index = this.selectList.indexOf(id);
                    this.selectList.splice(index,1)
                }
                // 提交选中集合到父组件
                this.onChange();
            },
            //点击全选框
            checkAll(e){
                if(e.target.checked){
                    this.selectAlls = true;
                    this.selectItems = true;
                    this.selectList = [];
                    this.data.forEach((item)=>{
                        this.selectList.push(item.id)
                    })
                    
                }else{
                    this.selectAlls = false;
                    this.selectList = [];
                    this.selectItems = false
                }
                // 提交选中集合到父组件
                this.onChange();
            },
            //编辑按钮事件
            onEdit(id){
                this.$emit('onEdit',id)
            },
            //删除按钮事件
            onRemove(id){
                this.$emit('onRemove',id)
            },
            //点击选框时把选中的id数组提交给父组件
            onChange(){
                this.$emit('onChange',this.selectList)
            }
        }
    }
</script>

<style>
    .table{
        width: 100%;
        box-sizing: border-box;
        padding: 15px;
        position: relative;
    }
    .table-main{
        width: inherit;
        position: relative;
        overflow: auto;
    }
    .table-box{
        width: inherit;
    }
    .table-check-all{
        padding: 8px 0;
    }
    .table-check-all,.table-check-item{
        display: flex;
        align-items: center;
        width: 40px;
        text-align: left;
    }
    .check-box[type=checkbox]{
            width: 17px;
            height: 17px;
            border: 1px #dcdfe6 solid!important;
            border-radius: 2px;
        }
    .check-box[type=checkbox]:checked::before {
            content: "✓";
            color: #fff;
            font-size: 8px;
            font-weight: bold;
            padding: 2px 4px;
            border-radius: 3px;
            background: #f56c6c;
        }

    tr{
        width: 100%;
        border-bottom: 1px #ebeef5 solid;
        box-sizing: border-box;
        display: flex;
        padding: 0 10px;
    }
    .table-line{
        border-bottom: 1px #ebeef5 solid;
    }
    tr th{
        box-sizing: border-box;
        text-align: left;
        padding: 8px 0;
        font-size: var(--font-size-base);
        font-weight: bold;
        color: #999;
    }
    tr td{
        display: flex;
        flex-wrap: nowrap;
        overflow-y: auto;
        align-items: center;
        box-sizing: border-box;
        font-weight: 400;
        font-size: 14px;
        color: #666;
    }
    .otbi-item-width{
        flex: 1;
    }
    .otbi-tool{
        min-width: 110px;
        padding: 8px;
        display: flex;
        justify-content: space-between;
    }
    .otbi-tool span{
        display: inline-block;
        font-size: var(--font-size-sm);
        padding: 5px 11px;
        border-radius: 4px;
        cursor: pointer;
    }
    .otbi-tool-edit{
        color: #666;
        border: 1px #dcdfe6 solid;
    }
    .otbi-tool-edit:hover{
        background: #ecf5ff;
        color: #1890ff;
        border: 1px #1890ff solid;
    }
    .otbi-tool-remove{
        color: #ffffff;
        background: #f56c6c;
        border: 1px #f56c6c solid;
    }
    .otbi-tool-remove:hover{
        background: rgba(245, 108, 108, 0.7);
        border: 1px rgba(245, 108, 108, 0.7) solid;
    }
    .otbi-item{
        min-width: 100px;
        width: 100%;
        padding: 8px;
        display: flex;
        flex-wrap: ;
        box-sizing: border-box;
    }
    .table-box-item:hover{
        background: #f5f7fa;
    }
    .otbi-tool-header{
        box-sizing: border-box;
        text-align: left;
        padding: 16px 8px 19px 8px;
        font-size: var(--font-size-base);
        font-weight: bold;
        color: #999;
    }
    .otbi-tool-frozen{
        background: #fff;
        position:absolute;
        top: 16px;
        right: 15px;
        padding-right: 10px;
        z-index: 999;
        border-left: 1px #eee solid;
    }
    .otbi-tool-header-frozen{
        background: #fff;
        position:;
        top: 15px;
        right: 15px;
        padding-right: 10px;
        margin-right: 7px;
        z-index: 999;
        border-left: 1px #eee solid;
        box-shadow: -3px 0px 10px 0px #eee;
    }

     /*斑马线*/
    .table-item-zebra{
        background: #fafafa;
    }
</style>
<template>  
    <div class="table">
        <div class="table-main">
           <table class="table-box" ref="opentablebox">
                <tr>
                    <!-- 选择框 -->
                    <th >
                        <div class="table-check-all" >
                            <input class="check-box" type="checkbox" :checked="selectAlls" @click="checkAll($event)">
                        </div>
                    </th>
                    <!-- 循环输出表头 -->
                    <th v-for="item in columns" :class="[item.width>0 ? ' ':'otbi-item-width']">
                        <div class="otbi-item"  :style="{'width':`${item.width}px`}">
                            {{item.title}}
                        </div>
                    </th>
                    <!-- 操作列表头 -->
                    <th>
                        <div class="otbi-tool">操作</div>
                    </th>
                </tr>
                <tr v-for= "(item,index) in data" :class="['table-box-item',index % 2 == 0&&isZebra ? 'table-item-zebra' : '']" >
                    <!-- 选择框 -->
                    <td>
                       <div class="table-check-item">
                            <input class="check-box" type="checkbox" :checked="selectItems" @click="checkItem(item.id,$event)">
                       </div>
                    </td>
                    <!-- 循环输出表头key值对应的table数据 -->
                    <td v-for="items in columns" :class="[!items.width?'otbi-item-width':'']">
                        <div class="otbi-item"  :style="{'width':`${items.width}px`}">
                            {{item[items.key]}}
                        </div>
                    </td>
                    <!-- 操作列 -->
                    <td>
                        <div class="otbi-tool" >
                            <span class="otbi-tool-edit" @click="onEdit(item.id)">编辑</span>
                            <span class="otbi-tool-remove" @click="onRemove(item.id)">删除</span>
                        </div>
                    </td>
                </tr>
           </table>
       </div>
       <!-- 冻结的操作列 -->
       <div class="otbi-tool-frozen" v-if="isRightfrozen">
            <div class="otbi-tool-header">操作</div>
            <div v-for= "(item,index) in data" :class="['table-line table-box-item',index % 2 == 0&&isZebra ? 'table-item-zebra' : '']" >
                <div>
                    <div :class="['otbi-tool',index % 2 == 0&&isZebra ? 'table-item-zebra' : '']" >
                        <span class="otbi-tool-edit" @click="onEdit(item.id)">编辑</span>
                        <span class="otbi-tool-remove" @click="onRemove(item.id)">删除</span>
                    </div>
                </div>
            </div>
       </div>
    </div>
</template> 

<script> 
    export default {
        name: 'Table',
        props: {
            //斑马线,默认true
            isZebra: {
                type: Boolean,
                default: true
            },
            //冻结右侧操作列,默认不冻结
            isRightfrozen: {
                type: Boolean,
                default: false
            },
            columns: {
                type: Array,
                default: function(data){
                    return data;
                }
            },
            data: {
                type: Array,
                default: function(data){
                    return data;
                }
            },
        },
        data () {
            return {
              selectAlls: false,//全选框绑定的chacked
              selectItems: false,//单选框绑定的chacked
              selectList: [],//选中的id数组
            }
        },
        methods: {
            //点击单选框
            checkItem(id,e){
                if(e.target.checked){
                    this.selectList.push(id);
                    if(this.selectList.length == this.data.length){
                        this.selectAlls = true;
                    }
                }else{
                    this.selectAlls = false;
                    let index = this.selectList.indexOf(id);
                    this.selectList.splice(index,1)
                }
                // 提交选中集合到父组件
                this.onChange();
            },
            //点击全选框
            checkAll(e){
                if(e.target.checked){
                    this.selectAlls = true;
                    this.selectItems = true;
                    this.selectList = [];
                    this.data.forEach((item)=>{
                        this.selectList.push(item.id)
                    })
                    
                }else{
                    this.selectAlls = false;
                    this.selectList = [];
                    this.selectItems = false
                }
                // 提交选中集合到父组件
                this.onChange();
            },
            //编辑按钮事件
            onEdit(id){
                this.$emit('onEdit',id)
            },
            //删除按钮事件
            onRemove(id){
                this.$emit('onRemove',id)
            },
            //点击选框时把选中的id数组提交给父组件
            onChange(){
                this.$emit('onChange',this.selectList)
            }
        }
    }
</script>

<style>
    .table{
        width: 100%;
        box-sizing: border-box;
        padding: 15px;
        position: relative;
    }
    .table-main{
        width: inherit;
        position: relative;
        overflow: auto;
    }
    .table-box{
        width: inherit;
    }
    .table-check-all{
        padding: 8px 0;
    }
    .table-check-all,.table-check-item{
        display: flex;
        align-items: center;
        width: 40px;
        text-align: left;
    }
    .check-box[type=checkbox]{
            width: 17px;
            height: 17px;
            border: 1px #dcdfe6 solid!important;
            border-radius: 2px;
        }
    .check-box[type=checkbox]:checked::before {
            content: "✓";
            color: #fff;
            font-size: 8px;
            font-weight: bold;
            padding: 2px 4px;
            border-radius: 3px;
            background: #f56c6c;
        }

    tr{
        width: 100%;
        border-bottom: 1px #ebeef5 solid;
        box-sizing: border-box;
        display: flex;
        padding: 0 10px;
    }
    .table-line{
        border-bottom: 1px #ebeef5 solid;
    }
    tr th{
        box-sizing: border-box;
        text-align: left;
        padding: 8px 0;
        font-size: var(--font-size-base);
        font-weight: bold;
        color: #999;
    }
    tr td{
        display: flex;
        flex-wrap: nowrap;
        overflow-y: auto;
        align-items: center;
        box-sizing: border-box;
        font-weight: 400;
        font-size: 14px;
        color: #666;
    }
    .otbi-item-width{
        flex: 1;
    }
    .otbi-tool{
        min-width: 110px;
        padding: 8px;
        display: flex;
        justify-content: space-between;
    }
    .otbi-tool span{
        display: inline-block;
        font-size: var(--font-size-sm);
        padding: 5px 11px;
        border-radius: 4px;
        cursor: pointer;
    }
    .otbi-tool-edit{
        color: #666;
        border: 1px #dcdfe6 solid;
    }
    .otbi-tool-edit:hover{
        background: #ecf5ff;
        color: #1890ff;
        border: 1px #1890ff solid;
    }
    .otbi-tool-remove{
        color: #ffffff;
        background: #f56c6c;
        border: 1px #f56c6c solid;
    }
    .otbi-tool-remove:hover{
        background: rgba(245, 108, 108, 0.7);
        border: 1px rgba(245, 108, 108, 0.7) solid;
    }
    .otbi-item{
        min-width: 100px;
        width: 100%;
        padding: 8px;
        display: flex;
        flex-wrap: ;
        box-sizing: border-box;
    }
    .table-box-item:hover{
        background: #f5f7fa;
    }
    .otbi-tool-header{
        box-sizing: border-box;
        text-align: left;
        padding: 16px 8px 19px 8px;
        font-size: var(--font-size-base);
        font-weight: bold;
        color: #999;
    }
    .otbi-tool-frozen{
        background: #fff;
        position:absolute;
        top: 16px;
        right: 15px;
        padding-right: 10px;
        z-index: 999;
        border-left: 1px #eee solid;
    }
    .otbi-tool-header-frozen{
        background: #fff;
        position:;
        top: 15px;
        right: 15px;
        padding-right: 10px;
        margin-right: 7px;
        z-index: 999;
        border-left: 1px #eee solid;
        box-shadow: -3px 0px 10px 0px #eee;
    }

     /*斑马线*/
    .table-item-zebra{
        background: #fafafa;
    }
</style>

调用

js
// data数据和columns数据用的上面讲解自定义参数的数据
<table @onEdit="onEdit" @onRemove="onRemove" @onChange="onChange" :columns="columns" :data="data"> </table>

//编辑按钮触发事件
onEdit(id){
    console.log(id)//正常输入
},

//删除按钮触发事件
onRemove(id){
    console.error(id)//error输出红色错误样式信息
},

//点击选框事件
onChange(data){
    console.warn(data)//warn输出黄色警告样式信息
},
// data数据和columns数据用的上面讲解自定义参数的数据
<table @onEdit="onEdit" @onRemove="onRemove" @onChange="onChange" :columns="columns" :data="data"> </table>

//编辑按钮触发事件
onEdit(id){
    console.log(id)//正常输入
},

//删除按钮触发事件
onRemove(id){
    console.error(id)//error输出红色错误样式信息
},

//点击选框事件
onChange(data){
    console.warn(data)//warn输出黄色警告样式信息
},

运行

图片

都正常输出了

下面看看冻结操作列

设置:isRightfrozen="true"

js
<table :isRightfrozen="true" @onEdit="onEdit" @onRemove="onRemove" @onChange="onChange" :columns="columns" :data="data"> </table>
<table :isRightfrozen="true" @onEdit="onEdit" @onRemove="onRemove" @onChange="onChange" :columns="columns" :data="data"> </table>

运行

图片

此组件代码写完还未优化,适合学习交流用

本文到此结束。

反馈信息

INFO

邮箱: open_teams@163.com