方式1:
- 利用第三方插件el-table-horizontal-scroll
npm i el-table-horizontal-scroll -S
- 在main.js中引入
import horizontalScroll from 'el-table-horizontal-scroll';
Vue.use(horizontalScroll);
- 在需要的表格中引入自定义指令 v-horizontal-scroll=“‘always’”
<el-table
:data="tableData"
:summary-method="getTotal"
:show-summary="summary"
v-horizontal-scroll="'always'"
class="custom-table"
>
- 修改默认样式
.custom-table {
position: relative;
::v-deep {
.el-scrollbar {
position: absolute !important;
left: 0 !important;
display: block !important;
}
.el-table--scrollable-x .el-table__body-wrapper {
&::-webkit-scrollbar {
height: 0;
}
}
.el-table--scrollable-y .el-table__body-wrapper {
&::-webkit-scrollbar {
width: 8px;
box-sizing: border-box;
}
/* 滚动条滑块 */
&::-webkit-scrollbar-thumb {
height: 5px;
border-radius: 5px;
background-color: #d2d5d9;
}
}
}
}
方式2:
<template>
<div :class="{ noSummaryTable: !summary }" class="customTable">
<div class="check-table-header">
<CustomTransfer
:options="tableColumns"
@changeTableTdVisible="changeTableTdVisible"
></CustomTransfer>
<CustomCheckAll v-if="options && options.length" :options="options" />
</div>
<el-table
:data="tableData"
v-loading="loading"
stripe
border
highlight-current-row
fit
:height="tableHeight"
ref="scrollTable"
:summary-method="getTotal"
:show-summary="summary"
show-overflow
>
<!-- el-table-column里面嵌套,实现多级表头 -->
<el-table-column
v-for="(item, i) in tableColumns"
:key="i"
v-if="
(!item.list || (item.list && checkDayList.length)) &&
(!item.isGeo || isGeo) &&
(!visibleList || !visibleList.includes(item.prop))
"
:label="item.label"
:fixed="item.fixed"
:prop="item.prop"
header-align="center"
:min-width="item.width || 120"
show-overflow-tooltip
>
<template v-if="item.list">
<template
v-if="getChildrenColumns(item) && getChildrenColumns(item).length"
>
<el-table-column
v-for="(item1, index) in getChildrenColumns(item)"
:prop="item.prop + '_' + item1"
:key="index"
:label="item1"
>
<template slot-scope="scope">
<!-- {{ item1 }} -->
{{ scope.row[item.prop][item1] }}
</template>
</el-table-column>
</template>
<el-table-column v-else>
<template slot-scope="scope"> -- </template>
</el-table-column>
</template>
<template slot-scope="scope">
<span v-if="!item.list">
{{
item.isGeo
? (countyCodeList[scope.row[item.prop]] || "") +
("(" + scope.row[item.prop] + ")")
: scope.row[item.prop]
}}</span
>
</template>
</el-table-column>
<!-- table最底部展示 (append 插入至表格最后一行之后的内容,对表格无限滚动用到该slot,插入的该行内容位于合计行之上)-->
<div
slot="append"
v-if="!(total && total <= tableData.length)"
style="
margin-top: 10px;
margin-bottom: 10px;
text-align: center;
font-size: 15px;
"
>
<div class="more-data">
<span>
{{
total && total <= tableData.length
? "已加载全部数据"
: total
? "滚动到底部加载更多"
: ""
}}</span
>
</div>
</div>
</el-table>
<div class="pagination-wrap" v-if="pagination.visible">
<el-pagination
small
hide-on-single-page
background
layout="total, prev, pager, next,sizes"
:total="pagination.total"
:page-sizes="[20, 50, 100, 200, 300, 400]"
:current-page.sync="pagination.pageNum"
:page-size="pagination.pageSize"
@current-change="toGetList"
></el-pagination>
</div>
</div>
</template>
<script>
import CustomCheckAll from "@/components/CustomCheckAll.vue";
import CustomTransfer from "@/components/CustomTransfer.vue";
import { mapActions, mapState, mapGetters } from "vuex";
import { countyCodeList } from "@/config/advertis.js";
export default {
components: { CustomCheckAll, CustomTransfer },
props: {
// 控制table最后一行是否显示合计行的变量
summary: {
type: Boolean,
default: true,
},
tableColumns: {
type: Array,
default: () => [],
},
dayColumns: {
type: Array,
default: () => [],
},
tableData: {
type: Array,
default: () => [],
},
loading: Boolean,
pagination: {
type: Object,
default: () => ({}),
},
isGeo: Boolean,
total: Number,
params: {
type: Object,
default: () => ({}),
},
type: String,
},
computed: {
...mapState("advertising", ["checkDayList"]),
...mapGetters("advertising", ["tableTotalData"]),
bodyWrapper() {
return this.$refs.scrollTable.$refs.bodyWrapper;
},
footerWrapper() {
return this.$refs.scrollTable.$refs.footerWrapper;
},
fixedWrapper() {
return this.$refs.scrollTable.$refs.fixedWrapper;
},
elTableRefs() {
return this.$refs.scrollTable.$refs;
},
},
data() {
return {
countyCodeList,
tableHeight: null,
options: null,
visibleList: null,
// 存放接口获取的合计行数据
totalData: null,
};
},
mounted() {
// 获取数据
this.handleScroll();
this.$nextTick(() => {
this.$refs["scrollTable"].doLayout();
});
this.options = this.dayColumns;
// 自定义scroll事件
this.bindEvents();
},
methods: {
// TODO 使用 CSS transform
// throttle 节流函数,组件自带的
syncPostion() {
const { scrollLeft, scrollTop, offsetWidth, scrollWidth } =
this.footerWrapper;
const {
headerWrapper,
bodyWrapper,
footerWrapper,
fixedBodyWrapper,
rightFixedBodyWrapper,
} = this.elTableRefs;
console.log(scrollTop, fixedBodyWrapper.scrollTop, "====");
if (bodyWrapper) bodyWrapper.scrollLeft = scrollLeft;
if (headerWrapper) headerWrapper.scrollLeft = scrollLeft;
if (fixedBodyWrapper) fixedBodyWrapper.scrollTop = scrollTop;
if (rightFixedBodyWrapper) rightFixedBodyWrapper.scrollTop = scrollTop;
const maxScrollLeftPosition = scrollWidth - offsetWidth - 1;
// console.log(
// "maxScrollLeftPosition",
// maxScrollLeftPosition,
// "--",
// scrollLeft
// );
if (scrollLeft >= maxScrollLeftPosition) {
console.log(fixedBodyWrapper.scrollTop, "---==-=-=-");
this.scrollPosition = "right";
} else if (scrollLeft === 0) {
this.scrollPosition = "left";
} else {
this.scrollPosition = "middle";
}
},
// 绑定scroll事件
bindEvents() {
this.footerWrapper.addEventListener("scroll", this.syncPostion, {
passive: true,
});
},
getChildrenColumns(data) {
let arr = [];
this.dayColumns.forEach((item) => {
if (
!this.hideTableList(data, item) &&
this.checkDayList.includes(item)
) {
arr.push(item);
}
});
return arr;
},
changeTableTdVisible(list) {
this.visibleList = list;
},
toGetList(page) {
// 处理页码变化逻辑
},
hideTableList(data, childData) {
if (this.$route.path == "/advertising/channelMedia") {
return (
(childData == "Day7" ||
childData == "Day14" ||
childData == "Day30" ||
childData == "Day60" ||
childData == "Day90") &&
(data.prop == "ltvMap" ||
data.prop == "revenueMap" ||
data.prop == "paidUsersMap")
);
}
return (
(childData == "Day0" && data.prop == "rrMap") ||
(childData == "Day7" &&
(data.prop == "ltvMap" ||
data.prop == "revenueMap" ||
data.prop == "paidUsersMap" ||
data.prop == "roiMap"))
);
},
/**
* @description:添加分页加载滚动事件
* @return {*}
*/
handleScroll() {
let table = this.$refs.scrollTable.bodyWrapper;
table.addEventListener("scroll", (e) => {
const { scrollTop, clientHeight, scrollHeight } = e.target;
// 检查是否滚动到底部
if (scrollHeight - scrollTop - 20 <= clientHeight) {
if (this.total > this.tableData.length && !this.loading) {
let data = this.params;
data.pageNumber = this.params.pageNumber + 1;
this.$emit("requestTableData", data);
}
}
// searchData.call(this, data, this.getGroupList, "groupListData");
});
},
// this.$refs.scrollTable.bodyWrapper滚动到初始位置
scrollToTop() {
this.$refs.scrollTable.bodyWrapper.scrollTop = 0;
},
// 获取表格合计数据
getTotal(params) {
// columns table的所有列
const { columns } = params;
let sums = []; // 要返回并展示在界面的数组
columns.forEach((element, index) => {
if (index == 0) {
sums[index] = "合计";
} else if (this.totalData != null) {
let resData = Object.keys(this.totalData);
if (element.property != undefined) {
let ele = element.property.split("_");
if (resData.includes(ele[0]) && ele.length == 1) {
sums[index] = this.totalData[ele[0]];
} else if (ele.length > 1) {
if (resData.includes(ele[0])) {
let obj = this.totalData[ele[0]];
sums[index] = obj[ele[1]];
}
} else {
sums[index] = "--";
}
} else {
sums[index] = "--";
}
}
});
return sums;
},
},
watch: {
// 监听table数据,确保table滚动条以及左侧固定列样式
tableData() {
// 当界面table的数据更新,确保横向滚动条正常拖拽
if (this.tableData && this.tableData.length > 0) {
this.tableHeight = "470";
}
// 当table没有数据,确保空table的样式正确
if (this.tableData.length == 0) {
this.tableHeight = "";
}
// 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
this.$nextTick(() => {
this.$refs["scrollTable"].doLayout();
});
},
checkDayList: {
handler(newValue, oldValue) {
this.$nextTick(() => {
this.$refs["scrollTable"].doLayout();
});
},
deep: true,
immediate: true,
},
tableTotalData: {
handler(newValue, oldValue) {
if (newValue) {
this.totalData = newValue;
}
},
deep: true,
immediate: true,
},
},
};
</script>
<style lang="scss" scoped>
.pagination-wrap {
margin-top: 20px;
::v-deep {
.el-pagination {
display: flex;
justify-content: flex-end;
}
}
}
.more-data {
text-align: center;
display: flex;
align-items: center;
justify-content: center;
span {
font-size: 14px;
display: inline-block;
height: 16px;
line-height: 16px;
}
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.loader-1 {
width: 20px;
height: 20px;
border: 5px solid #00afee;
border-bottom-color: #90cf5b;
border-radius: 50%;
-webkit-animation: rotation 1s linear infinite;
animation: rotation 1s linear infinite;
margin: auto;
}
.customTable {
::v-deep {
// 修改样式,解决表头和内容错位问题
.el-table__empty-text {
padding: 100px 0;
}
.el-table__cell {
.cell {
width: 100%;
}
}
// 解决无法拖动问题
.el-table th.el-table__cell > .cell {
margin-left: 6px;
}
.el-table__fixed,
.el-table__fixed-right {
height: calc(100% - 15px) !important;
}
.el-table__fixed::before,
.el-table__fixed-right::before {
height: 0;
}
// 去除原本的进度条
.el-table__body-wrapper {
overflow-x: hidden !important;
}
// 添加新的的进度条
.el-table__footer-wrapper {
overflow-x: auto !important;
// overflow-y: scroll !important;
width: calc(100% + 15px)!important;
position: relative;
::-webkit-scrollbar {
width: 10px;
height: 10px;
background-color: white;
border: none;
}
::-webkit-scrollbar-thumb {
background-color: #c1c1c1;
border-radius: 5px;
}
}
}
.check-table-header {
display: flex;
align-items: center;
justify-content: flex-end;
margin-bottom: 12px;
}
}
.noSummaryTable {
::v-deep {
// 去除原本的进度条
.el-table__body-wrapper {
overflow-x: auto !important;
}
// 添加新的的进度条
.el-table__footer-wrapper {
overflow-x: hidden !important;
width: 100% ;
}
}
}
</style>