提示:请先看第二步中的组件说明,和组件封装中其他扩展的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>
我是马丁的车夫,欢迎转发收藏!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通