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>