uniapp mixin 使用

创建 ListMoreDataMixin

// 由于没有超类的限制这里要判断下
function ____checkGetData(context) {
  if (!context.getData || typeof context.getData !== "function") {
    throw new Error("使用[ListMoreDataMixin]必须实现getData函数");
  }
}

export const ListMoreDataMixin = {
  data() {
    return {
      listData: [], // 数据列表
      page: 1, // 1
      limit: 10,
      isLoading: false, // 是否在加载数据
      isRefresh: false, // 下拉刷新

      autoInitListData: true,
    };
  },
  methods: {
    // 初始化数据
    async initData() {
      ____checkGetData(this);
      this.page = 1;
      this.isLoading = true;
      const data = await this.getData();
      this.listData = data;
      this.isLoading = false;
    },

    // 上拉加载更多
    async loadMore() {
      ____checkGetData(this);
      this.isLoading = true;
      this.page += 1;

      const data = await this.getData();

      // 没有更多了
      if (!data || !data.length) {
        this.page--;
        if (this.notMoreData) {
          this.notMoreData?.();
        } else {
          uni.showToast({
            title: "到底了...",
            icon: "none",
          });
        }
      }

      this.listData = [...this.listData, ...data];
      this.isLoading = false;
    },

    // 下拉刷新
    async onRefresh(done) {
      ____checkGetData(this);
      this.isRefresh = true;
      await this.initData();
      this.isRefresh = false;
      if (done && typeof done === "function") done();
    },
  },
  mounted() {
    if (this.autoInitListData) this.initData();
  },
};

在组件中使用

<template>
    <scroll-view
      v-if="listData.length"
      :scroll-y="true"
      :scroll-with-animation="true"
      :refresher-enabled="true"
      :refresher-triggered="isRefresh"
      @refresherrefresh="onRefresh"
      @scrolltolower="loadMore"
    >
      <list-item v-for="item of listData" :key="item.id" :data="item" />
    </scroll-view>
</template>

<script>
import { ListMoreDataMixin } from 'list-more-data-mixin.js';

export default {
  mixins: [ListMoreDataMixin],
  methods: {
    // 必须实现
    async getData() {
      return this.api.get("/api/xxx", { page: this.page, limit: this.limit });
    },
  }
};
</script>

长列表

看这里

创建 LongListMoreDataMixin

// 由于没有超类的限制这里要判断下
function ____checkGetData(context) {
  if (!context.getData || typeof context.getData !== "function") {
    throw new Error("使用[LongListMoreDataMixin]必须实现getData函数");
  }
}

export const LongListMoreDataMixin = (manage) => {
  const PAGE = 1;
  const LIMIT = 20;
  return {
    data() {
      return {
        sections: [],
        page: PAGE,
        limit: LIMIT,
        isLoading: false, // 是否在加载数据
        isRefresh: false, // 下拉刷新

        autoInitListData: true,
      };
    },
    methods: {
      // 初始化数据
      async initData() {
        ____checkGetData(this);
        this.page = PAGE;
        this.isLoading = true;
        this._list = manage.clear();
        this.sections = [];
        const data = await this.getData();
        
        this._appendData(data);
        this.isLoading = false;
      },

      // 上拉加载更多
      async loadMore() {
        ____checkGetData(this);
        this.isLoading = true;
        this.page += 1;

        const data = await this.getData();

        // 没有更多了
        if (!data || !data.length) {
          this.page--;
          if (this.notMoreData) {
            this.notMoreData?.();
          } else {
            uni.showToast({
              title: "到底了...",
              icon: "none",
            });
          }
        }

        this._appendData(data);
        this.isLoading = false;
      },

      // 下拉刷新
      async onRefresh(done) {
        ____checkGetData(this);
        this.isRefresh = true;
        await this.initData();
        this.isRefresh = false;
        if (done && typeof done === "function") done();
      },

      _addSection(data) {
        if (!data.length) return;
        this.sections.push({
          begin: this._list.length - data.length,
          end: this._list.length
        })
      },
      
      _appendData(data) {
        this._list.push(...data);
        this._addSection(data);
      }
    },
    mounted() {
      if (this.autoInitListData) this.initData();
    },
  };
}

组件中将你的列表分段

<template>
  <view>
    <my-section v-for="(item, index) in sections" :key="index" :section="item"></my-section>
  </view>
</template>

<script>
import manage from './list-manage';
import { LongListMoreDataMixin } from './LongListMoreDataMixin.js';

export default {
  mixins: [LongListMoreDataMixin(manage)],
  onPullDownRefresh() {
    this.onRefresh(() => {
      uni.stopPullDownRefresh();
    });
  },
  onReachBottom() {
    this.loadMore();
  },
  methods: {
    getData() {
      return new Promise(_res => {
        this.request({
          page: this.page,
          size: this.limit,
          success: res => {
            _res(res);
          }
        });
      });
    }
  }
};
</script>

my-section:

<template>
  <my-item v-for="(item, index) in list" :key="index" :data="item"></my-item>
</template>

<script>
import manage from './list-manage';

export default {
  props: ['section'],
  data() {
    return {
      list: []
    };
  },
  created() {
    this.list = manage.getSection(this.section);
  }
};
</script>

<style></style>

list-manage.js 中导出静态列表,和一些管理函数

const manage = {
  list: [],
  clear() {
    return manage.list = []
  },
  getSection(section) {
    return manage.list.slice(section.begin, section.end);
  }
}

export default manage;

vue3 组合式函数

// ListMoreData.js

import { ref, onMounted, computed } from 'vue'

const KFirstPage = 1;

export function useListMoreData(getData, autoInitListData = true) {
  if (!getData || typeof getData !== "function") {
    throw new Error("使用[useListMoreData]必须要getData参数");
  }

  const listData = ref([]);
  const page = ref(KFirstPage);
  const limit = ref(10);
  const isLoading = ref(false);
  const isNotMore = ref(false);
  const isRefresh = ref(false);

  // more/loading/noMore
  const status = computed(() => {
    if (isNotMore.value) return 'noMore';
    if (isLoading.value) return 'loading';
    return 'more';
  });

  // 初始化数据
  const onInitData = async () => {
    page.value = KFirstPage;
    isLoading.value = true;
    const data = await getData();

    if (data.length < limit.value) {
      isNotMore.value = true;
    }

    listData.value = data;
    isLoading.value = false;
  };

  // 上拉加载更多
  const onLoadMore = async () => {
    isLoading.value = true;
    page.value += 1;

    const data = await getData();

    // 没有更多了
    if (!data || !data.length) {
      page.value--;
      isNotMore.value = true;
      uni.showToast({
        title: "到底了...",
        icon: "none",
      });
    } else {
      isNotMore.value = false;
    }

    listData.value.push(...data)
    isLoading.value = false;
  };

  // 下拉刷新
  const onRefresh = async (done) => {
    isRefresh.value = true;
    await onInitData();
    isRefresh.value = false;
    if (done && typeof done === "function") done();
  };

  onMounted(() => {
    if (autoInitListData) onInitData();
  })

  return {
    listData, // 数据列表
    page, // 1
    limit, // 每页数据条数
    isLoading, // 是否在加载数据
    isNotMore, // 没有更多了
    isRefresh, // 下拉刷新

    status,

    onInitData,
    onLoadMore,
    onRefresh,
  }
}

组件中使用

<scroll-view scroll-y="true" :scroll-with-animation="true" :refresher-enabled="true"
        :refresher-triggered="isRefresh" @refresherrefresh="onRefresh" @scrolltolower="onLoadMore">
  ...
</scroll-view>

<script setup>
	import { useListMoreData } from "./ListMoreData.js"
	import { ref } from "vue"

	const getData = () => {
		return Promise.resolve([{
			id: Date.now(),
			name: "asd"
		}, {
			id: Date.now(),
			name: "asd"
		}])
	};

const { listData, status, isLoading, isNotMore, isRefresh, onInitData, onLoadMore, onRefresh } =
  useListMoreData(getData, true);
</script>

npm i ajanuw-list-more-data

<template>
  <view class="content">
    <view> {{ lmd.status }} </view>
    <scroll-view
      scroll-y
      :scroll-with-animation="true"
      :refresher-enabled="true"
      :refresher-triggered="lmd.isRefresh"
      @refresherrefresh="lmd.onRefresh()"
      @scrolltolower="lmd.onLoadMore()"
    >
      <view v-for="el in lmd.listData"> {{ el }} </view>
      <uni-load-more
        :status="lmd.status"
        @clickLoadMore="lmd.onLoadMore()"
      ></uni-load-more>
    </scroll-view>
  </view>
</template>

<script setup>
import { ListMoreData } from "ajanuw-list-more-data";
import { reactive, onMounted } from "vue";

const data = [];
for (let i = 1; i < 14; i++) {
  data.push("name " + i);
}

const lmd = reactive(
  new ListMoreData(async (p) => {
    return new Promise((_res) => {
      setTimeout(() => {
        _res(data.slice((p.page - 1) * p.limit, p.page * p.limit));
      }, 1000);
    });
  })
);

onMounted(() => {
  lmd.onInitData();
});
</script>
posted @ 2020-07-02 17:10  Ajanuw  阅读(4756)  评论(0编辑  收藏  举报