<!--
* @description 表格组件
* @fileName TableList.vue
* @authorQ
* @date 2021/05/15 15:13:45
-->
<template>
<div class="table-container">
<el-table
v-if="showTable"
ref="filterTable"
:span-method="spanName?objectSpanMethod:null"
:data="data"
:show-summary="showSummary.show ? showSummary.show : null"
:summary-method="showSummary.show ? summaryMethod : null"
:sum-text="showSummary.sumText ? showSummary.sumText : null"
@filter-change="handleFilterChange"
:border="border"
:stripe="stripe"
empty-text="暂无数据"
style="width: 100%"
:show-header="showHeader"
:tooltip-effect="tooltipTheme"
@selection-change="handleSelectionChange"
>
<!-- 选择框列 -->
<el-table-column
v-if="selection"
type="selection"
:align="'center'"
></el-table-column>
<!-- 排序列 -->
<el-table-column
v-if="indexShow"
width="100"
label="序号"
:align="'center'"
>
<template slot-scope="scope">
<div>
<span>{{ scope.$index + 1 }}</span>
</div>
</template>
</el-table-column>
<template v-for="(item, index) in columns">
<!-- 特殊列处理 -->
<template v-if="item.render">
<!-- visible 是否显示该列 -->
<el-table-column
v-if="item.visible"
:filters="item.filters ? item.filters : null"
:column-key="item.prop"
:key="index"
:prop="item.prop ? item.prop : null"
:align="item.align ? item.align : null"
:fixed="item.fixed ? item.fixed : null"
:label="item.label ? item.label : null"
:show-overflow-tooltip="item.tooltip"
:class-name="className"
:sortable="item.sortable ? item.sortable : false"
:width="item.width ? item.width : null"
>
<!-- 多级表头 -->
<template v-if="item.children && item.children.length > 0">
<div v-for="(item1, index1) in item.children" :key="index1">
<el-table-column
:key="index1"
:filters="item.filters ? item.filters : null"
:column-key="item.prop"
:prop="item1.prop ? item1.prop : null"
:align="item1.align ? item1.align : null"
:fixed="item1.fixed ? item1.fixed : null"
:label="item1.label ? item1.label : null"
:show-overflow-tooltip="item1.tooltip"
:class-name="className"
:sortable="item1.sortable ? item1.sortable : false"
:width="item1.width ? item1.width : null"
>
<exSlot
:render="item1.render"
:row="scope.row"
:index1="scope.$index"
:column="item1"
/>
</el-table-column>
</div>
</template>
<!-- 不是多级表头 -->
<template slot-scope="scope">
<exSlot
:render="item.render"
:row="scope.row"
:index="scope.$index"
:column="item"
/>
</template>
</el-table-column>
</template>
<!-- 正常列 -->
<template v-else>
<!-- visible 是否显示该列 -->
<el-table-column
v-if="item.visible"
:key="index"
:column-key="item.prop"
:filters="item.filters ? item.filters : null"
:prop="item.prop ? item.prop : null"
:align="item.align ? item.align : null"
:fixed="item.fixed ? item.fixed : null"
:label="item.label ? item.label : null"
:class-name="className"
:show-overflow-tooltip="item.tooltip"
:sortable="item.sortable ? item.sortable : false"
:width="item.width ? item.width : null"
>
<!-- 多级表头 -->
<template v-if="item.children && item.children.length > 0">
<template v-for="(item1, index1) in item.children">
<el-table-column
:prop="item1.prop ? item1.prop : null"
:column-key="item.prop"
:filters="item.filters ? item.filters : null"
:align="item1.align ? item1.align : null"
:fixed="item1.fixed ? item1.fixed : null"
:label="item1.label ? item1.label : null"
:show-overflow-tooltip="item1.tooltip"
:class-name="className"
:sortable="item1.sortable ? item1.sortable : false"
:width="item1.width ? item1.width : null"
>
<template slot-scope="scope">
<span v-html="formatter(scope.row[item1.prop])"></span>
</template>
</el-table-column>
</template>
</template>
<template slot-scope="scope">
<!-- 字典处理 -->
<template v-if="item.dict">
<!-- 判断原始数据是否有效,有效转为字典数据,无效则转为--占位符 -->
<span
v-if="!scope.row[item.prop]"
v-html="formatter(scope.row[item.prop])"
></span>
<dict-tag
v-else
:options="dict.type[item.prop]"
:value="scope.row[item.prop]"
/>
</template>
<!-- 时间格式化 -->
<span v-else-if="item.time">{{
parseTime(scope.row[item.prop], "{y}-{m}-{d}")
}}</span>
<!-- 不做处理 -->
<span v-else v-html="formatter(scope.row[item.prop])"></span>
</template>
</el-table-column>
</template>
</template>
<!-- 操作列 -->
<el-table-column
v-if="isEdit === true"
label="操作"
:align="'center'"
width="200"
:fixed="fixed"
>
<template slot-scope="scope">
<slot name="editSlot">
<template>
<el-button type="primary" @click="editClick(scope.row)"
>编辑</el-button
>
<el-button type="danger" @click="deleteClick(scope.row)"
>删除</el-button
>
</template>
</slot>
</template>
</el-table-column>
</el-table>
<el-pagination
:style="{ float: `${paginationPosition}` }"
v-if="pagination"
:background="background"
:current-page.sync="currentPage"
:page-size.sync="currentSize"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script>
// 自定义内容的组件
var exSlot = {
functional: true,
props: {
row: Object,
render: Function,
index: Number,
column: {
type: Object,
default: null,
},
},
// render 函数
render: (h, context) => {
const params = {
row: context.props.row,
index: context.props.index,
};
if (context.props.columns) params.columns = context.props.columns;
return context.props.render(h, params);
},
};
export default {
name: "TableList",
components: { exSlot },
props: {
data: {
type: Array,
default: () => [],
},
update: {
type: Number,
default: () => null,
},
// 是否显示合计列
showSummary: {
type: Object,
default: () => {
return {
show: false,
sumText: "总计",
};
},
},
// 需要通过那个字段属性进行比较合并
spanName: {
type: String,
default: () => {
return null;
},
},
// 需要合并数组
spanColumnArr:{
type:Array,
default:()=>{
return [0]
}
},
tooltipTheme: {
type: String,
default: "dark",
},
// 是否显示表头
showHeader: {
type: Boolean,
default: true,
},
// 是否添加排序列
indexShow: {
type: Boolean,
default: true,
},
showTable: {
type: Boolean,
default: true,
},
// 是否显示选择框列
selection: {
type: Boolean,
default: false,
},
// 字段名
columns: {
type: Array,
default: () => [],
},
// 是否含有边框
border: {
type: Boolean,
default: false,
},
// 是否显示斑马条纹
stripe: {
type: Boolean,
default: false,
},
// 是否是可以编辑的表格
isEdit: {
type: Boolean,
default: false,
},
// 是否固定右侧一列,只对右侧操作栏起作用
fixed: {
type: String,
default: "right",
},
// 是否显示分页
pagination: {
type: Boolean,
default: false,
},
// 分页的位置
paginationPosition: {
type: String,
default: "center",
},
total: {
required: false,
type: Number,
},
page: {
type: Number,
default: 1, // 默认第一页
},
limit: {
type: Number,
default: 10, // 默认每页20条
},
pageSizes: {
type: Array,
// default: [10, 20, 30, 50]
default: function () {
return [1, 2, 3, 5]; // 默认显示可选的每页多少条数据
},
},
layout: {
type: String,
default: "total, sizes, prev, pager, next, jumper",
},
background: {
type: Boolean,
default: true,
},
autoScroll: {
type: Boolean,
default: true,
},
hidden: {
type: Boolean,
default: false,
},
className: {
type: String,
default: "",
},
render: {
type: Function,
default: function () {},
},
},
data() {
return {
cloneColumns: [],
spanArr: [], // 跨行数组
pos: null, // 跨行数组
};
},
computed: {
// 当前页多少条数据并且赋值给父组件
currentPage: {
get() {
return this.page;
},
set(val) {
this.$emit("update:page", val);
},
},
// 改变当前页几条数据得值赋值给父组件
currentSize: {
get() {
return this.limit;
},
set(val) {
this.$emit("update:limit", val);
},
},
},
created() {
this.cloneColumns = JSON.parse(JSON.stringify(this.columns));
},
watch: {
columns(value) {},
},
mounted() {
if (this.spanName) {
this.getSpanArr(this.data);
}
},
methods: {
// 合同单元格直接传入:spanName="id或者其他字段属性名"
getSpanArr(data) {
for (let i = 0; i < data.length; i++) {
if (i === 0) {
this.spanArr.push(1);
this.pos = 0;
} else {
// 判断当前的元素和上一个元素是否相同
// name 需要通过哪个字段来进行比较合并
if (data[i][this.spanName] === data[i - 1][this.spanName]) {
this.spanArr[this.pos] += 1;
this.spanArr.push(0);
} else {
this.spanArr.push(1);
this.pos = i;
}
}
}
},
// 合并
objectSpanMethod({ row, column, rowIndex, columnIndex }) {
if (this.spanColumnArr.includes(columnIndex)) {
const _row = this.spanArr[rowIndex];
const _col = _row > 0 ? 1 : 0;
return {
rowspan: _row,
colspan: _col,
};
}
},
// 自定义合计行
summaryMethod(params) {
const { columns, data } = params;
const sums = [];
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = "总计";
return;
}
const values = data.map((item) => item[column.property]);
let num = 0;
for (let i = 0; i < values.length; i++) {
if (values[i]) {
num += Number(values[i]);
sums[index] = num;
} else {
if (isNaN(values[i])) {
} else {
num += Number(values[i]);
sums[index] = num;
}
}
}
if (column.property === "lv1Time" || column.property === "lv2Time") {
if (values.filter(Boolean).length === 0) {
sums[index] = "";
} else {
let num = 0;
for (let i = 0; i < values.length; i++) {
if (values[i]) {
num += Number(values[i]);
sums[index] = num;
} else {
if (isNaN(values[i])) {
} else {
num += Number(values[i]);
sums[index] = num;
}
}
}
}
}
});
let arr = [];
sums.map((i, index) => {
if (index === 0) {
arr.push(i);
} else {
if (!isNaN(i)) {
arr.push(i);
} else {
arr.push("");
}
}
});
return arr;
},
handleFilterChange(filters) {
if (filters.length > 0) {
this.$refs.filterTable.clearSelection();
this.$refs.filterTable.toggleRowSelection(filters.pop());
} else {
this.columns.map((item) => {
// 判断当前是那一列进行了筛选
if (item.prop === Object.keys(filters)[0]) {
item.filterValues = filters;
}
});
this.$emit("filtersParams", this.columns);
}
},
// 当前行当前列数据是否有效,无效的话,返回--占位符
formatter(row) {
if (row) {
return isNaN(parseFloat(row)) && isFinite(row)
? '<span class="isNaN">--</span>'
: row;
} else {
return '<span class="isNaN">--</span>';
}
},
/**
* @param {*}
* @return {*}
* @author: Q
* @Date: 2021-09-01 11:56:37
* @description: 已选的数据项
*/
handleSelectionChange(val) {
this.$emit("selectVal", val);
},
handleSizeChange(val) {
this.pageSize = val;
this.$emit("pagination", { pageIndex: this.page, pageSize: val });
},
handleCurrentChange(val) {
this.$emit("pagination", { pageIndex: val, pageSize: this.limit });
},
// 修改按钮的点击事件
editClick(val) {
this.$emit("edit", val);
},
// 删除按钮点击事件
deleteClick(val) {
this.$confirm("此操作将永久删除该文件, 是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
this.$emit("del", val);
})
.catch(() => {
this.$message({
type: "info",
message: "已取消删除",
});
});
},
},
};
</script>
<style lang="scss">
.table-container {
text-align: center;
.el-pagination {
margin-left: 0 !important;
margin-top: 0 !important;
background: #fff !important;
height: 112px;
line-height: 112px;
display: flex;
justify-content: center;
align-items: center;
ul {
display: flex;
justify-content: space-around;
align-items: center;
li {
border-radius: 6px !important;
color: #666666 !important;
}
.active {
color: #fff !important;
background: #1677ffff !important;
}
}
button {
border-radius: 6px !important;
color: #666666 !important;
}
}
}
.table-container.el-table {
margin-top: 20px;
}
.table-container .el-pagination {
margin-top: 20px;
margin-left: 20px;
}
.isNaN {
color: red;
}
</style>