ScrollTable.vue 4.16 KB
<template>
  <div class="scroll-table-container" :style="{ height: containerHeight }">
    <!-- 表头部分 -->
    <div class="table-header" v-if="$slots.header">
      <slot name="header"></slot>
    </div>
    <div class="table-header default-header" v-else>
      <span
        class="header-item"
        v-for="(column, index) in columns"
        :key="index"
        :style="{ width: column.width }"
      >
        {{ column.label }}
      </span>
    </div>

    <!-- 列表内容 -->
    <ul
      class="scroll-list"
      ref="scrollList"
      @mouseenter="cancelScroll"
      @mouseleave="autoScroll"
    >
      <li
        class="list-item"
        v-for="(item, index) in tableData"
        :key="index"
        :class="getItemClass(item)"
      >
        <slot name="item-content" :item="item" :index="index">
          <div class="default-item-content">
            <span
              class="content-item"
              v-for="(column, colIndex) in columns"
              :key="colIndex"
              :style="{ width: column.width }"
            >
              {{ getItemField(item, column.field) }}
            </span>
          </div>
        </slot>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "ScrollTable",
  props: {
    data: {
      type: Array,
      required: true,
      default: () => [],
    },
    height: {
      type: String,
      default: "300px",
    },
    autoScrollEnabled: {
      type: Boolean,
      default: true,
    },
    scrollSpeed: {
      type: Number,
      default: 1200,
    },
    columns: {
      type: Array,
      required: true,
      default: () => [],
    },
  },
  data() {
    return {
      selectedItems: [],
      scrollTimer: null,
      tableData: [],
    };
  },
  computed: {
    containerHeight() {
      return this.height;
    },
  },
  watch: {
    data: {
      immediate: true,
      handler(newData) {
        this.tableData = newData;
      },
    },
  },
  methods: {
    getItemClass(item) {
      return item.class || {};
    },
    getItemField(item, fieldKey) {
      return item[fieldKey] || "";
    },
    autoScroll() {
      if (!this.autoScrollEnabled) return;

      const divData = this.$refs.scrollList;
      if (!divData) {
        console.error("ScrollList ref is undefined");
        return;
      }

      const scrollStep = this.scrollSpeed / 60; // fps设为60
      divData.scrollTop += 1;

      if (Math.round(divData.scrollTop + divData.clientHeight) >= divData.scrollHeight) {
        divData.scrollTop = 0;
      }

      this.scrollTimer = window.requestAnimationFrame(this.autoScroll.bind(this));
    },
    cancelScroll() {
      window.cancelAnimationFrame(this.scrollTimer);
    },
  },
  mounted() {
    this.$nextTick(() => {
      if (this.autoScrollEnabled) {
        this.autoScroll();
      }
    });
  },
  beforeDestroy() {
    window.cancelAnimationFrame(this.scrollTimer);
  },
};
</script>

<style scoped>
.scroll-table-container {
  overflow: hidden;
  position: relative;
}

.table-header {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-around;
  padding: 10px;
  background: linear-gradient(0deg, rgba(61,98,147,0.35) 0%, rgba(61,98,147,0.03) 100%);
}

.default-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.header-item {
  margin-right: 10px;
  display: inline-block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.scroll-list {
  scrollbar-width: none; /* Firefox */
  -ms-overflow-style: none; /* IE 10+ */
  overflow-x: hidden;
  overflow-y: auto;
  height: calc(100% - 40px); /* Adjust height to account for header */
  padding: 0;
}

.scroll-list::-webkit-scrollbar {
  display: none; /* Chrome Safari */
}

.list-item {
  padding: 10px;
  margin-bottom: 5px;
  height: 36px;
  font-size: 16px;
  color: #BBD7EA;
  position: relative;
  display: flex;
  align-items: center;
  background-color: rgba(61, 98, 147,.2);
}
.custom-header-item{
  font-weight: bold;
}
.default-item-content {
  display: flex;
  align-items: center;
  width: 100%;
}

.content-item {
  margin-right: 10px;
  display: inline-block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
</style>