1.子组件中
<template>
<div class="content">
<div class="searchItem">
<el-form inline v-if="props.isShow">
<el-form-item>
<el-button type="primary" @click="append">添加{{ props.configurationName }}</el-button>
</el-form-item>
</el-form>
<el-table
:data="tableData"
style="width: 100%"
:border="props.border"
@selection-change="handleSelectionChange"
@row-click="rowClick"
:show-summary="showSummary"
:summary-method="getSummaries"
>
<el-table-column type="selection" width="40" v-if="isSelect" />
<el-table-column label="序号" v-if="isSerial" width="90" fixed="left">
<template v-slot="{ $index }">
{{ (paginationForm.pageNum - 1) * paginationForm.pageSize + $index + 1 }}
</template>
</el-table-column>
<template v-for="(header, index) in normalizedColumns" :key="index">
<el-table-column
v-if="header.children && header.children.length > 0 && header.isShow"
:label="header.label"
:prop="header.property"
:fixed="header.fixed"
:width="header.width"
>
<template v-for="(child, childIndex) in header.children" :key="childIndex">
<el-table-column
v-if="header.isShow"
:label="child.label"
:prop="child.property"
:width="child.width"
:sortable="child.sortable"
:show-overflow-tooltip="child.showOverflowTooltip"
:fixed="child.fixed"
>
<!-- 自定义行内容插槽 -->
<template #default="scope">
<slot name="row-slot" :row="scope.row" :column="child" :index="scope.$index">
{{ scope.row[child.property] }}
</slot>
</template>
</el-table-column>
</template>
</el-table-column>
<el-table-column
v-else-if="!header.children && header.isShow"
:label="header.label"
:prop="header.property"
:sortable="header.sortable"
:width="header.width"
:fixed="header.fixed"
>
<!-- 自定义行内容插槽 -->
<template #default="scope">
<slot name="row-slot" :row="scope.row" :column="header" :index="scope.$index">
{{ scope.row[header.property] }}
</slot>
</template>
</el-table-column>
</template>
<slot name="append"></slot>
</el-table>
<el-pagination
background
:current-page="paginationForm.pageNum"
:page-size="paginationForm.pageSize"
:page-sizes="[10, 20, 50, 100, 200]"
layout="total, sizes, prev, pager, next"
:total="paginationForm.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
style="margin-top: 20px"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, defineEmits, defineProps, ref, VNode } from 'vue';
import { ElTable, TableColumnCtx } from 'element-plus';
import http from '@/libs/service/http';
const paginationForm = reactive({
pageNum: 1,
pageSize: 10,
total: 0,
});
interface User {
date: string;
name: string;
address: string;
}
interface SummaryMethodProps<T = any> {
columns: TableColumnCtx<T>[];
data: T[];
}
interface Column {
label: string;
property?: string;
width?: string;
children?: Column[];
sortable?: boolean;
showOverflowTooltip?: boolean;
fixed?: string | boolean;
isShow?: boolean;
}
// 定义组件props
const props = defineProps<{
isShow?: boolean;
isSelect?: boolean;
border?: boolean;
total?: number;
configurationName?: string;
columns: Column[];
isPagination?: boolean;
columnType?: boolean;
isSerial?: boolean;
searchForm?: object;
showSummary?: boolean; //是否展示合计行
tableRequest?: string;
tableTitle?: string;
}>();
const tableData = ref([]);
// 定义组件emits
const emits = defineEmits(['appendList', 'currentChange', 'rowClick']);
const multipleSelection = ref<User[]>([]);
// 处理多级表头数据结构
const normalizedColumns = computed(() => {
return normalizeColumns(props.columns);
});
// 处理多级表头递归函数
function normalizeColumns(columns: Column[]): Column[] {
return columns.map((column) => {
if (column.children && column.children.length > 0) {
column.children = normalizeColumns(column.children); // 递归处理子列
}
return column;
});
}
// 处理选择改变事件
const handleSelectionChange = (val: User[]) => {
multipleSelection.value = val;
};
// 处理每页条数改变事件
const handleSizeChange = (val: number) => {
paginationForm.pageSize = val;
getTableList();
};
// 处理当前页数改变事件
const handleCurrentChange = (val: number) => {
paginationForm.pageNum = val;
getTableList();
};
// 处理行点击事件
const rowClick = (row: any) => {
emits('rowClick', row);
};
// 添加事件
const append = () => {
emits('appendList');
};
/**
* 获取列表数据
*/
const getTableList = () => {
const resData = {
current: paginationForm.pageNum,
size: paginationForm.pageSize,
...props.searchForm,
};
const tableRequest = props.tableRequest;
if (tableRequest) {
http.get(tableRequest, { ...resData }).then((res: any) => {
if (props.tableTitle == '数据大盘') {
res.records.forEach((item) => {
//填写接口返回的逻辑
});
}
tableData.value = res.records;
paginationForm.total = res.total;
});
}
};
/**
* 合计行
* 若当前行值为0则不参与计算
*/
const getSummaries = (param: SummaryMethodProps) => {
const { columns, data } = param;
const sums: (string | VNode)[] = [];
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = h('div', {}, ['总计/均值']);
return;
}
if (column.property == 'taskTime') {
sums[index] = h('div', {}, ['-']);
return;
}
const values = data
.map((item) => Number(item[column.property]))
.filter((value) => value !== 0);
if (!values.every((value) => Number.isNaN(value))) {
const sum = values.reduce((prev, curr) => {
const value = Number(curr);
if (!Number.isNaN(value)) {
if (column.property == 'roi') {
return prev + value / values.length;
} else {
return prev + value;
}
} else {
return prev;
}
}, 0);
// 控制小数位数为两位,不进行四舍五入
const formattedSum = column.property == 'roi' ? `${sum.toFixed(1)}` : `${sum.toFixed(2)}`;
sums[index] = formattedSum;
} else {
const values1 = data.map((item) => item[column.property]).filter((value) => value != 0);
if (values1.length) {
const percentageValues = values1
.map((value) => {
if (typeof value === 'string' && value.includes('%')) {
return Number(value.split('%')[0]);
} else {
return NaN;
}
})
.filter((value) => !Number.isNaN(value));
if (percentageValues.length) {
const rowValue = percentageValues
.map((item) => Number(item))
.filter((value) => value != 0);
const average = rowValue.reduce((prev, curr) => prev + curr, 0) / rowValue.length || 0;
sums[index] = `${(Math.floor(average * 100) / 100).toFixed(2)}%`;
} else {
sums[index] = '0';
}
} else {
sums[index] = '0';
}
}
});
return sums;
};
onMounted(() => {
getTableList();
});
defineExpose({
getTableList,
});
</script>
<style lang="less">
.content {
margin-top: 10px;
border: 1px solid #eee;
padding: 20px;
border-radius: 6px;
}
.el-table__fixed {
background-color: #f0f0f0;
}
</style>
代码中的:
<el-table-column v-if="columnType" label="操作" width="240">
<template #default="{row}">
<slot name="append" :data="row"></slot>
</template>
</el-table-column>等同于以下写法
<slot name="append"></slot> 是使用插槽的两种方法
2.父组件中使用
<initTable
ref="DataMarketRef"
:isShow="false"
:columns="columnsList"
:searchForm="searchForm"
:tableRequest="Operate.queryDataDashboard"
isSerial
>
<template #row-slot="{ row, column }">
<div v-if="column.property == 'loginNum'">
{{列内容}}
</div>
</template>
<template #append>
<el-table-column label="操作" width="240" fixed="right">
<template v-slot="scope">
</template>
</el-table-column>
</template>
</initTable>
<!-- <template #append="row"> -->
<!-- <el-button type="primary" @click="bindProject('upDate',row.data)">增加</el-button>-->
<!-- <el-button type="info" @click="changeLog(row.data)">修改记录</el-button>-->
<!-- </template> -->