<template>
  <div class="base_list" ref="listRef">
    <div v-if="loading && list.length === 0" class="base_list__loading">
      <van-loading size="24px">加载中...</van-loading>
    </div>
    <template v-for="(item, index) in list" :key="item.id">
      <slot :item="item" :index="index" />
    </template>
    <div v-if="loading && list.length > 0" class="base_list__loading">
      <van-loading size="24px">加载中...</van-loading>
    </div>
    <div v-if="finished && list.length > 0" class="base_list__finished">{{ finishedText }}</div>
    <div v-if="finished && list.length === 0 && !loading" class="base_list__empty">暂无数据</div>
    <div ref="base_list__sentinel" class="base_list__sentinel"></div>
  </div>
</template>

<script>
export default {
  name: 'BaseList',
  props: {
    url: { type: String, required: true },
    method: { type: String, default: 'get' },
    params: { type: Object, default: () => ({}) },
    pageSize: { type: Number, default: 10 },
    finishedText: { type: String, default: '没有更多了' },
    parseData: { type: Function, default: (res) => res.data.items },
  },
  data() {
    return {
      loading: false,
      finished: false,
      list: [],
      page: 1,
      requestId: 0,
      observer: null,
    }
  },
  emits: ['update:list', 'load', 'refresh'],
  expose: ['refresh'],
  watch: {
    url() {
      this.refresh()
    },
    params: {
      handler() {
        if (this._skipWatch) return
        this.refresh()
      },
      deep: true
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.observer = new IntersectionObserver((entries) => {
        if (entries[0].isIntersecting && !this.loading && !this.finished) {
          this.loadMore()
        }
      }, { rootMargin: '100px' })
      if (this.$refs.base_list__sentinel) {
        this.observer.observe(this.$refs.base_list__sentinel)
      }
    })
  },
  methods: {
    loadMore() {
      if (this.loading) return
      const currentRequestId = this.requestId
      this.loading = true
      const request = this.method === 'get' ? this.$get : this.$post
      request(this.url, { page: this.page, pageSize: this.pageSize, ...this.params })
        .then(res => {
          if (currentRequestId !== this.requestId) return
          const data = this.parseData(res)
          const safeData = data || []

          if (this.page === 1) {
            this.list = safeData
          } else {
            this.list.push(...safeData)
          }
          this.page++
          this.finished = safeData.length < this.pageSize
          this.$emit('update:list', this.list)
          this.$emit('load', this.list)
        })
        .catch(err => {
          if (currentRequestId !== this.requestId) return
          this.$showFailToast(err.message || '加载失败')
        })
        .finally(() => { this.loading = false })
    },
    refresh() {
      this.requestId++
      this.list = []
      this.page = 1
      this.finished = false
      this.loading = false
      this._skipWatch = true
      this.loadMore()
      this.$emit('refresh', this.list)
      // 等 params 的 deep watcher 排队回调跑完再解锁，避免同一轮 tick 里被再触发一次
      this.$nextTick(() => { this._skipWatch = false })
    },
  },
}
</script>

<style scoped>
.base_list {
  min-height: 200px;
  display: flex;
  flex-direction: column;
}

.base_list__loading {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 20px;
  width: 100%;
  box-sizing: border-box;
}

.base_list__loading :deep(.van-loading) {
  display: inline-flex;
  align-items: center;
  flex-direction: row;
  white-space: nowrap;
  float: none !important;
}

.base_list__loading :deep(.van-loading__text) {
  float: none !important;
}

.base_list__finished,
.base_list__empty {
  width: 100%;
  text-align: center;
  padding: 15px;
  color: #969799;
  font-size: 14px;
}
</style>