Ant Design Table 表格的无缝滚动怎么实现啊?

基于AntdTable表格无缝滚动怎么实现啊?或者有什么插件吗?

...........

阅读 1.4k
avatarAI BotBETA

对于Ant Design的Table组件,无缝滚动功能目前并没有直接提供。但是,你可以通过自定义滚动条样式,结合css来模拟无缝滚动效果。具体做法是将你的表格固定在一个高度,并让滚动条滚动到底部后立即回到顶部,这样看起来就像一个无缝的滚动效果。

不过要注意的是,这并不是真正的无缝滚动,因为实际上滚动条还是会在滚动到底部后停止,只是视觉效果上看起来像是无缝的。

如果你需要更复杂的功能,例如真正的无缝滚动或者分页加载数据等,可能需要使用第三方的插件或者自己开发相应的功能。

对于插件,你可以在GitHub等开源代码库中搜索相关的插件,看看是否有符合你需求的。你也可以尝试自己开发,使用React的hooks和生命周期函数来控制滚动条的滚动位置和加载数据等。

1 个回答

我自己写了一个无缝滚动组件,你可以拿去试一试

<!-- 使用方法:
注意点:
1.如果是弹窗中使用,最好每次打开时重新初始化一下
// 初始化
setTimeout(() => {
  stRef.value.init();
}, 50);
2.如果需要重置滚动高度
stRef.value.scrollTop(0);
3.如果有分页,paging中的三个数据,是用来判断是否已加载完成的,如果不需要分页:paging="false"

<tScrollTable ref="stRef" :table="_d.table" :paging="_d.paging" @next="getNext()">
  <template #bodyCell="{record, column}">
    <div v-if="column.dataIndex == 'actions'" class="flex justify-center items-center h-full w-full">
      <span>详情</span>
    </div>
    <template v-else>{{record[column.dataIndex]}}</template>
  </template>
</tScrollTable>
<script>

const _d = reactive({
    table: {
        head: [
            {title: '制造单位', dataIndex: 'organization_name'},
      {title: '设备类别', dataIndex: 'name'},
      {title: '操作', dataIndex: 'actions'},
        ],
        data: []
    },
    paging: {
        current: 1,
    pageSize: 15,
    total: 0
    }
})
function search() {
  _d.paging.current = 1;
  _d.table.data = [];
  getData();
}
function getNext() {
  _d.paging.current++;
  getData();
}
// 获取列表
function getData() {
    d.loading = true;
  let params = {
    pageCurrent: _d.paging.current,
    pageRows: _d.paging.pageSize,
    areaCode: _d.areaCode
  }
  http.postAction(CLOUD_STAT+"onlineData/getCodeEquipmentList", params).then(res => {
    _d.paging.total = res.rowSum; // 注意:必须要设置total
    let list = res.rowDatas;
    if(_d.paging.current == 1) {
      _d.table.data = [...list];
    }  else {
      _d.table.data.push(...list);
    }
  }).finally(() => {
    _d.loading = false;
  })
}
</script>
 -->
<template>
  <div class="t-scroll-table" :style="{height: props.height}">
        <div class="t-header"
            :style="{paddingRight: _d.headPr}">
            <div class="tr" :style="props.headerTrStyle">
                <div v-for="(item, index) in props.table.head" :key="index" 
                    class="td"
                    :style="{width: item.width, flex: item.width ? 'initial' : '1', ...props.tdStyle}">{{item.title}}</div>
            </div>
        </div>
        <div class="t-body">
            <div ref="scrollWrapperRef" class="scroll-wrapper">
              <div ref="scrollContentRef" class="scroll-content">
                  <div v-for="(aItem, aIndex) in props.table.data" :key="aIndex" class="tr" :style="props.bodyTrStyle">
                      <div v-for="(bItem, bIndex) in props.table.head" :key="bIndex" 
                          class="td"
                          :style="{width: bItem.width, flex: bItem.width ? 'initial' : '1', ...props.tdStyle}"
                          :title="aItem[bItem.dataIndex]">
                          <template v-if="slots.bodyCell">
                              <slot name="bodyCell" :column="bItem" :record="aItem" :index="aIndex" :text="aItem[bItem.dataIndex]"></slot>
                          </template>
                          <template v-else>
                              {{aItem[bItem.dataIndex]}}
                          </template>
                      </div>
                  </div>
                  <div v-if="props.paging" class="table-tip">
                    <template v-if="notEnd">
                        <LoadingOutlined style="margin-right: 5px;" />
                        正在加载中
                    </template>
                    <template v-else>{{props.table.data.length ? '没有更多了' : '暂无数据'}}</template>
                </div>
                <div v-else-if="!props.table.data.length" class="table-tip">暂无数据</div>
              </div>
          </div>
        </div>
    </div>
</template>

<script setup>
import { reactive, ref, onMounted, useSlots, computed, watch } from 'vue'
import { LoadingOutlined } from '@ant-design/icons-vue';

const scrollWrapperRef = ref();
const scrollContentRef = ref();

const slots = useSlots();
const emits = defineEmits(['next']);
const props = defineProps({
    // 包裹成高度
    height: {
        type: String,
        default: '685px'
    },
    table: {
        type: Object,
        default: () => ({
            head: [],
            data: []
        })
    },
    // 如果返回false,表示不分页
    paging: {
        type: [Object, Boolean],
        default: () => ({
            current: 1,
          pageSize: 15,
          total: 0
        })
    },
    // 表头样式
    headerTrStyle: {
        type: Object,
        default: () => ({})
    },
    // 表身样式
    bodyTrStyle:{
        type: Object,
        default: () => ({})
    },
    // td样式
    tdStyle: {
        type: Object,
        default: () => ({})
    }
})

const _d = reactive({
    headPr: 0
})

const notEnd = computed(() => {
    return props.paging ? props.paging.current*props.paging.pageSize<props.paging.total : false;
})

// 获取表格头部是否设置pdding-right
watch(() => props.table.data.length, () => {
    setTimeout(() => {
        if(scrollContentRef.value && scrollWrapperRef.value) {
            if(scrollContentRef.value && scrollWrapperRef.value) {
                _d.headPr = scrollContentRef.value.clientHeight > scrollWrapperRef.value.clientHeight ? '3px' : 0
            } else {
                _d.headPr = 0;
            }
        }
    }, 100)
}, {
    immediate: true
})

onMounted(() => {
    oScroll.init()
})

// 滚动对象
const oScroll = {
    oWrapper: null,
    oContent: null,
    wrapperHeight: 0,
    init() {
        console.log('初始化');
        oScroll.oWrapper = scrollWrapperRef.value;
        if(!oScroll.oWrapper) {
            return;
        }
        oScroll.oContent = scrollContentRef.value;
        oScroll.wrapperHeight = oScroll.oWrapper.clientHeight;
        // 设置滚动事件
        let scrollTime = 0;
        oScroll.oWrapper.onscroll = (params) => {
            if(notEnd.value && oScroll.isScrollBottom(params.target.scrollTop)) {
                let currentTime = new Date().getTime();
                if(currentTime - scrollTime > 500) { // 0.5秒内只能触发一次
                    scrollTime = currentTime;
                    console.log('触发');
                    emits('next');
                }
            }
        }
    },
    // 判断滚动是否触底
    isScrollBottom(scrollTop) {
        let contentHeight = oScroll.oContent.clientHeight;
        if(contentHeight - oScroll.wrapperHeight < scrollTop + 20) {
            return true;
        } else {
            return false;
        }
    }
}


//设置滚动高度
function scrollTop(top){
    if(oScroll.oWrapper){
        oScroll.oWrapper.scrollTop = top
    }
}

defineExpose({
    init: oScroll.init,
    scrollTop
})

</script>

<style lang="less" scoped>
.t-scroll-table {
    color: #fff;
    display: flex;
    flex-direction: column;
    font-size: 14px;
    .t-header {
        .tr {
            background: #05314A;
        }
    }
    .t-body {
        flex: 1;
        height: 0;
        .tr {
            margin-top: 10px;
            background: #07263B;
        }
    }
    .tr {
        display: flex;
        .td {
            flex: 1;
            line-height: 42px;
            padding: 0 16px;
            height: 42px;
            text-align: center;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }
    }
}
.scroll-wrapper {
    height: 100%;
    overflow: auto;
    &::-webkit-scrollbar {
      width: 3px;
    }
}
.table-tip {
    margin-top: 5px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #aaa;
}
</style>

下面是我在一个弹窗中使用的示例

<template>
  <div>
    <a-modal v-model:visible="_d.visible" title="赋码管理" width="1250px">
      <a-spin :spinning="_d.loading">
        <tScrollTable ref="stRef" :table="_d.table" :paging="_d.paging" @next="getNext()">
          <template #bodyCell="{record, column}">
            <div v-if="column.dataIndex == 'ewm'" class="flex justify-center items-center h-full w-full">
              <img class="ewm" src="@/assets/online/ewm-02.png"/>
            </div>
            <div v-else-if="column.dataIndex == 'organization_name'" :title="record[column.dataIndex]">
              {{oSimple.unitName(record[column.dataIndex])}}
            </div>
            <template v-else>{{record[column.dataIndex]}}</template>
          </template>
        </tScrollTable>
      </a-spin>
    </a-modal>
  </div>
    
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue'
import tScrollTable from "./modules/tScrollTable/index.vue";
import * as echarts from "echarts";
import dayjs from "dayjs";
import { CLOUD_STAT } from "@/api";
import { http } from "qlrz-service";
import {oSimple} from "@/utils/Tools.js";

const stRef = ref();

const _d = reactive({
  visible: false,
  areaCode: '',

  table: {
    head: [
      {title: '制造单位', dataIndex: 'organization_name'},
      {title: '设备类别', dataIndex: 'name'},
      {title: '设备代码', dataIndex: 'equipment_code'},
      {title: '设备名称', dataIndex: 'device_name'},
      {title: '安全码', dataIndex: 'ewm'},
    ],
    data: []
  },

  paging: {
    current: 1,
    pageSize: 15,
    total: 0
  }
})

onMounted(() => {
  
})


function open(areaCode) {
  _d.areaCode = areaCode || '';
  _d.visible = true;

  // 初始化
  setTimeout(() => {
    stRef.value.init();
  }, 50);

  search();
}

function search() {
  _d.paging.current = 1;
  _d.table.data = [];
  getData();
}

function getNext() {
  _d.paging.current++;
  getData();
}

// 获取列表
function getData() {
  _d.loading = true;
  let params = {
    pageCurrent: _d.paging.current,
    pageRows: _d.paging.pageSize,
    areaCode: _d.areaCode
  }
  http.postAction(CLOUD_STAT+"onlineData/getCodeEquipmentList", params).then(res => {
    _d.paging.total = res.rowSum;
    let list = res.rowDatas;
    if(_d.paging.current == 1) {
      _d.table.data = [...list];
    }  else {
      _d.table.data.push(...list);
    }
  }).finally(() => {
    _d.loading = false;
  })
}

defineExpose({
  open
})


</script>
<style lang="less" scoped>
.ewm {
  width: 28px;
  height: 28px;
}
</style>
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进