Vue2基于Element-ui中的el-table组件进行二次封装(减少代码提高复用,扩展API)

提示:请先看第二步中的组件说明,和组件封装中其他扩展的api,的注释都在html中对应的代码结构都有主要依靠JSON数据动态执行和回调

特点:通过JSON数据动态渲染列表标题和数据列表,JSON数据驱动事件监听和回调以及数据过滤,和满足更多自定义需求组合

效果图:

 

 

下面这种模式可以点击图片,可以是一张图片,也可以数组中多个图片,点击图片可以预览切换

 

 

 

 

 

1.页面调用(记得进行引入组件后在调用)

复制代码
<table-List :tableData="tableData" :tableLabel="tableLabel" :choice="true" pagingPosition="center" :paging="true"
        :operation="operation" :total="total" headerAlign='center' @handleNumberChange="handleNumberChange"
        @sortChange="sortChange" @select="select" :headButton="true" @handleCurrentPage="handleCurrentPage">
        <template v-slot:second>
          <el-button type="primary" size="mini" ><i class="el-icon-download"></i> 导出</el-button>
        </template>
</table-List>
import tableList from '@/components/tableList' components:{
  tableList,
}, data(){
return{ tableData: [],//数据源 tableLabel:[ { prop: 'nickName', label: '用户昵称', align: 'center', }, { prop: 'friendCount', label: '好友数量', align: 'center', }, ], operation:{ width: '220', Button:[ { label: '查看', size: "mini", authority: false, callback(k) { console.log(k) } } ] }, total:10, } } , methods:{ handleNumberChange(val){},// 每一页展示多少条切换 handleCurrentPage(val){},//切换分页 sortChange(column){},// 排序 select(valArr){},//列表复选框 }
复制代码

 

2.封装组件核心代码

复制代码
<template>
    <div style="height: 100%;">
        <!-- 这里插槽主要是自定义头部标签操作按钮 -->
        <div style="padding: 5px 0;" v-if="headButton">
            <slot name="second"></slot>
        </div>
        <el-table :data="tableData" :row-key="rowKey" border @sort-change='sortChange' @select="select"
            :highlight-current-row="true" @select-all="select"
            :header-cell-style="{'text-align':headerAlign,'background':'#eef1f6'}" @current-change="handleCurrentChange"
            :max-height="fixedHeight>0?fixedHeight:tableHeight-difference" :ref="Math.random()">
            <!-- 复选框 -->
            <el-table-column type="selection" v-if="choice" width="50" :align="headerAlign" :key="Math.random()">
            </el-table-column>
            <!-- 序号 -->
            <el-table-column label="序号" v-if="serialNumber" width="50" type="index" :align="headerAlign"
                :key="Math.random()">
                <template slot-scope="scope">
                    {{searchData.limit * (searchData.page - 1) + (scope.$index + 1)}}
                </template>
            </el-table-column>
            <!-- 动态表头 -->
            <!-- {
                prop:'title',// 列表渲染的字段key
                label:'菜单名称',// 字段中文名称
                align:''// 列表内容文字的位置
                sort:true, // 是否排序
                form:'string', (可选:string/img/Button/filter/select)
                key:{label: "label",value: "value"} // 这个配置是下拉框的form = select
                filter(row){},// 对数据进行过滤 并返回内容 和vue 过滤器一样的功能
                configure:{ // 配置按钮的参数对象
                    type:''// 参考el-button 官网参数
                    size:''// 参考el-button 官网参数
                },
                callback(row){} //点击按钮的回调函数
            } -->
            <div v-for="(item, index) in tableLabel" :key="index">
                <!-- 普通数据展示/或者字符串文本展示 -->
                <el-table-column v-if="!item.form || item.form ==='string'" :prop="item.prop" :width="item.width"
                    :label="item.label" :sortable="item.sort" :align="item.align?item.align:'left'" :key="index"
                    show-overflow-tooltip>
                </el-table-column>

                <!-- 数据过滤:根据filter函数自定义规则进行数据返回 -->
                <el-table-column v-else-if="item.form === 'filter'" :width="item.width" :label="item.label"
                    :sortable="item.sort" :align="item.align?item.align:'left'" :key="index" show-overflow-tooltip>
                    <template slot-scope="scope">
                        {{item.filter(scope.row)}}
                    </template>
                </el-table-column>

                <!-- 图片展示 -->
                <el-table-column v-else-if="item.form === 'img'" :width="item.width" :label="item.label"
                    :sortable="item.sort" :align="item.align?item.align:'left'" :key="index">
                    <template slot-scope="scope">
                        <!-- 如果是字符串就是一个图片 -->
                        <div v-if="typeof scope.row[item.prop] === 'string'">
                            <el-image style="width: 22px; height: 22px"
                                :src="scope.row[item.prop]?scope.row[item.prop]:defaultImg"
                                :preview-src-list="[scope.row[item.prop]?scope.row[item.prop]:defaultImg]">
                            </el-image>
                        </div>
                        <!-- 不是字符串就统一数组来处理 -->
                        <div v-else-if="scope.row[item.prop]!==null&& scope.row[item.prop].length>0">
                            <span v-for="(item1,index1) in scope.row[item.prop]" :key="index1">
                                <el-image style="width: 22px; height: 22px;margin-right: 20px;" :src="scope.row[item.prop][index1]"
                                    :preview-src-list="scope.row[item.prop]" :z-index='2100'>
                                </el-image>
                            </span>
                        </div>
                    </template>
                </el-table-column>
                <!-- 按钮展示 -->
                <el-table-column v-else-if="item.form === 'Button'" :width="item.width" :label="item.label"
                    :sortable="item.sort" :align="item.align?item.align:'left'" :key="index">
                    <template slot-scope="scope">
                        <el-button @click="item.callback&&item.callback(scope.row)"
                            :type="item.configure&&item.configure.type?item.configure.type:''"
                            :size="item.configure&&item.configure.size?item.configure.size:''">
                            {{item.configure.text||'缺少按钮文字'}}</el-button>
                    </template>
                </el-table-column>
                <!-- 多个复选框 -->
                <el-table-column v-else-if="item.form === 'checkbox'" :width="item.width" :label="item.label"
                    :sortable="item.sort" :align="item.align?item.align:'left'" :key="index">
                    <template slot-scope="scope">
                        <span style="margin-left: 5px;" v-for="(v,i) in scope.row.authority" :key="i">
                            <el-checkbox :label="v.name" name="type" v-model="v.open"></el-checkbox>
                        </span>
                        <span v-if="scope.row.authority&&scope.row.authority.length<1">暂无</span>
                    </template>
                </el-table-column>
                <!-- 单选框 -->
                <!-- 表头配置tableLabel -->
                <!-- {
                        prop: 'timeType',最终下拉框每次选中的值都绑定到 这个定义的字段上
                        label: '时间单位',
                        align: 'center',
                        form: "radio",
                        key: {
                            label: "label",
                            value: "value"
                        },
                        width: 250,
                    }, -->
                <!-- 列表循环数据 每一项必须要有 radioList属性为数组 -->
                <el-table-column v-else-if="item.form === 'radio'" :width="item.width" :label="item.label"
                    :sortable="item.sort" :align="item.align?item.align:'left'" :key="index">
                    <template slot-scope="scope" >
                        <div v-if="scope.row.radioList && scope.row.radioList.length">
                            <el-radio-group v-model="scope.row[item.prop]">
                                <el-radio 
                                v-for="items in scope.row.radioList"
                                :key="items[item.key.value]"
                                :label="items[item.key.value]" >{{items[item.key.label]}}</el-radio>
                              </el-radio-group>
                        </div>
                        <div v-else>
                            <span style="color: #e6a23c;">缺少radioList属性数组!</span>
                        </div>
                    </template>
                </el-table-column>
                <!-- 下拉框 -->
                <!-- 表头配置tableLabel -->
                <!-- {
                    prop:'xxx' 最终下拉框每次选中的值都绑定到 这个定义的字段上
                    form:'select',
                    key:{label: "label",value: "value"}
                } -->
                <!-- 列表循环数据 每一项必须要有 options属性为数组 -->
                <el-table-column v-else-if="item.form === 'select'" :width="item.width" :label="item.label"
                    :sortable="item.sort" :align="item.align?item.align:'left'" :key="index">
                    <template slot-scope="scope">
                        <el-select v-model="scope.row[item.prop]" placeholder="请选择" size="mini">
                                <el-option
                                  v-for="items in scope.row.options"
                                  :key="items[item.key.value]"
                                  :label="items[item.key.label]"
                                  :value="items[item.key.value]">
                                </el-option>
                        </el-select>
                    </template>
                </el-table-column>
                <!-- 输入框 -->
                <el-table-column v-else-if="item.form === 'input'" :width="item.width" :label="item.label"
                    :sortable="item.sort" :align="item.align?item.align:'left'" :key="index">
                    <template slot-scope="scope">
                        <el-input  placeholder="请输入内容" v-model="scope.row[item.prop]"size="mini" >
                        </el-input>
                    </template>
                </el-table-column>
                
            </div>

            <el-table-column label="操作" :width="operation.width" v-if="JSON.stringify(operation)!== '{}'">
                <template slot-scope="scope">
                    <!-- 操作按钮 -->
                    <!-- {
                        label:'删除',
                        type:'danger',// 按钮类型  参考el-button 官网参数
                        size:"mini", // 按钮大小  参考el-button 官网参数
            plain:true, // 是否朴素按钮
                        callback(row){}, // 点击按钮回调函数
                        disabled(row){ return true/false} //是否禁用 可以条件判断 让函数返回布尔值
                        authority:true// 如果涉及到权限按钮控制的 就添加这个属性(false普通按钮/true权限按钮)
                        authorityEvent(row){return true/false}
                    } -->
                    <!-- 动态操作按钮 -->
                    <div class="flex center">
                        <div class="flex center" v-for="(item,index) in operation.Button" :key="index">
                            <el-button style="margin: 0 5px;" v-if="!item.authority"
                                @click="item.callback(Object.assign({},scope.row))" :type="item.type?item.type:''"
                                :size="item.size?item.size:''" :disabled="item.disabled&&item.disabled(scope.row)"
                                :plain="plain">{{item.label}}</el-button>
                            <el-button style="margin: 0 5px;"
                                v-else-if="item.authority && item.authorityEvent&& item.authorityEvent(item.label)"
                                @click="item.callback(Object.assign({},scope.row))" :type="item.type?item.type:''"
                                :size="item.size?item.size:''" :disabled="item.disabled&&item.disabled(scope.row)"
                                :plain="plain">{{item.label}}</el-button>
                        </div>
                    </div>
                </template>
            </el-table-column>
        </el-table>
        <!-- 分页 -->
        <div style="padding: 10px;" v-if="paging" :style="{'text-align':pagingPosition}">
            <el-pagination @size-change="handleNumberChange" @current-change="handleCurrentPage"
                :current-page="searchData.page" :page-sizes="[10,15, 20, 30, 40,50]" :page-size="searchData.limit"
                layout="total, sizes, prev, pager, next, jumper" :total="total">
            </el-pagination>
        </div>
    </div>
</template>

<!-- /**
 * 组件说明
 * 属性:
 * 参数                     说明                           类型                    默认值
 * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 * tableData              列表 的数据源                   Array                      []
 * treeProps              支持树类型的数据的列表显示       Object                     {children: 'children'}
 * rowKey                 树类型的数据 列表必须要指定        String                     '_id'
 * serialNumber           是否需要序号                     Boolean                    true
 * headerAlign               表头对齐方式/复选框/序号              String                    'left'/'center'/'right'
 * tableLabel               动态表头渲染数组                Array                     []
 * choice                   是否开启多选复选框                Boolean                 false
 * operation               操作列表按钮                    Object                     {}
 * total                   分页总页数                     number                     0
 * paging                   是否开启分页                     Boolean                 false
 * pagingPosition           分页底部的位置                     String                     'left'/'center'/'right'
 * fixedHeight               固定table列表高度                 number                      0
 * pagingPage               分页参数                         Object                     {pageSize: 20,page: 1}
 * headButton             列表头部按钮是否显示              Boolean                   false

 * 回调事件:
 * select  获取复选框选中对象 返回数组
 * sortChange     获取当前点击表头指定排序结果
 * handleCurrentPage     每次切换页数触发并且回调页数
 * handleNumberChange     每次切换展示的条数触发并且回调
 */ -->
<script>
    const defaultImg = require('../../assets/image/user.png')
    export default {
        components: {

        },
        props: {
            plain: {
                type: Boolean,
                default: true
            },
            headButton: {
                type: Boolean,
                default: false
            },
            tableData: { // 数据
                type: Array,
                default: () => ([])
            },
            treeProps: { // 支持树类型的数据的显示
                type: Object,
                default: () => ({
                    children: 'children'
                })
            },
            rowKey: { //树类型的数据 列表必须要指定row-key id
                type: String,
                default: '_id'
            },
            serialNumber: { // 是否需要序号
                type: Boolean,
                default: true
            },
            headerAlign: {
                type: String,
                default: 'left'
            },
            tableLabel: {
                type: Array,
                default: () => ([])
            },
            choice: {
                type: Boolean,
                default: false
            },
            operation: {
                type: Object,
                default: () => ({})
            },
            total: {
                type: Number,
                default: 0
            },
            paging: {
                type: Boolean,
                default: false
            },
            pagingPosition: {
                type: String,
                default: 'left'
            },
            fixedHeight: {
                type: Number,
                default: 0
            },
            pagingPage: {
                type: Object,
                default: () => ({
                    limit: 15,
                    page: 1
                })
            },
            // 设置偏移高度(到达每个页面不同内容高度,都可以让列表高度填充满整个可视区)
            difference:{
                type: Number,
                default: 0
            }
        },
        data() {
            return {
                defaultImg: defaultImg,
                tableHeight: document.documentElement.clientHeight-(70+80), // 表格自适应高度 默认撑满
                // 分页固定参数
                searchData: {
                    limit: this.pagingPage.limit,
                    page: this.pagingPage.page
                }
            }
        },
        watch: {


        },
        created() {

        },
        methods: {
            // 重置分页参数
            pagingReset(limit,page){
                if(!limit && !page){
                    this.searchData.limit = 15;
                    this.searchData.page = 1
                } else if(limit){
                    this.searchData.limit = limit;
                } else {
                    this.searchData.page = page
                }
                
            },
            test(){
                console.log('添加事件')
            },
            // 切换页数
            handleCurrentPage(val) {
                this.searchData.page = val
                this.$emit('handleCurrentPage', val)
            },
            // 每页N展示条
            handleNumberChange(val) {
                this.searchData.limit = val
                this.$emit('handleNumberChange', val)
            },
            // 复选框勾选
            select(row) {
                this.$emit('select', row)
            },
            // 排序
            sortChange(column, prop, order) {
                this.$emit('sortChange', column)
            },
            // 点击列表 获取数据
            handleCurrentChange(row) {
                // console.log(row)
            }
        }
    }
</script>
<style scoped>
    /* 默认el-table 行高过于高,这里设置减少padding之间的间距 */
    /deep/ th {
        padding: 9px 0;
    }

    /deep/ .el-table td {
        padding: 9px 0;
    }
</style>
复制代码

 

 

我是马丁的车夫,欢迎转发收藏!

 

posted on   马丁的车夫  阅读(2800)  评论(3编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示