解决el-table合计行在滚动条下方的问题

方式1:

  1. 利用第三方插件el-table-horizontal-scroll

npm i el-table-horizontal-scroll -S

  1. 在main.js中引入
import horizontalScroll from 'el-table-horizontal-scroll';
Vue.use(horizontalScroll);
  1. 在需要的表格中引入自定义指令 v-horizontal-scroll=“‘always’”
   <el-table
      :data="tableData"
      :summary-method="getTotal"
      :show-summary="summary"
      v-horizontal-scroll="'always'"
      class="custom-table"
    >
  1. 修改默认样式
.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>
### 解决 `el-table` 合计显示时滚动条问题 对于 `el-table` 组件,在使用合计以及固定功能时,确实会遇到一些布局上的挑战。具体来说,当表格包含大量数据并启用了合计之后,可能会出现滚动条被遮挡的情况。 #### 使用自定义指令优化体验 为了改善这一情况,可以考虑采用自定义指令的方式处理滚动事件。通过给 `<el-table>` 添加一个名为 `v-el-table-scroll-fixed` 的自定义指令来增强组件的功能[^1]: ```html <template> <el-table v-el-table-scroll-fixed :data="tableData"> <!-- 表格内容 --> </el-table> </template> <script setup lang="ts"> import { defineDirective } from 'vue'; defineDirective('el-table-scroll-fixed', { mounted(el) { const handleScroll = () => { // 自定义逻辑实现 }; el.addEventListener('scroll', handleScroll); }, }); </script> ``` 这种方法能够有效解决固定而导致的交互障碍,使得即使在存在固定的情况下也能正常操作滚动条。 #### 调整 DOM 结构顺序 另一个有效的解决方案是从 HTML 层面上调整元素之间的相对位置关系。通常情况下,默认渲染出来的结构会使滚动条位于合计之上;而如果希望将滚动条放置于合计之下,则可以通过改变模板中的标签排方式达成目标[^3]: ```html <div class="custom-table-container"> <div class="summary-row">...</div> <!-- 总结/合计 --> <el-table :data="tableData"> ... </el-table> </div> ``` 这样做不仅可以让视觉效果更符合预期,同时也简化了 CSS 定位方面的复杂度。 #### 修改样式属性 针对特定版本(如2.15.13),还可以尝试通过对 `.el-table__fixed-right .is-scrolling-left` 类名对应的样式进微调,从而确保滚动条不会因为 z-index 或者其他因素影响用户体验[^2]: ```css .el-table__fixed-right.is-scrolling-left ::before, .el-table__fixed-left.is-scrolling-left::after { content: ''; position: absolute; top: 0; bottom: 0; width: 8px; /* 根据实际需求设定 */ } ``` 以上几种方法可以根据实际情况单独或组合应用,以达到最佳的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值