Vue 表格自定义树形组件,行,列表可传入等
1.组件,BaseTable 下有两个文件,index.vue ,extend.js
index.vue代码
<template> <div class="crud"> <!--crud头部,包含列表标题和可操作按钮--> <el-row class="crud-header"> <!--<el-col :span="8">{{formTitle}}列表</el-col>--> <el-col :span="24"> <el-button type="success" size="mini" v-if="gridBtnConfig.create" @click="createOrUpdate(null)"> <i class="el-icon-plus"></i> 新增 </el-button> <!-- 标记为single的按钮不需要禁用,不依赖选择数据--> <el-button :disabled="!item.single && multipleSelection.length === 0" size="mini" @click="handleEmit(item.emitName,'outer')" v-for="(item,index) in outerButton" :key="index" :type="item.type?item.type:'primary'"> <svg-icon v-if="item.icon" :icon-class="item.icon"></svg-icon> {{item.name}} </el-button> <el-button :disabled="multipleSelection.length === 0" size="mini" v-if="gridBtnConfig.delete" type="danger" @click="remove(2)"> <svg-icon icon-class="delete"></svg-icon> 删除 </el-button> </el-col> </el-row> <!--crud主体内容区,展示表格内容--> <el-table ref="multipleTable" v-loading="listLoading" element-loading-text="请等待" :data="showGridData" @selection-change="handleSelectionChange" border :stripe="true" :header-cell-style="{'textAlign':'center'}" style="width: 100%"> <el-table-column v-if="!hideMultiple" fixed="left" type="selection" width="55"> </el-table-column> <el-table-column v-if="hideMultiple" fixed="left" label="序号" type="index" width="55"> </el-table-column> <el-table-column v-for="(item,index) in gridConfig" :key="index" :prop="item.prop" :label="item.label" :align="nameLeft(item.label)" show-overflow-tooltip :width="item.width?item.width:''"> <template slot-scope="scope"> <span v-if="item.filter" ref="yifutian">{{walkProperty(item.prop,scope.row)||walkProperty(item.prop,scope.row)==0? item.filter(walkProperty(item.prop,scope.row)) : '--'}}</span> <Cell v-if="item.render" :row="scope.row" :column="item" :index="scope.$index" :render="item.render"></Cell> <span v-if="!item.render&&!item.filter"> <span v-if="!item.template" ref="yifutian">{{walkProperty(item.prop,scope.row)||walkProperty(item.prop,scope.row)==0 ? walkProperty(item.prop,scope.row) : '--'}}</span> <a style="color: #1582cf;cursor: pointer;" v-else @click="handleEmit(item.emitName,'inner',scope.row)">{{walkProperty(item.prop,scope.row)||walkProperty(item.prop,scope.row)==0 ? walkProperty(item.prop,scope.row) : '--'}}</a> </span> </template> </el-table-column> <el-table-column v-if="!hideEditArea" label="操作" :width="gridEditWidth?gridEditWidth:200" fixed="right"> <template slot-scope="scope"> <el-tooltip v-if="gridBtnConfig.update" effect="dark" content="修改" placement="top"> <span style="color: #409EFF" class="edit-icon" @click="createOrUpdate(scope.row)"><svg-icon icon-class="xg"></svg-icon></span> </el-tooltip> <el-tooltip v-if="gridBtnConfig.view" effect="dark" content="查看" placement="top"> <span style="color: #409EFF" class="edit-icon" @click="viewDetail(scope.row)"><svg-icon icon-class="view"></svg-icon></span> </el-tooltip> <!--扩展按钮,如果扩展按钮设置了动态显示隐藏的条件,则需要根据条件显示隐藏--> <el-tooltip v-for="(item,index) in innerButton" :key="index" :type="item.type?item.type:'primary'" v-if="(!item.hideJudge)||(item.hideJudge && walkProperty(item.hideJudge.key,scope.row) === item.hideJudge.val && (item.hideJudge.key2 ? walkProperty(item.hideJudge.key2,scope.row) === item.hideJudge.val2 : true))" effect="dark" :content="item.name" placement="top"> <span :style="{color:item.iconColor?item.iconColor:'#409EFF'}" class="edit-icon" @click="handleEmit(item.emitName,'inner',scope.row)"><svg-icon :icon-class="item.icon?item.icon:'xg'"></svg-icon></span> </el-tooltip> <el-tooltip v-if="gridBtnConfig.singleDelete" effect="dark" content="删除" placement="top"> <span style="color: #F56C6C" class="edit-icon" @click="remove(1,scope.row)"><svg-icon icon-class="delete"></svg-icon></span> </el-tooltip> </template> </el-table-column> </el-table> <!--crud的分页组件--> <div class="crud-pagination"> <!--如果不是异步请求展示数据,需要隐藏分页--> <el-pagination v-if="isAsync" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage" :page-sizes="pageSizes?pageSizes:[10, 20, 30, 40]" :page-size="currentPageSize" layout="total, sizes, prev, pager, next, jumper" :total="dataTotal"> </el-pagination> </div> <!--crud按钮触发的表单弹窗--> <BaseDialogForm :width="dialogWidth" :label-width="dialogLabelWidth" :title="dialogTitle" ref="dialogForm" :config="formConfig" :form-data="formModel" @submit="dialogSubmit"></BaseDialogForm> <!--查看详情模态框--> <gx-dialog title="查看详情" :visible.sync="detailDialogVisible" :width="dialogWidth ? dialogWidth : '1300px'"> <ViewDetail :config="infoConfig?infoConfig:formConfig" :data="detailData"></ViewDetail> </gx-dialog> <el-dialog title="提示" :close-on-click-modal="false" :visible.sync="deletess" width="400px"> <i class="el-icon-info" style="color:#E6A23C;"></i> <span>此操作将永久删除数据, 是否继续?</span> <span slot="footer" class="dialog-footer"> <el-button type="primary" @click="deletes" size="mini">确 定</el-button> <el-button @click="qxss" size="mini">取 消</el-button> </span> </el-dialog> </div> </template> <script> import BaseDialogForm from '@/components/BaseDialogForm/index.vue' import ViewDetail from '@/components/ViewDetail/index.vue' import GxDialog from "../GxDialog/index" /*用render渲染自定义template*/ import Cell from './expand'; export default { name: "base-crud", components: { BaseDialogForm, ViewDetail, GxDialog, Cell }, props: [ // 'pageSizes', // 表单标题,例如用户、角色 'formTitle', // 表单配置 'formConfig', //详情的字段配置 'infoConfig', // 表单的model数据 'formData', // 表格配置 'gridConfig', // 表格按钮配置 'gridBtnConfig', // 表格死数据 'gridData', // 数据接口 'apiService', // 判断是否是异步数据 'isAsync', // 表格编辑区域宽度 'gridEditWidth', // 是否隐藏表格操作 'hideEditArea', // 自定义新增和修改操作,覆盖组件内的新增和删除逻辑 'selfCreateAndUpdate', //是否显示多选,若不显示多选,则显示列表序号 'hideMultiple', //数据的id标识字符串 'idKey', //表格获取数据默认传入的参数 'gridParams', // 'Update', // 禁止删除的条件配置 'forbiddenData', // 操作的模态框宽度 'dialogWidth', // 操作模态框中的表单label宽度 'dialogLabelWidth', // 状态颜色区别配置数组:[[未处理],[已处理],[已过期],[其它状态]] 'statusobj' ], data() { return { // 新增修改模态框title dialogTitle: '', // 展示的表格数据,数据来源可能是父组件传递的固定数据,可能是接口请求数据 showGridData: [], // 当前页码 currentPage: 1, // 每页显示数量 currentPageSize: 10, // 列表数据总数 dataTotal: 0, // 选中行数据 selectedRow: '', // 表单数据 formModel: {}, // 保留多选的表格数据 multipleSelection: [], // 扩展按钮,包括表格内按钮和表格外的批量操作按钮 innerButton: [], outerButton: [], // 详情数据 detailData: {}, detailDialogVisible: false, // 是否是新增 isCreate: true, // 数据加载 listLoading: false, // 查询参数 searchParams: {}, //删除的数据 ids: [], //删除的弹框 deletess: false, color: true, statusObj: '' } }, mounted() { //初始化扩展按钮 if (this.gridBtnConfig.expands && this.gridBtnConfig.expands.length > 0) { //内部按钮,只操作单行数据 this.innerButton = this.gridBtnConfig.expands.filter(item => item.editType === 'inner'); //外部按钮,操作批量数据 this.outerButton = this.gridBtnConfig.expands.filter(item => item.editType === 'outer'); } this.getData(); this.statusObj = this.statusobj ? this.statusobj : [['未提交'], ['已提交', '已批复', '审批通过', '待上报', '已批复', '已撤回'], ['审核中', '返回修改'], ['已上报', '待批复'], ['审批不通过']]; }, methods: { nameLeft(name) { let a = name.substr(name.length - 2, 2); // console.log(name.indexOf('名称'),a); if (a == '名称') { return 'left' } else { return 'center' } }, // 获取列表数据 getData() { this.listLoading = true; //没有定义api,直接返回,不进行请求 if (!this.apiService) { this.listLoading = false; this.showGridData = this.gridData; return; } let params = { current: this.currentPage, size: this.currentPageSize }; params = Object.assign({}, params, this.gridParams, this.searchParams); this.apiService.list(params).then(res => { this.showGridData = res.page.list; this.dataTotal = res.page.totalCount; this.listLoading = false; //请求数据完成 this.$emit('getDataOver', res.page.list); }); }, //查询 search(params) { this.currentPage = 1 this.currentPageSize = 10 this.searchParams = Object.assign({}, params); this.getData(); }, //清空查询 clearSearch() { this.searchParams = {}; this.getData(); }, // 查看详情 viewDetail(row) { this.detailData = Object.assign({}, row); this.detailDialogVisible = true; }, createOrUpdate(item) { // 如果配置了覆盖组件内新增和修改按钮时执行 if (this.selfCreateAndUpdate) { this.$emit('createOrUpdate', item); return; } this.$refs.dialogForm.resetForm(); this.formModel = item ? Object.assign({}, item) : Object.assign({}, this.formData); this.dialogTitle = (item ? '修改' : '新增') + this.formTitle; this.isCreate = !item; this.$refs.dialogForm.showDialog(); }, // 处理相应父组件的事件方法 handleEmit(emitName, type, row) { // 如果是表格内按钮,只需要回传单行数据,表格外按钮,则需要回传批量数据 if (type === 'inner') { this.$emit(emitName, row); } else { this.$emit(emitName, this.multipleSelection); } }, handleCurrentChange(page) { this.currentPage = page; this.getData(); }, handleSizeChange(size) { this.currentPageSize = size; this.getData(); }, // 处理点击行 handleRowClick(row, event, column) { this.selectedRow = Object.assign({}, row); }, // 模态框数据提交 dialogSubmit(data) { if (this.Update) { this.$emit('Update', data); return; } this.apiService[this.isCreate ? 'create' : 'update'](data).then(res => { this.$message.success(this.dialogTitle + '成功!'); this.getData(); this.$refs.dialogForm.hideDialog(); }) }, hideDialog() { this.$refs.dialogForm.hideDialog(); }, //处理多选改变时 handleSelectionChange(val) { this.multipleSelection = val; }, // 切换当前行的选中状态 toggleSelection(rows) { if (rows) { this.$nextTick(()=>{ rows.forEach(row => { this.$refs.multipleTable.toggleRowSelection(row); }); }) } else { this.$refs.multipleTable.clearSelection(); } }, //删除方法 remove(type, data) { this.ids = []; //type为1代表单个删除,2代表批量删除 if (type === 1) { this.ids = [data[this.idKey ? this.idKey : 'id']]; } else { //如果上传了禁止删除的条件,进入下面条件处理 if (this.forbiddenData) { let key = this.forbiddenData.key; let arr = this.forbiddenData.data; let boolean = false; this.multipleSelection.forEach(item => { arr.forEach(val => { if (val === item[key]) { return (boolean = true); } }); //如果已经有禁止删除项,退出遍历,优化性能 if (boolean) { return false; } }); if (boolean) { return this.$notify({ title: '警告', message: '您选择的数据包含禁止删除项,请重新选择数据进行删除!!!', type: 'warning', position: 'top-left', duration: 2000 }); } } this.ids = this.multipleSelection.map(item => item[this.idKey ? this.idKey : 'id']); } // this.$confirm('此操作将永久删除数据, 是否继续?', '提示', { // showCancelButton: true, // confirmButtonText: '确定', // cancelButtonText: '取消', // confirmButtonClass:'confirmButtonClass', // type: 'warning' // }).then(() => { // this.apiService.delete(ids).then(res => { // this.$message({ // type: 'success', // message: '删除成功!' // }); // // this.getData(); // }) // }).catch(() => { // this.$message({ // type: 'info', // message: '已取消删除' // }); // // // }); this.deletess = true }, deletes() { this.apiService.delete(this.ids).then(res => { this.$message({ type: 'success', message: '删除成功!' }); this.getData(); this.deletess = false }) }, qxss() { this.deletess = false this.$message({ type: 'info', message: '已取消删除' }); }, walkProperty(prop, data) { //a.b if (prop && prop.indexOf('.') !== -1) { let path = prop.split('.'), i, v = data;//[a, b] while (i = path.shift()) { if (v && v[i]) { v = v[i]; } else { v = ''; } } return v; } else { return data[prop]; } }, //清空多选 clearSelection() { //清空多选的选中 this.$refs.multipleTable && this.$refs.multipleTable.clearSelection(); }, shangse() { let i = 1; let elementArrays = this.$refs.yifutian; if (!elementArrays) { return; } this.statusObj.forEach(obj => { obj.forEach(obj2 => { elementArrays.forEach(item => { let str = item.innerText; if (str === obj2) { item.setAttribute('class', 'class' + i); } }) }); i++; }); }, setGridParams(data) { this.search(data); } }, watch: { // 防止表格预置数据不成功,涉及生命周期问题 gridData() { this.showGridData = this.gridData; this.dataTotal = this.showGridData.length; }, showGridData: function () { this.$nextTick(function () { this.shangse(); }) } }, } </script> <style rel="stylesheet/scss" lang="scss" scoped> .crud { .crud-header { margin-bottom: 10px; line-height: 40px; } .crud-pagination { text-align: right; margin-top: 10px; } /*表格内编辑图标*/ .edit-icon { font-size: 20px; margin-right: 10px; cursor: pointer; } /*未处理*/ .class1 { color: #999999; padding: 5px; } /*已处理*/ .class2 { color: #06AC4D; padding: 5px; } /*已过期*/ .class3 { color: #FBBE42; padding: 5px; } /*其它状态*/ .class4 { color: #3DB5FC; padding: 5px; } .class5 { color: #FC5454; padding: 5px; } } </style> <style> .confirmButtonClass { color: #fff; background-color: #0597D2 !important; border-color: #0597D2 !important; } </style>
--extend.js代码
export default { name: 'TableExpand', functional: true, props: { row: Object, render: Function, index: Number, column: { type: Object, default: null } }, render: (h, ctx) => { const params = { row: ctx.props.row, index: ctx.props.index }; if (ctx.props.column) params.column = ctx.props.column; return ctx.props.render(h, params); } };
2.以上是两个组件的代码,下面将介绍调用及传入数据的格式
--调用组件
引入组件,且注册组件
import BaseTable from '@/components/BaseTable/index.vue' 注册加入components components: { BaseTable }, 调用组件 <BaseTable :api-service="informationApi" :grid-config="INFO_CONFIG.gridConfig" :grid-btn-config="INFO_CONFIG.gridBtnConfig" ref="crud" :grid-edit-width="80" :is-async="true" :hide-edit-area="true"> </BaseTable> 传入数据的Js数据格式: const INFO_CONFIG = { gridConfig: [ {label: '设备代码', prop: 'sbdm', width: '150'}, {label: '设备名称', prop: 'sbmc'}, {label: '型号规格', prop: 'xh'}, {label: '计量单位', prop: 'jldw', filter: equipmentUnitFilter}, {label: '类型', prop: 'lx'}, {label: '厂家', prop: 'cj'}, {label: '是否是计量设备', prop: 'sfjlsb', filter: filterJLSB}, {label: '筹供单位', prop: 'cgdw', filter: filterGCDW}, {label: '使用年限(月)', prop: 'synx'}, ], //操作按钮 gridBtnConfig: { create: false, update: false, delete: false, view: false } }
注意informationApi:是API 调用地址,如果需要直接传入数据,需要更组件中的逻辑,apiService传入true,不需要请求数据,出入gridData=[],即表格显示的数据,组件中传入的参数都写了注释,根据需做出更改吧
关注博客,后期更新Vue表单组件。