6 changed files with 223 additions and 172 deletions
@ -1,147 +0,0 @@ |
|||||
<script setup lang="ts"> |
|
||||
import { onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'; |
|
||||
import { ref, reactive, toRefs, nextTick, onMounted } from 'vue' |
|
||||
|
|
||||
type argument = { |
|
||||
customListType?: 'default' | 'custom' | 'scroll' |
|
||||
isPerformSearch?: boolean |
|
||||
isPullDownRefresh?: boolean |
|
||||
onFormSearch: (formData: any, onSuccess: Function) => void |
|
||||
onRender?: Function |
|
||||
options?: { [n: string]: string | number | boolean } |
|
||||
emptyText?: string |
|
||||
emptyHeight?: number |
|
||||
} |
|
||||
|
|
||||
const props = withDefaults(defineProps<argument>(), { |
|
||||
customListType: 'default', |
|
||||
isPerformSearch: true, |
|
||||
isPullDownRefresh: true, |
|
||||
onFormSearch: () => { |
|
||||
console.log('onFormSearch') |
|
||||
}, |
|
||||
onRender: () => { |
|
||||
console.log('onRender') |
|
||||
}, |
|
||||
options: () => { |
|
||||
return {} |
|
||||
}, |
|
||||
emptyText: '暂无数据~', |
|
||||
emptyHeight: 60 |
|
||||
}) |
|
||||
|
|
||||
let { options, isPerformSearch, isPullDownRefresh, onFormSearch, onRender } = toRefs(props) |
|
||||
|
|
||||
const list = ref<any[]>([]) |
|
||||
const status = ref('loading') |
|
||||
const initing = ref(true) |
|
||||
const loading = ref(true) |
|
||||
const query = reactive({ |
|
||||
page: 1, |
|
||||
limit: 10, |
|
||||
total: 0 |
|
||||
}) |
|
||||
|
|
||||
const loadData = (callback?: Function) => { |
|
||||
uni.showLoading({ title: '数据加载中' }) |
|
||||
loading.value = true |
|
||||
const params = { ...query, ...options.value } as { [n: string]: string | number | boolean } |
|
||||
delete params.total |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
onFormSearch.value(params, ({ data }: { data: { data: any; total: number } }) => { |
|
||||
loading.value = false |
|
||||
initing.value = false |
|
||||
callback && callback() |
|
||||
query.page = params.page as number |
|
||||
query.limit = params.limit as number |
|
||||
query.total = data.total || 0 |
|
||||
list.value = [...list.value, ...data.data] |
|
||||
|
|
||||
status.value = query.page * query.limit >= query.total ? 'nomore' : 'loading' |
|
||||
|
|
||||
onRender.value && onRender.value(data) |
|
||||
|
|
||||
uni.hideLoading() |
|
||||
}) |
|
||||
}, 100) |
|
||||
} |
|
||||
|
|
||||
const refreshFn = (callback?: Function) => { |
|
||||
query.page = 1 |
|
||||
query.total = 0 |
|
||||
list.value = [] |
|
||||
initing.value = true |
|
||||
loadData(callback) |
|
||||
} |
|
||||
|
|
||||
const onScrolltolower = () => { |
|
||||
if (query.page * query.limit >= query.total) { |
|
||||
status.value = 'nomore' |
|
||||
} else { |
|
||||
if (loading.value) return |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
status.value = 'loading' |
|
||||
query.page += 1 |
|
||||
loadData() |
|
||||
}, 1000) |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
onMounted(() => { |
|
||||
if (isPerformSearch.value) { |
|
||||
nextTick(() => { |
|
||||
refreshFn() |
|
||||
}) |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
onPullDownRefresh(() => { |
|
||||
if (isPullDownRefresh.value) refreshFn(() => uni.stopPullDownRefresh()) |
|
||||
}) |
|
||||
|
|
||||
onReachBottom(() => { |
|
||||
if (props.customListType !== 'scroll') { |
|
||||
onScrolltolower() |
|
||||
} |
|
||||
}) |
|
||||
|
|
||||
defineExpose({ |
|
||||
list, |
|
||||
refreshFn |
|
||||
}) |
|
||||
</script> |
|
||||
|
|
||||
<template> |
|
||||
<view v-if="!initing"> |
|
||||
<block v-if="list.length > 0"> |
|
||||
<template v-if="customListType === 'custom'"> |
|
||||
<slot v-bind:data="list" /> |
|
||||
<u-loadmore :status="status" /> |
|
||||
</template> |
|
||||
<template v-else-if="customListType === 'scroll'"> |
|
||||
<scroll-view class="scroll-view" scroll-y @scrolltolower="onScrolltolower"> |
|
||||
<view v-for="(item, index) in list" :key="index"> |
|
||||
<slot v-bind:row="item" v-bind:index="index" /> |
|
||||
</view> |
|
||||
<u-loadmore :status="status" /> |
|
||||
</scroll-view> |
|
||||
</template> |
|
||||
<template v-else> |
|
||||
<view v-for="(item, index) in list" :key="index"> |
|
||||
<slot v-bind:row="item" v-bind:index="index" /> |
|
||||
</view> |
|
||||
<u-loadmore :status="status" /> |
|
||||
</template> |
|
||||
</block> |
|
||||
<ex-empty v-show="list.length <= 0 && status === 'nomore'" :height="emptyHeight" :tips="emptyText" /> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.scroll-view { |
|
||||
width: 100%; |
|
||||
height: 100%; |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,181 @@ |
|||||
|
@charset "UTF-8"; |
||||
|
#app { |
||||
|
box-sizing: border-box; |
||||
|
background-color: #f5f5f5; |
||||
|
} |
||||
|
|
||||
|
/* 颜色变量 */ |
||||
|
/*============================= 文字尺寸 =============================*/ |
||||
|
image { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
view { |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
/*============================= 弹性盒子 =============================*/ |
||||
|
.flex-end-evenly, .flex-end-around, .flex-end-between, .flex-end-end, .flex-end-center, .flex-end-start, .flex-center-evenly, .flex-center-around, .flex-center-between, .flex-center-end, .flex-center-start, .flex-start-evenly, .flex-start-around, .flex-start-between, .flex-start-end, .flex-start-center, .flex-start-start, .flex { |
||||
|
display: flex; |
||||
|
flex-wrap: nowrap; |
||||
|
} |
||||
|
|
||||
|
.flex { |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
.flex-start-start { |
||||
|
align-items: flex-start; |
||||
|
justify-content: flex-start; |
||||
|
} |
||||
|
.flex-start-center { |
||||
|
align-items: flex-start; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
.flex-start-end { |
||||
|
align-items: flex-start; |
||||
|
justify-content: flex-end; |
||||
|
} |
||||
|
.flex-start-between { |
||||
|
align-items: flex-start; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
.flex-start-around { |
||||
|
align-items: flex-start; |
||||
|
justify-content: space-around; |
||||
|
} |
||||
|
.flex-start-evenly { |
||||
|
align-items: flex-start; |
||||
|
justify-content: space-evenly; |
||||
|
} |
||||
|
.flex-center-start { |
||||
|
align-items: center; |
||||
|
justify-content: flex-start; |
||||
|
} |
||||
|
.flex-center-end { |
||||
|
align-items: center; |
||||
|
justify-content: flex-end; |
||||
|
} |
||||
|
.flex-center-between { |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
.flex-center-around { |
||||
|
align-items: center; |
||||
|
justify-content: space-around; |
||||
|
} |
||||
|
.flex-center-evenly { |
||||
|
align-items: center; |
||||
|
justify-content: space-evenly; |
||||
|
} |
||||
|
.flex-end-start { |
||||
|
align-items: flex-end; |
||||
|
justify-content: flex-start; |
||||
|
} |
||||
|
.flex-end-center { |
||||
|
align-items: flex-end; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
.flex-end-end { |
||||
|
align-items: flex-end; |
||||
|
justify-content: flex-end; |
||||
|
} |
||||
|
.flex-end-between { |
||||
|
align-items: flex-end; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
.flex-end-around { |
||||
|
align-items: flex-end; |
||||
|
justify-content: space-around; |
||||
|
} |
||||
|
.flex-end-evenly { |
||||
|
align-items: flex-end; |
||||
|
justify-content: space-evenly; |
||||
|
} |
||||
|
|
||||
|
[class*=flex-].nowrap, |
||||
|
[class^=flex].nowrap { |
||||
|
flex-wrap: nowrap; |
||||
|
} |
||||
|
[class*=flex-].stretch, |
||||
|
[class^=flex].stretch { |
||||
|
align-items: stretch; |
||||
|
} |
||||
|
[class*=flex-].row, |
||||
|
[class^=flex].row { |
||||
|
flex-direction: row; |
||||
|
} |
||||
|
[class*=flex-].row-reverse, |
||||
|
[class^=flex].row-reverse { |
||||
|
flex-direction: "row-reverse"; |
||||
|
} |
||||
|
[class*=flex-].column, |
||||
|
[class^=flex].column { |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
[class*=flex-].column-reverse, |
||||
|
[class^=flex].column-reverse { |
||||
|
flex-direction: "column-reverse"; |
||||
|
} |
||||
|
[class*=flex-].wrap, |
||||
|
[class^=flex].wrap { |
||||
|
flex-wrap: wrap; |
||||
|
} |
||||
|
[class*=flex-].wrap-reverse, |
||||
|
[class^=flex].wrap-reverse { |
||||
|
flex-wrap: "wrap-reverse"; |
||||
|
} |
||||
|
|
||||
|
.flex1 { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.flex2 { |
||||
|
flex: 2; |
||||
|
} |
||||
|
|
||||
|
.flex3 { |
||||
|
flex: 3; |
||||
|
} |
||||
|
|
||||
|
.flex4 { |
||||
|
flex: 4; |
||||
|
} |
||||
|
|
||||
|
/*============================= 文字溢出 =============================*/ |
||||
|
.text-ellipsis { |
||||
|
overflow: hidden; |
||||
|
white-space: nowrap; |
||||
|
text-overflow: ellipsis; |
||||
|
} |
||||
|
.text-ellipsis-2 { |
||||
|
overflow: hidden; |
||||
|
white-space: normal; |
||||
|
text-overflow: ellipsis; |
||||
|
display: -webkit-box; |
||||
|
-webkit-box-orient: vertical; |
||||
|
line-clamp: 1; |
||||
|
-webkit-line-clamp: 2; |
||||
|
} |
||||
|
.text-ellipsis-3 { |
||||
|
overflow: hidden; |
||||
|
white-space: normal; |
||||
|
text-overflow: ellipsis; |
||||
|
display: -webkit-box; |
||||
|
-webkit-box-orient: vertical; |
||||
|
line-clamp: 1; |
||||
|
-webkit-line-clamp: 3; |
||||
|
} |
||||
|
.text-ellipsis-4 { |
||||
|
overflow: hidden; |
||||
|
white-space: normal; |
||||
|
text-overflow: ellipsis; |
||||
|
display: -webkit-box; |
||||
|
-webkit-box-orient: vertical; |
||||
|
line-clamp: 1; |
||||
|
-webkit-line-clamp: 4; |
||||
|
} |
||||
|
|
||||
|
/*# sourceMappingURL=uni.css.map */ |
||||
Loading…
Reference in new issue