首先我们新建一个render.js文件
// render.js
export default {
functional: true,
name: 'myRender',
props: {
row: Object,
render: Function,
index: Number,
column: {
type: Object,
default: null
}
},
render: (h, content)=>{
const {props} = content;
const params = {
row: props.row,
index: props.index
}
return props.render(h, params);
}
}
然后我们再新建一个myTable.vue文件
// myTable.vue
<template>
<div>
<el-table :row-key="rowKey" @sort-change="sortChange" @header-click="headerClick"
:data="tableData" @selection-change="handleSelectionChange" :show-summary="showSummary"
:sum-text="sumText" :summary-method="getSummaries" ref="multipleTable"
highlight-current-row style="width: 100%;" v-loading="loading" element-loading-text="拼命加载中"
element-loading-spinner="el-icon-loading" header-row-class-name="myTableHeadClass" class="myTableClass">
<slot name="columnHead"></slot>
<el-table-column v-bind="item" v-for="(item,i) in tableColumnList" :key="`${item}${i}`" :sortable="item.sortable || false">
<template slot="header" slot-scope="{row}" >
<myRender v-if="item.headerRender" :render="item.headerRender" :row="row" :column="item" ></myRender>
<span v-else>
{{item.label}}
</span>
</template>
<template slot-scope="{row}" v-if="!item.type">
<myRender v-if="item.render" :render="item.render" :row="row" :column="item" ></myRender>
<div v-else>{{ item.isFormatTime ? timeFormat(row[item.prop]) : row[item.prop]}}</div>
</template>
</el-table-column>
<slot name="columnFoot"></slot>
</el-table>
<myPagination v-show="needPagination" :count="count" :initData="initData" ref="myPagination"></myPagination>
<my-dialog title="表头筛选" :dialoShow="dialoShow" @updateSave="updateSave" @closeDialo="dialoShow=false;" :width="40" >
<div class='checkBox'>
<el-checkbox-group v-model="checkList" >
<el-checkbox :label="item.label" class="checkOne" v-for="item in columnList" :key="item.id"></el-checkbox>
</el-checkbox-group>
</div>
</my-dialog>
</div>
</template>
<script>
import dateUtil from 'fecha'; //处理时间格式的一个插件
import myPagination from './myPagination.vue';
import myRender from './render.js';
import myDialog from './dialog.vue';
export default {
props:{
columnList:{ // 表格列的数据
type:Array,
default:[]
},
dataParams:{
type:Object,
required: false
},
url:String,
fetchMethods:{
type: String,
required: false,
default: 'get',
},
showSummary:{ // 设置为true就会在表格尾部展示合计行
type: Boolean,
required: false,
default: false,
},
sumText:{ // 总计显示名字
type: String,
required: false,
default: '总计',
},
computedFunc:{ // 自定义总计的计算方法
type: Function,
required:false
},
getSummaries: {
type: Function,
required: false,
default: function (param){
if(this.computedFunc)return this.computedFunc(param);
const { columns, data } = param;
const sums = [];
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = this.sumText;
return;
}
const values = data.map(item => {
let num = item[column.property];
if(typeof item[column.property] === 'string' && item[column.property].includes('%')){
num = num.replace(/%$/g,'');
column.hasPercentSign = true; // 该列是否含有百分号
}
return Number(num);
});
if (!values.every(value => isNaN(value))) {
sums[index] = values.reduce((prev, curr) => {
const value = Number(curr);
if (!isNaN(value)) {
return prev + curr;
} else {
return prev;
}
}, 0);
if(column.property === 'passRateStr'){ // 通过率=通过好友数/打招呼数
const passNumIndex = columns.findIndex(v=>v.property === 'passNum');
const greetingNumIndex = columns.findIndex(v=>v.property === 'greetingNum');
if(passNumIndex > -1 && greetingNumIndex > -1){
sums[index] = sums[greetingNumIndex] === 0 ? 0 : sums[passNumIndex] / sums[greetingNumIndex] * 100;
} else {
sums[index] = '/';
column.hasPercentSign = false;
}
} else if(column.property === 'replyRateStr'){ // 回复率=会员回复数/通过好友数
const passNumIndex = columns.findIndex(v=>v.property === 'replyNum');
const greetingNumIndex = columns.findIndex(v=>v.property === 'passNum');
if(passNumIndex > -1 && greetingNumIndex > -1){
sums[index] = sums[greetingNumIndex] === 0 ? 0 : sums[passNumIndex] / sums[greetingNumIndex] * 100;
} else{
sums[index] = '/';
column.hasPercentSign = false;
}
} else if(column.property === 'passAllocateRateStr'){ // 分配率=分配数/通过好友数
const passNumIndex = columns.findIndex(v=>v.property === 'allocateNum');
const greetingNumIndex = columns.findIndex(v=>v.property === 'passNum');
if(passNumIndex > -1 && greetingNumIndex > -1){
sums[index] = sums[greetingNumIndex] === 0 ? 0 : sums[passNumIndex] / sums[greetingNumIndex] * 100;
} else{
sums[index] = '/';
column.hasPercentSign = false;
}
} else if(column.property === 'allocateRateStr'){ // 资源分配率=分配数/添加数
const passNumIndex = columns.findIndex(v=>v.property === 'allocateNum');
const greetingNumIndex = columns.findIndex(v=>v.property === 'addNum');
if(passNumIndex > -1 && greetingNumIndex > -1){
sums[index] = sums[greetingNumIndex] === 0 ? 0 : sums[passNumIndex] / sums[greetingNumIndex] * 100;
} else{
sums[index] = '/';
column.hasPercentSign = false;
}
} else if(column.property === 'deleteRateStr'){ // 删除率=删除数/通过好友数
const passNumIndex = columns.findIndex(v=>v.property === 'deleteNum');
const greetingNumIndex = columns.findIndex(v=>v.property === 'passNum');
if(passNumIndex > -1 && greetingNumIndex > -1){
sums[index] = sums[greetingNumIndex] === 0 ? 0 : sums[passNumIndex] / sums[greetingNumIndex] * 100;
} else{
sums[index] = '/';
column.hasPercentSign = false;
}
}
if(column.hasPercentSign){
sums[index] = sums[index].toFixed(2);
sums[index]+= '%';
}
} else {
sums[index] = '/';
}
});
return sums;
}
},
needPagination:{ // 是否需要分页
type: Boolean,
required: false,
default: true,
},
columDefaultShow:{
type:Array,
default:()=>{
return [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
}
},
rowKey:{
type: Function,
default:(row)=>{
let checkRepeatObj = {}
if (row) {
if (row.id) {
if (checkRepeatObj[row.id]) {
if (!row._secondId) {
row._secondId = Math.random() + ''
}
/** 方便根据key重用元素 */
return row._secondId
} else {
checkRepeatObj[row.id] = 1
return row.id
}
}
// row.id不存在
if (!row._secondId) {
row._secondId = Math.random() + ''
}
return row._secondId
} else {
// row为空
return Math.random() + ''
}
}
}
},
data(){
return{
checkRepeatObj: {},
tableData:[],
loading: false,
count: 0,
initParams:{},
dialoShow: false,
checkList: [],
tableColumnList: [],
multipleSelection: [],
}
},
created(){
for(const val of this.columDefaultShow) {
if(this.columnList[val])this.tableColumnList.push(this.columnList[val]);
}
},
methods:{
timeFormat(time, type="YYYY-MM-DD HH:mm:ss"){
return time ? dateUtil.format(new Date(time), type) : '';
},
clearSelection(){
this.$refs.multipleTable.clearSelection();
},
handleSelectionChange(val){
this.$emit('handleSelectionChange',val);
},
async initData(obj={}){
this.initParams = Object.assign(this.initParams,obj);
const data = Object.assign({
pageSize: this.$refs.myPagination.getPageSize(),
pageNo: this.$refs.myPagination.getPageNo() - 1,
}, this.dataParams,this.initParams);
const methods = this.fetchMethods.toLowerCase();
this.loading = true;
if(methods === 'get' ){
await this.$get(this.url, data, {timeout:60000}).then((res) => {
this.loading = false;
this.tableData = Object.assign([], res.list ? res.list : res.data ? res.data : []);
this.count = res.count ? res.count : 0;
this.$emit('getTableData',this.tableData, this.count);
}).catch((err) => {
this.loading = false;
this.tableData = [];
this.count = 0;
this.$emit('getTableData',this.tableData, this.count);
});
} else {
await this.$post(this.url, data, {timeout:60000}).then((res) => {
this.loading = false;
this.tableData = Object.assign([], res.list ? res.list : res.data ? res.data : [])
this.count = res.count ? res.count : 0;
this.$emit('getTableData',this.tableData, this.count);
}).catch((err) => {
this.loading = false;
this.tableData = [];
this.count = 0;
this.$emit('getTableData',this.tableData, this.count);
});
}
},
setTableData(data=[]){
if(this.$refs.myPagination)this.$refs.myPagination.setPageNo();
if(this.$refs.myPagination)this.$refs.myPagination.setPageSize();
this.loading = false;
this.tableData = Object.assign([],data);
this.count = this.tableData.length;
},
async search(obj={}, resetPageSize=true){
if(this.$refs.myPagination)this.$refs.myPagination.setPageNo();
if(this.$refs.myPagination&&resetPageSize)this.$refs.myPagination.setPageSize();
await this.initData(obj);
},
sortChange(row){
this.$emit('sortChange', row);
},
getPageSize(){
return this.$refs.myPagination.getPageSize();
},
getPageNo(){
return this.$refs.myPagination.getPageNo();
},
headerClick(column, event){
if(column.type === "selection")return;
this.dialoShow = true;
this.checkList = this.tableColumnList.map(v=>v.label);
},
updateSave(){
this.tableColumnList = this.columnList.filter(v=>this.checkList.includes(v.label));
this.dialoShow = false;
},
},
components:{myRender,myPagination,myDialog}
}
</script>
<style scoped>
.checkBox .checkOne{
width: 20%;
margin-bottom: 16px;
}
.headImg, .headImg img{
width: 36px;
flex-shrink: 0;
flex-basis: 36px;
height: 36px;
}
</style>
Tips:文中myPagination组件和myDialog组件都是我自己分装的分页和弹窗组件,可查看我的上两篇文章找到相应代码,也可以直接点击链接跳转过去myPagination和myDialog
使用方式:
// xxx.vue中
<template>
<el-form :inline="true" class="demo-form-inline" @submit.native.prevent>
<el-form-item label="邮箱" >
<el-input placeholder="请输入邮箱" v-model.trim="email" clearable @keydown.enter.native="search"></el-input>
</el-form-item>
<el-form-item label="状态" >
<el-select filterable clearable v-model="status" placeholder="请选择状态" @change="search">
<el-option
v-for="item in statusList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-button type="primary" @click.stop="search" size="small" class="blueBgBtn ">查询</el-button>
<el-button type="primary" @click.stop="addGateway" size="small" class="grayBgBtn">新增</el-button>
</el-form>
<myTable ref="myTable" :columnList="columnList" :dataParams="dataParams" url="/workapi/chatgptToken/queryList" >
<template v-slot:columnFoot>
<el-table-column label="操作" width="120">
<template slot-scope="scope">
<i class="el-icon-edit iconBlueBtn iconMarRight" title="编辑" @click.stop="handleEdit(scope.row)"></i>
</template>
</el-table-column>
</template>
</myTable>
</template>
<script>
import myTable from "@/components/myTable.vue";
export default{
data(){
this.columnList = [
{label: '邮箱', prop:'email'},
{label: 'token', prop:'token'},
{label: '状态', prop:'status', render: (h,{row}) => {
const statusObj = this.statusList.find(v=>v.id === row.status);
return h('div', {
class: {
redText: row.status === 0,
blueText: row.status === 1,
}
}, statusObj ? statusObj.name : '')
}},
{label: '备注', prop:'remark',render:(h,params)=>{
if(!params.row.remark || params.row.remark.length < 20){
return h('div', params.row.remark);
} else {
return h("el-popover",{
attrs:{
placement: 'top-start',
width: '300',
trigger: 'hover',
content: params.row.remark
}
},[h('div', {
slot: 'reference',
class:{
nowrapEllipsis: true,
poiner: true
},
}, params.row.remark)])
}
}},
{label: '创建人', prop:'creatorName'},
{label:'创建时间', prop: 'createTime', isFormatTime:true,width:160},
]
},
computed:{
dataParams(){
return {
email:this.email,
status:this.status,
}
}
},
methods:{
initData(){
this.$refs.myTable.initData(this.dataParams);
},
search(){
this.$refs.myTable.search(this.dataParams);
},
handleEdit(row){
console.log(row)
}
},
components:{ myTable}
}
</script>