关于vue3:头部置顶20

52次阅读

共计 2835 个字符,预计需要花费 8 分钟才能阅读完成。

因为之前的头部置顶 1.0 存在一些问题。当头部上方还须要置顶一些其余货色时,比方导出案件、筛选框、搜寻框、题目等等时,之前的代码不够灵便,没有设置置顶的偏移量。当页面内关上抽屉反复利用 table 时,也会存在问题,因为里面的滚动曾经让头部置顶了,关上抽屉时,table 的 head 仍然在,并且会遮挡数据。

因而重写了置顶的 js 代码,通过监听对应 dom 的滚动,而不是监听 window。

// 判断是否呈现滚动条
function hasScrolled(el: HTMLElement, direction = 'vertical') {if (direction === 'vertical') {return el.scrollHeight > el.clientHeight;} else if (direction === 'horizontal') {return el.scrollWidth > el.clientWidth;}
}

// 找到有滚动条的 dom
function findScrollDom(el: HTMLElement) {if (!el) return null;
  const isScrolled = hasScrolled(el);
  if (isScrolled) {return el;}
  const pEl = el.parentElement;
  if (pEl) {return findScrollDom(pEl);
  } else {return null;}
}
function useListenerScroll() {const containerRef = ref();
  const isFixed = ref(false);
  const width = ref('100%'); // 置顶容易的宽度
  const top = ref('51px'); // 默认的定位高度,导航栏高度
  const titleHeight = ref(0); // head 上放的 title 或其余操作的容器高度

  const initEvent = async () => {await nextTick();

    // 如果 table 组件为空,递归调用
    if (!containerRef.value) {initEvent();
      return;
    }
    
    // 找到滚动容器
    const scrollDom = findScrollDom(containerRef.value);
    if (scrollDom) {const titleDom = scrollDom.getElementsByClassName('table-title-content');
      // 获取 title 内容高度
      if (titleDom[0]) titleHeight.value = titleDom[0].clientHeight;
      else titleHeight.value = 0;
      scrollDom.addEventListener('scroll', listenerScroll);
      top.value = scrollDom.offsetTop + 'px';
    }
  };

  function listenerScroll(event: any) {
    const $el = event.target;
    if ($el) {
      const scrollTop = $el.scrollTop;
      const fixedDom = $el.getElementsByClassName('table-content');
      if (fixedDom.length) {const dom = fixedDom[0];
        const domTop = dom.offsetTop - titleHeight.value - 51;
        
        // 滚动高度大于设定高度时,置顶头部
        if (scrollTop > domTop) {isFixed.value = true;}
        if (scrollTop < domTop) {
          isFixed.value = false;
          width.value = dom.clientWidth + 'px';
        }
      }
    }
  }

  return {
    top,
    width,
    isFixed,
    containerRef,
    initEvent,
  };
}
<template>
  <div ref="containerRef">
    <div :style="fixedHeaderStyle">
      <div class="table-title-content" v-if="$slots['w1-title'] || title">
        <span v-if="title">{{title}}</span>
        <slot name="title" />
      </div>
      <div>
        <a-table
          :pagination="false"
          v-bind="$attrs"
          :data-source="[]"
        >
          <template #[item]="data" v-for="item in Object.keys($slots)">
            <slot :name="item" v-bind="data" />
          </template>
        </a-table>
      </div>
    </div>
    <a-table
      class="table-content"
      v-bind="$attrs"
      :dataSource="dataSource"
      v-loading="loading"
    >
      <template #[item]="data" v-for="item in Object.keys($slots)">
        <slot :name="item" v-bind="data"> </slot>
      </template>
    </a-table>
  </div>
</template>

<script lang="ts">
  import {computed, defineComponent, onMounted} from 'vue';
  import {isFunction} from '/@/utils/is';
  import useListenerScroll from './hooks/useListenerScroll';

  export default defineComponent({
    props: {
      dataSource: {type: Array,},
      loading: {type: Boolean,},
      title: {type: String,},
    },
    setup(props) {const { isFixed, containerRef, initEvent, width, top} = useListenerScroll();
      const fixedHeaderStyle = computed(() => ({
        position: isFixed.value ? 'fixed' : 'relative',
        top: isFixed.value ? top.value : 0,
        zIndex: isFixed.value ? 19 : 0,
        width: width.value,
      }));

      onMounted(() => {initEvent();
      });

      return {
        isFixed,
        containerRef,
        fixedHeaderStyle,
      };
    },
  });
</script>

正文完
 0