<script setup lang="ts">
import { ref, onMounted, onUnmounted, defineProps, defineEmits, watch, nextTick } from 'vue';
import { ElLoading } from 'element-plus';

const props = defineProps({
  // 是否正在加载
  loading: {
    type: Boolean,
    default: false
  },
  // 是否已加载全部数据
  finished: {
    type: Boolean,
    default: false
  },
  // 加载错误时的提示文字
  errorText: {
    type: String,
    default: '加载失败，请重试'
  },
  // 加载完成时的提示文字
  finishedText: {
    type: String,
    default: '没有更多了'
  },
  // 是否显示加载错误提示
  error: {
    type: Boolean,
    default: false
  },
  // 滚动触发加载的距离阈值（px）
  threshold: {
    type: Number,
    default: 200
  },
  // 是否立即检查并触发加载
  immediateCheck: {
    type: Boolean,
    default: true
  },
  // 容器高度，设置后将使用固定高度，否则自适应内容高度
  height: {
    type: String,
    default: ''
  }
});

const emit = defineEmits(['load', 'update:loading']);

// 列表容器引用
const listRef = ref<HTMLElement | null>(null);
// 底部加载指示器引用
const loadingRef = ref<HTMLElement | null>(null);
// 是否正在检查
const checking = ref(false);

// 创建交叉观察器
let observer: IntersectionObserver | null = null;

// 初始化观察器
const initObserver = () => {
  // 如果已经创建过观察器，先断开连接
  if (observer) {
    observer.disconnect();
  }

  // 创建新的观察器
  observer = new IntersectionObserver(
    (entries) => {
      // 如果底部元素进入视口，且不在加载中、没有全部加载完成、没有错误，则触发加载
      if (
        entries[0].isIntersecting &&
        !props.loading &&
        !props.finished &&
        !props.error &&
        !checking.value
      ) {
        check();
      }
    },
    {
      // 设置根元素为列表容器
      root: listRef.value,
      // 设置阈值，当目标元素有任何部分进入视口时触发
      threshold: 0,
      // 设置根元素的边距，提前触发加载
      rootMargin: `0px 0px ${props.threshold}px 0px`
    }
  );

  // 观察底部加载指示器
  if (loadingRef.value) {
    observer.observe(loadingRef.value);
  }
};

// 检查并触发加载
const check = () => {
  // 防止重复触发
  checking.value = true;
  // 使用 nextTick 确保 DOM 更新后再检查
  nextTick(() => {
    if (listRef.value && loadingRef.value) {
      // 如果不在加载中、没有全部加载完成、没有错误，则触发加载
      if (!props.loading && !props.finished && !props.error) {
        emit('update:loading', true);
        emit('load');
      }
      checking.value = false;
    }
  });
};

// 监听 loading 状态变化
watch(
  () => props.loading,
  (loading) => {
    // 当加载完成后，重新检查是否需要继续加载
    if (!loading && !props.finished) {
      // 使用 nextTick 确保 DOM 更新后再检查
      nextTick(() => {
        initObserver();
      });
    }
  }
);

// 监听 error 状态变化
watch(
  () => props.error,
  (error) => {
    // 当错误状态改变时，重新初始化观察器
    if (!error) {
      initObserver();
    }
  }
);

// 重试加载
const onClickErrorText = () => {
  emit('update:loading', true);
  emit('load');
};

onMounted(() => {
  // 初始化观察器
  initObserver();

  // 如果设置了立即检查，则立即触发检查
  if (props.immediateCheck) {
    check();
  }
});

onUnmounted(() => {
  // 断开观察器连接
  if (observer) {
    observer.disconnect();
    observer = null;
  }
});
</script>

<template>
  <div ref="listRef" class="yl-list" :style="{ height: height }">
    <!-- 列表内容插槽 -->
    <slot></slot>

    <!-- 底部加载状态 -->
    <div ref="loadingRef" class="yl-list__loading">
      <template v-if="loading">
        <div class="yl-list__loading-text">
          <slot name="loading">
            <el-loading v-if="loading" text="加载中..." />
          </slot>
        </div>
      </template>

      <template v-else-if="error">
        <div class="yl-list__error-text" @click="onClickErrorText">
          <slot name="error">{{ errorText }}</slot>
        </div>
      </template>

      <template v-else-if="finished">
        <div class="yl-list__finished-text">
          <slot name="finished">{{ finishedText }}</slot>
        </div>
      </template>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.yl-list {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;

  &__loading,
  &__error-text,
  &__finished-text {
    padding: 16px 0;
    color: #969799;
    font-size: 14px;
    text-align: center;
  }

  &__error-text {
    color: #f56c6c;
    cursor: pointer;
  }
}
</style>
