feat(pull-refresh): refresh pull-refresh UI (#965)

* feat(pull-refresh): 刷新UI设计

Signed-off-by: jacknan <zhumaonan@aliyun.com>

* feat(pull-refresh): 刷新demo

Signed-off-by: jacknan <zhumaonan@aliyun.com>

* feat(pull-refresh): 刷新demo

Signed-off-by: jacknan <zhumaonan@aliyun.com>

* feat(pull-refresh): 刷新demo

Signed-off-by: jacknan <zhumaonan@aliyun.com>

---------

Signed-off-by: jacknan <zhumaonan@aliyun.com>
This commit is contained in:
jacknan 2023-12-05 17:16:14 +08:00 committed by GitHub
parent dc9ba745e3
commit 878d51eee0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 835 additions and 659 deletions

View File

@ -1,12 +1,8 @@
<template>
<div class="wp100 hp100 f-r of-hidden">
<div class="w230 pt20 of-auto">
<tiny-tree-menu
class="!w213"
:data="menuData"
:filter-node-method="fn.searchMenu"
@current-change="fn.clickMenu"
></tiny-tree-menu>
<tiny-tree-menu class="!w213" :data="menuData" :filter-node-method="fn.searchMenu"
@current-change="fn.clickMenu"></tiny-tree-menu>
</div>
<div class="fi-1 f-c px20 pb30 f-c pr200 of-auto">
<!-- 标题 -->
@ -16,8 +12,7 @@
<div id="preview" class="bg-white">
<div class="mb20 py10 pl16 child<code>p4 child<code>bg-lightless">
<div class="mr20 fw-bold">
{{ state.currDemo?.name['zh-CN'] }}( <span class="allselect">{{ state.currDemo?.codeFiles[0] }}</span
>):
{{ state.currDemo?.name['zh-CN'] }}( <span class="allselect">{{ state.currDemo?.codeFiles[0] }}</span>):
</div>
<div v-html="state.currDemo?.desc['zh-CN']"></div>
</div>
@ -78,13 +73,8 @@
<!-- 右边浮动所有的demos -->
<tiny-floatbar v-if="state.demos.length > 0" class="!top120 !z1 !right25">
<div class="f12 ofy-auto">
<div
v-for="demo in state.demos"
:key="demo.demoId"
@click="fn.selectDemo(demo.demoId)"
class="w130 px10 py4 bg-light f-r f-pos-between"
:class="{ 'c-error': state.currDemo === demo }"
>
<div v-for="demo in state.demos" :key="demo.demoId" @click="fn.selectDemo(demo.demoId)"
class="w130 px10 py4 bg-light f-r f-pos-between" :class="{ 'c-error': state.currDemo === demo }">
<div class="link-primary h:c-error h:td-under ellipsis">
{{ demo.name['zh-CN'] }}
<Icon-star-icon v-if="state.currDemo === demo" style="fill: #ee343f" />

View File

@ -1,10 +1,24 @@
<template>
<tiny-pull-refresh :pullDown="pullDownRefresh" :animation-duration="1000">
<h3>hello pull-refresh</h3>
</tiny-pull-refresh>
<div>
<div class="page__hd">
<h1 class="page__title">Refresh</h1>
<p class="page__desc">刷新</p>
</div>
<div class="page__content">
<tiny-pull-refresh
animation-duration="1000"
v-model="value"
:has-more="hasMore"
@pullDown="handlerPullDownRefresh"
@pullUp="handlerPullUpLoad"
>
<div :key="item.name" v-for="item in data">{{ item.label }}</div>
</tiny-pull-refresh>
</div>
</div>
</template>
<script lang="jsx">
<script>
import { PullRefresh } from '@opentiny/vue'
export default {
@ -13,19 +27,51 @@ export default {
},
data() {
return {
pullDownRefresh: {
handler: () => this.handlerPullDownRefresh()
}
data: [...Array(30)].map((i, index) => {
return { label: `${index + 1} list data` }
}),
value: true,
hasMore: true
}
},
methods: {
handlerPullUpLoad() {
setTimeout(() => {
const length = this.data.length
for (let i = 1; i <= 10; i++) {
this.data.push({ label: `${i + length} list data` })
}
this.value = false
}, 2000)
},
handlerPullDownRefresh() {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 1000)
})
setTimeout(() => {
this.value = false
}, 2000)
}
}
}
</script>
<style scoped>
.page__hd {
padding: 40px;
}
.page__title {
font-weight: 400;
font-size: 21px;
text-align: left;
}
.page__desc {
margin-top: 5px;
color: #888;
font-size: 14px;
text-align: left;
}
.page__content {
height: 350px;
}
</style>

View File

@ -2,20 +2,22 @@
<div>
<div class="page__hd">
<h1 class="page__title">Refresh</h1>
<p class="page__desc">下拉刷新</p>
<p class="page__desc">刷新</p>
</div>
<div class="page__content">
<tiny-pull-refresh
v-model="value"
:has-more="hasMore"
@pullDown="handlerPullDownRefresh"
@pullUp="handlerPullUpLoad"
>
<div :key="item.name" v-for="item in data">{{ item.label }}</div>
</tiny-pull-refresh>
</div>
<tiny-pull-refresh
:pullDown="pullDownRefresh"
success-text="刷新成功"
animation-duration="500"
success-duration="500"
>
<h3>hello pull-refresh</h3>
</tiny-pull-refresh>
</div>
</template>
<script lang="jsx">
<script>
import { PullRefresh } from '@opentiny/vue'
export default {
@ -24,18 +26,24 @@ export default {
},
data() {
return {
pullDownRefresh: {
handler: () => this.handlerPullDownRefresh()
}
data: [...Array(30)].map((i, index) => {
return { label: `${index} list data` }
}),
value: true,
hasMore: true
}
},
methods: {
handlerPullUpLoad() {
setTimeout(() => {
this.value = false
this.hasMore = false
}, 3000)
},
handlerPullDownRefresh() {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 1000)
})
setTimeout(() => {
this.value = false
}, 3000)
}
}
}
@ -45,15 +53,21 @@ export default {
.page__hd {
padding: 40px;
}
.page__title {
font-weight: 400;
font-size: 21px;
text-align: left;
}
.page__desc {
margin-top: 5px;
color: #888;
font-size: 14px;
text-align: left;
}
.page__content {
height: 350px;
}
</style>

View File

@ -0,0 +1,66 @@
<template>
<div>
<div class="page__hd">
<h1 class="page__title">Refresh</h1>
<p class="page__desc">刷新</p>
</div>
<div class="page__content">
<tiny-pull-refresh v-model="value" :has-more="hasMore" disabled-pull-down @pullUp="handlerPullUpLoad">
<div :key="item.name" v-for="item in data">{{ item.label }}</div>
</tiny-pull-refresh>
</div>
</div>
</template>
<script>
import { PullRefresh } from '@opentiny/vue'
export default {
components: {
TinyPullRefresh: PullRefresh
},
data() {
return {
data: [...Array(30)].map((i, index) => {
return { label: `${index + 1} list data` }
}),
value: false,
hasMore: true
}
},
methods: {
handlerPullUpLoad() {
setTimeout(() => {
const length = this.data.length
for (let i = 1; i <= 10; i++) {
this.data.push({ label: `${i + length} list data` })
}
this.value = false
}, 3000)
}
}
}
</script>
<style scoped>
.page__hd {
padding: 40px;
}
.page__title {
font-weight: 400;
font-size: 21px;
text-align: left;
}
.page__desc {
margin-top: 5px;
color: #888;
font-size: 14px;
text-align: left;
}
.page__content {
height: 350px;
}
</style>

View File

@ -0,0 +1,67 @@
<template>
<div>
<div class="page__hd">
<h1 class="page__title">Refresh</h1>
<p class="page__desc">刷新</p>
</div>
<div class="page__content">
<tiny-pull-refresh v-model="value" :has-more="hasMore" disabled-pull-up @pullDown="handlerPullDown">
<div :key="item.name" v-for="item in data">{{ item.label }}</div>
</tiny-pull-refresh>
</div>
</div>
</template>
<script>
import { PullRefresh } from '@opentiny/vue'
export default {
components: {
TinyPullRefresh: PullRefresh
},
data() {
return {
data: [...Array(30)].map((i, index) => {
return { label: `${index + 1} list data` }
}),
value: false,
hasMore: true
}
},
methods: {
handlerPullDown() {
setTimeout(() => {
this.data = []
const length = this.data.length
for (let i = 1; i <= 20; i++) {
this.data.push({ label: `${i + length} list data` })
}
this.value = false
}, 3000)
}
}
}
</script>
<style scoped>
.page__hd {
padding: 40px;
}
.page__title {
font-weight: 400;
font-size: 21px;
text-align: left;
}
.page__desc {
margin-top: 5px;
color: #888;
font-size: 14px;
text-align: left;
}
.page__content {
height: 350px;
}
</style>

View File

@ -1,57 +0,0 @@
<template>
<div>
<div class="page__hd">
<h1 class="page__title">Refresh</h1>
<p class="page__desc">下拉刷新</p>
</div>
<tiny-pull-refresh
v-model="isLoading"
@refresh="onRefresh"
success-text="刷新成功"
animation-duration="500"
success-duration="2000"
:disabled="false"
>
<h3>hello pull-refresh</h3>
</tiny-pull-refresh>
</div>
</template>
<script lang="jsx">
import { PullRefresh } from '@opentiny/vue'
export default {
components: {
TinyPullRefresh: PullRefresh
},
data() {
return {
isLoading: false
}
},
methods: {
onRefresh() {
setTimeout(() => {
this.isLoading = false
}, 1000)
}
}
}
</script>
<style scoped>
.page__hd {
padding: 40px;
}
.page__title {
font-weight: 400;
font-size: 21px;
text-align: left;
}
.page__desc {
margin-top: 5px;
color: #888;
font-size: 14px;
text-align: left;
}
</style>

View File

@ -6,11 +6,10 @@
</div>
<div class="page__content">
<tiny-pull-refresh
:pullUp="pullUpLoad"
:hasMore="hasMore"
success-text="刷新成功"
animation-duration="500"
success-duration="500"
v-model="value"
:has-more="hasMore"
@pullDown="handlerPullDownRefresh"
@pullUp="handlerPullUpLoad"
>
<div :key="item.name" v-for="item in data">{{ item.label }}</div>
</tiny-pull-refresh>
@ -27,25 +26,24 @@ export default {
},
data() {
return {
data: [{ label: 'hello pull-refresh' }],
pullUpLoad: {
handler: () => this.handlerPullUpLoad()
},
data: [...Array(30)].map((i, index) => {
return { label: `${index} list data` }
}),
value: true,
hasMore: true
}
},
methods: {
handlerPullUpLoad() {
return new Promise((resolve) => {
setTimeout(() => {
if (this.data.length === 5) {
this.hasMore = false
} else {
this.data.unshift({ label: 'hello pull-refresh up' })
resolve(this.data)
}
}, 1000)
})
setTimeout(() => {
this.value = false
this.hasMore = false
}, 3000)
},
handlerPullDownRefresh() {
setTimeout(() => {
this.value = false
}, 3000)
}
}
}
@ -55,18 +53,21 @@ export default {
.page__hd {
padding: 40px;
}
.page__title {
font-weight: 400;
font-size: 21px;
text-align: left;
}
.page__desc {
margin-top: 5px;
color: #888;
font-size: 14px;
text-align: left;
}
.page__content {
height: 250px;
height: 350px;
}
</style>

View File

@ -6,11 +6,10 @@
</div>
<div class="page__content">
<tiny-pull-refresh
:pullUp="pullUpLoad"
:pullDown="pullDownRefresh"
success-text="刷新成功"
animation-duration="500"
success-duration="500"
v-model="value"
:has-more="hasMore"
@pullDown="handlerPullDownRefresh"
@pullUp="handlerPullUpLoad"
>
<div :key="item.name" v-for="item in data">{{ item.label }}</div>
</tiny-pull-refresh>
@ -27,31 +26,24 @@ export default {
},
data() {
return {
data: [{ label: 'hello pull-refresh' }],
pullUpLoad: {
handler: () => this.handlerPullUpLoad()
},
pullDownRefresh: {
handler: () => this.handlerPullDownRefresh()
}
data: [...Array(30)].map((i, index) => {
return { label: `${index} list data` }
}),
value: true,
hasMore: true
}
},
methods: {
handlerPullUpLoad() {
return new Promise((resolve) => {
setTimeout(() => {
this.data.unshift({ label: 'hello pull-refresh up' })
resolve(this.data)
}, 1000)
})
setTimeout(() => {
this.value = false
this.hasMore = false
}, 3000)
},
handlerPullDownRefresh() {
return new Promise((resolve) => {
setTimeout(() => {
this.data.push({ label: 'hello pull-refresh down' })
resolve(this.data)
}, 1000)
})
setTimeout(() => {
this.value = false
}, 3000)
}
}
}
@ -61,18 +53,21 @@ export default {
.page__hd {
padding: 40px;
}
.page__title {
font-weight: 400;
font-size: 21px;
text-align: left;
}
.page__desc {
margin-top: 5px;
color: #888;
font-size: 14px;
text-align: left;
}
.page__content {
height: 250px;
height: 350px;
}
</style>

View File

@ -1,31 +0,0 @@
<template>
<tiny-pull-refresh :pullDown="pullDownRefresh" head-height="100">
<h3>hello pull-refresh</h3>
</tiny-pull-refresh>
</template>
<script lang="jsx">
import { PullRefresh } from '@opentiny/vue'
export default {
components: {
TinyPullRefresh: PullRefresh
},
data() {
return {
pullDownRefresh: {
handler: () => this.handlerPullDownRefresh()
}
}
},
methods: {
handlerPullDownRefresh() {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 1000)
})
}
}
}
</script>

View File

@ -1,10 +1,24 @@
<template>
<tiny-pull-refresh :pullDown="pullDownRefresh" loosing-text="可以了可以了别扒拉了">
<h3>hello pull-refresh</h3>
</tiny-pull-refresh>
<div>
<div class="page__hd">
<h1 class="page__title">Refresh</h1>
<p class="page__desc">刷新</p>
</div>
<div class="page__content">
<tiny-pull-refresh
loosing-text="松手刷新"
v-model="value"
:has-more="hasMore"
@pullDown="handlerPullDownRefresh"
@pullUp="handlerPullUpLoad"
>
<div :key="item.name" v-for="item in data">{{ item.label }}</div>
</tiny-pull-refresh>
</div>
</div>
</template>
<script lang="jsx">
<script>
import { PullRefresh } from '@opentiny/vue'
export default {
@ -13,19 +27,48 @@ export default {
},
data() {
return {
pullDownRefresh: {
handler: () => this.handlerPullDownRefresh()
}
data: [...Array(30)].map((i, index) => {
return { label: `${index} list data` }
}),
value: true,
hasMore: true
}
},
methods: {
handlerPullUpLoad() {
setTimeout(() => {
this.value = false
this.hasMore = false
}, 3000)
},
handlerPullDownRefresh() {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 1000)
})
setTimeout(() => {
this.value = false
}, 3000)
}
}
}
</script>
<style scoped>
.page__hd {
padding: 40px;
}
.page__title {
font-weight: 400;
font-size: 21px;
text-align: left;
}
.page__desc {
margin-top: 5px;
color: #888;
font-size: 14px;
text-align: left;
}
.page__content {
height: 350px;
}
</style>

View File

@ -1,13 +1,30 @@
<template>
<tiny-pull-refresh :pullDown="pullDownRefresh">
<template #loading>
<span>loading...</span>
</template>
<h3>hello pull-refresh</h3>
</tiny-pull-refresh>
<div>
<div class="page__hd">
<h1 class="page__title">Refresh</h1>
<p class="page__desc">刷新</p>
</div>
<div class="page__content">
<tiny-pull-refresh
v-model="value"
:has-more="hasMore"
@pullDown="handlerPullDownRefresh"
@pullUp="handlerPullUpLoad"
>
<template #header>
<span>refreshing...</span>
</template>
<template #footer>
<span>load more...</span>
</template>
<div :key="item.name" v-for="item in data">{{ item.label }}</div>
</tiny-pull-refresh>
</div>
</div>
</template>
<script lang="jsx">
<script>
import { PullRefresh } from '@opentiny/vue'
export default {
@ -16,19 +33,51 @@ export default {
},
data() {
return {
pullDownRefresh: {
handler: () => this.handlerPullDownRefresh()
}
data: [...Array(30)].map((i, index) => {
return { label: `${index + 1} list data` }
}),
value: true,
hasMore: true
}
},
methods: {
handlerPullUpLoad() {
setTimeout(() => {
const length = this.data.length
for (let i = 1; i <= 10; i++) {
this.data.push({ label: `${i + length} list data` })
}
this.value = false
}, 2000)
},
handlerPullDownRefresh() {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 1000)
})
setTimeout(() => {
this.value = false
}, 2000)
}
}
}
</script>
<style scoped>
.page__hd {
padding: 40px;
}
.page__title {
font-weight: 400;
font-size: 21px;
text-align: left;
}
.page__desc {
margin-top: 5px;
color: #888;
font-size: 14px;
text-align: left;
}
.page__content {
height: 350px;
}
</style>

View File

@ -0,0 +1,74 @@
<template>
<div>
<div class="page__hd">
<h1 class="page__title">Refresh</h1>
<p class="page__desc">刷新</p>
</div>
<div class="page__content">
<tiny-pull-refresh
v-model="value"
:has-more="hasMore"
pull-up-distance="30"
@pullDown="handlerPullDownRefresh"
@pullUp="handlerPullUpLoad"
>
<div :key="item.name" v-for="item in data">{{ item.label }}</div>
</tiny-pull-refresh>
</div>
</div>
</template>
<script>
import { PullRefresh } from '@opentiny/vue'
export default {
components: {
TinyPullRefresh: PullRefresh
},
data() {
return {
data: [...Array(30)].map((i, index) => {
return { label: `${index} list data` }
}),
value: true,
hasMore: true
}
},
methods: {
handlerPullUpLoad() {
setTimeout(() => {
this.value = false
this.hasMore = false
}, 3000)
},
handlerPullDownRefresh() {
setTimeout(() => {
this.value = false
}, 3000)
}
}
}
</script>
<style scoped>
.page__hd {
padding: 40px;
}
.page__title {
font-weight: 400;
font-size: 21px;
text-align: left;
}
.page__desc {
margin-top: 5px;
color: #888;
font-size: 14px;
text-align: left;
}
.page__content {
height: 350px;
}
</style>

View File

@ -1,10 +1,25 @@
<template>
<tiny-pull-refresh :pullDown="pullDownRefresh" pulling-text="你敢在拉一点么">
<h3>hello pull-refresh</h3>
</tiny-pull-refresh>
<div>
<div class="page__hd">
<h1 class="page__title">Refresh</h1>
<p class="page__desc">刷新</p>
</div>
<div class="page__content">
<tiny-pull-refresh
pull-down-loading-text="刷新中"
pull-up-loading-text="加载中"
v-model="value"
:has-more="hasMore"
@pullDown="handlerPullDownRefresh"
@pullUp="handlerPullUpLoad"
>
<div :key="item.name" v-for="item in data">{{ item.label }}</div>
</tiny-pull-refresh>
</div>
</div>
</template>
<script lang="jsx">
<script>
import { PullRefresh } from '@opentiny/vue'
export default {
@ -13,19 +28,48 @@ export default {
},
data() {
return {
pullDownRefresh: {
handler: () => this.handlerPullDownRefresh()
}
data: [...Array(30)].map((i, index) => {
return { label: `${index} list data` }
}),
value: true,
hasMore: true
}
},
methods: {
handlerPullUpLoad() {
setTimeout(() => {
this.value = false
this.hasMore = false
}, 3000)
},
handlerPullDownRefresh() {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 1000)
})
setTimeout(() => {
this.value = false
}, 3000)
}
}
}
</script>
<style scoped>
.page__hd {
padding: 40px;
}
.page__title {
font-weight: 400;
font-size: 21px;
text-align: left;
}
.page__desc {
margin-top: 5px;
color: #888;
font-size: 14px;
text-align: left;
}
.page__content {
height: 350px;
}
</style>

View File

@ -1,46 +0,0 @@
<template>
<tiny-pull-refresh
:pullUp="pullUpLoad"
:pullDown="pullDownRefresh"
success-text="运气真好!!!"
failed-text="哦哦,出错了!"
>
<h3>hello pull-refresh</h3>
</tiny-pull-refresh>
</template>
<script>
import { PullRefresh } from '@opentiny/vue'
export default {
components: {
TinyPullRefresh: PullRefresh
},
data() {
return {
pullUpLoad: {
handler: () => this.handlerPullUpLoad()
},
pullDownRefresh: {
handler: () => this.handlerPullDownRefresh()
}
}
},
methods: {
handlerPullUpLoad() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error())
}, 1000)
})
},
handlerPullDownRefresh() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
}
}
}
</script>

View File

@ -1,31 +0,0 @@
<template>
<tiny-pull-refresh ref="refresh" :pullDown="pullDownRefresh" success-duration="3000" success-text="加载完成">
<h3>hello pull-refresh</h3>
</tiny-pull-refresh>
</template>
<script lang="jsx">
import { PullRefresh } from '@opentiny/vue'
export default {
components: {
TinyPullRefresh: PullRefresh
},
data() {
return {
pullDownRefresh: {
handler: () => this.handlerPullDownRefresh()
}
}
},
methods: {
handlerPullDownRefresh() {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 1000)
})
}
}
}
</script>

View File

@ -1,27 +0,0 @@
<template>
<tiny-pull-refresh v-model="isLoading" @refresh="onRefresh" success-text="运气真好!!!">
<h3>hello pull-refresh</h3>
</tiny-pull-refresh>
</template>
<script lang="jsx">
import { PullRefresh } from '@opentiny/vue'
export default {
components: {
TinyPullRefresh: PullRefresh
},
data() {
return {
isLoading: false
}
},
methods: {
onRefresh() {
setTimeout(() => {
this.isLoading = false
}, 1000)
}
}
}
</script>

View File

@ -27,50 +27,50 @@ export default {
codeFiles: ['animation-duration.vue']
},
{
demoId: 'disabled',
demoId: 'disabled-pull-down',
name: {
'zh-CN': '禁用下拉刷新',
'en-US': 'button round'
},
desc: {
'zh-CN': '<p>禁用下拉刷新</p>',
'en-US': '<p>button round</p>'
'en-US': '<p>disabled pull down</p>'
},
codeFiles: ['disabled.vue']
codeFiles: ['disabled-pull-down.vue']
},
{
demoId: 'enhance',
demoId: 'disabled-pull-up',
name: {
'zh-CN': '支持上拉、下拉刷新',
'en-US': 'button round'
'zh-CN': '禁用上拉刷新',
'en-US': 'disabled pull up'
},
desc: {
'zh-CN': '<p>支持上拉、下拉刷新</p>',
'en-US': '<p>button round</p>'
'zh-CN': '<p>禁用上拉刷新</p>',
'en-US': '<p>disabled pull up</p>'
},
codeFiles: ['enhance.vue']
codeFiles: ['disabled-pull-up.vue']
},
{
demoId: 'head-height',
demoId: 'has-more',
name: {
'zh-CN': '提示信息的高度',
'en-US': 'events'
'zh-CN': '没有更多数据',
'en-US': 'no more'
},
desc: {
'zh-CN': '<p>下拉后,提示信息的高度<p>',
'en-US': '<p>bbutton click</p>'
'zh-CN': '<p>没有更多数据</p>',
'en-US': '<p>no more</p>'
},
codeFiles: ['head-height.vue']
codeFiles: ['has-more.vue']
},
{
demoId: 'loosing-text',
name: {
'zh-CN': '超过<code>head</code>的高度后的提示',
'en-US': 'events'
'zh-CN': '可以触发下拉刷新的提示文字',
'en-US': 'loosing-text'
},
desc: {
'zh-CN': '<p>下拉超过<code>head</code>的高度后的提示<p>',
'en-US': '<p>bbutton click</p>'
'zh-CN': '<p>可以触发下拉刷新的提示文字<p>',
'en-US': '<p>loosing text</p>'
},
codeFiles: ['loosing-text.vue']
},
@ -81,58 +81,46 @@ export default {
'en-US': 'events'
},
desc: {
'zh-CN': '<p>下拉刷新<code>loading</code>的插槽<p>',
'en-US': '<p>bbutton click</p>'
'zh-CN': '<p>插槽<p>',
'en-US': '<p>slot</p>'
},
codeFiles: ['pull-refresh-slot.vue']
},
{
demoId: 'pulling-text',
demoId: 'pull-up-distance',
name: {
'zh-CN': '下拉提示文字',
'en-US': 'events'
'zh-CN': '上拉触发加载的距离',
'en-US': 'pull up distance'
},
desc: {
'zh-CN': '<p>开始下拉并还没到<code>head</code>的高度时的提示文字<p>',
'en-US': '<p>bbutton click</p>'
'zh-CN': '<p>上拉触发加载的距离<p>',
'en-US': '<p>pull up distanc</p>'
},
codeFiles: ['pull-up-distance.vue']
},
{
demoId: 'pulling-text',
name: {
'zh-CN': '加载状态文字',
'en-US': 'pulling text'
},
desc: {
'zh-CN': '<p>加载状态文字<p>',
'en-US': '<p>pulling text</p>'
},
codeFiles: ['pulling-text.vue']
},
{
demoId: 'result-text',
demoId: '刷新事件',
name: {
'zh-CN': '刷新完成时的提示文字',
'en-US': 'events'
'zh-CN': '刷新事件',
'en-US': 'refresh event'
},
desc: {
'zh-CN': '<p>刷新完成时的提示文字<p>',
'en-US': '<p>bbutton click</p>'
'zh-CN': '<p>刷新事件<p>',
'en-US': '<p>refresh event</p>'
},
codeFiles: ['result-text.vue']
},
{
demoId: 'success-duration',
name: {
'zh-CN': '刷新成功的文字展示时长',
'en-US': 'events'
},
desc: {
'zh-CN': '<p>刷新完成时,提示的显示时长<p>',
'en-US': '<p>bbutton click</p>'
},
codeFiles: ['success-duration.vue']
},
{
demoId: 'success-text',
name: {
'zh-CN': '刷新成功的文字',
'en-US': 'events'
},
desc: {
'zh-CN': '<p>刷新成功时的提示文字<p>',
'en-US': '<p>bbutton click</p>'
},
codeFiles: ['success-text.vue']
codeFiles: ['event.vue']
}
],
apis: [
@ -141,28 +129,18 @@ export default {
type: 'component', // API 类型
properties: [
{
name: 'disabled',
type: 'Boolean',
name: 'v-model',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '<p>禁止下拉刷新默认false</p>',
'en-US': 'display different button'
'zh-CN': '<p>是否刷新状态可以手动设置false来关闭刷新</p>',
'en-US': 'is refreshing'
},
demoId: 'disabled'
},
{
name: 'head-height',
type: 'Number',
defaultValue: '50',
desc: {
'zh-CN': '<p>loading提示的高度,默认50</p>',
'en-US': 'display different button'
},
demoId: 'head-height-wrap'
demoId: 'base'
},
{
name: 'loosing-text',
type: 'String',
type: 'string',
defaultValue: '释放即可刷新',
desc: {
'zh-CN': '<p>下拉高度大于等于head-height时的提示文字默认为释放即可刷新</p>',
@ -171,106 +149,106 @@ export default {
demoId: 'loosing-text'
},
{
name: 'pulling-text',
type: 'String',
defaultValue: '下拉即可刷新',
name: 'has-more',
type: 'boolean',
defaultValue: 'true',
desc: {
'zh-CN': '<p>下拉高度小于head-height时的提示文字默认为下拉即可刷新</p>',
'en-US': 'display different button'
'zh-CN': '<p>是否有更多数据</p>',
'en-US': 'has more'
},
demoId: 'has-more'
},
{
name: 'disabled-pull-down',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '<p>是否禁用下拉刷新</p>',
'en-US': 'disablde pull down'
},
demoId: 'disabled-pull-down'
},
{
name: 'disabled-pull-up',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '<p>是否禁用上拉</p>',
'en-US': 'disablded pull up'
},
demoId: 'disabled-pull-up'
},
{
name: 'pull-up-distance',
type: 'number',
defaultValue: '18',
desc: {
'zh-CN': '<p>触发上拉刷新的距离</p>',
'en-US': 'pull up distance'
},
demoId: 'pull-up-distance'
},
{
name: 'pull-up-loading-text',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '<p>上拉加载文字</p>',
'en-US': 'pull up loading text'
},
demoId: 'pulling-text'
},
{
name: 'success-duration',
type: 'Number',
defaultValue: '500',
desc: {
'zh-CN': '<p>刷新完成的提示文字的显示时长需要与success-text配合使用默认500</p>',
'en-US': 'display different button'
},
demoId: 'success-duration'
},
{
name: 'success-text',
type: 'String',
name: 'pull-down-loading-text',
type: 'string',
defaultValue: '',
desc: {
'zh-CN': '<p>下拉刷新完成的提示文字</p>',
'en-US': 'display different button'
'zh-CN': '<p>下拉加载文字</p>',
'en-US': 'pull down loading text'
},
demoId: 'result-text'
},
{
name: 'failed-text',
type: 'String',
defaultValue: '',
desc: {
'zh-CN': '<p>下拉刷新失败的提示文字</p>',
'en-US': 'display different button'
},
demoId: 'success-text'
},
{
name: 'value',
type: 'Boolean',
defaultValue: 'true',
desc: {
'zh-CN': '<p>下拉过程中会被置为true当设置为false时关闭loading</p>',
'en-US': 'display different button'
},
demoId: 'success-text'
},
{
name: 'pullDown',
type: {},
defaultValue: '',
desc: {
'zh-CN': '<p>下拉刷新</p>',
'en-US': 'display different button'
},
demoId: 'base'
},
{
name: 'pullUp',
type: '{}',
defaultValue: '',
desc: {
'zh-CN': '<p>上拉加载</p>',
'en-US': 'display different button'
},
demoId: 'enhance'
demoId: 'pulling-text'
}
],
events: [
{
name: 'refresh',
type: 'Function()',
name: 'pull-down',
type: '(event: PullEvent) => void',
defaultValue: '',
desc: {
'zh-CN': '<p>下拉时触发的事件</p>',
'en-US': 'Click'
},
demoId: 'pulling-text'
demoId: 'event'
},
{
name: 'pull-up',
type: '(event: PullEvent) => void',
defaultValue: '',
desc: {
'zh-CN': '<p>上拉时触发的事件</p>',
'en-US': 'Click'
},
demoId: 'event'
}
],
slot: [
{
name: 'default',
name: 'header',
type: '',
defaultValue: '',
desc: {
'zh-CN': '<p>组件默认插槽</p>',
'en-US': 'Click'
'zh-CN': '<p>下拉刷新插槽</p>',
'en-US': 'pull down slot'
},
demoId: ''
},
{
name: 'loading',
name: 'footer',
type: '',
defaultValue: '',
desc: {
'zh-CN': '<p>下拉加载中的插槽</p>',
'en-US': 'Click'
'zh-CN': '<p>上拉加载更多插槽</p>',
'en-US': 'pull up slot'
},
demoId: 'pull-refresh-slot'
}

View File

@ -20,16 +20,11 @@ export const initPullRefresh =
pullingDownText: t('ui.pullRefresh.pullingDown'),
pullUpDisabled: false,
pullDownDisabled: false,
headHeight: 50,
footHeight: 50
headHeight: 48
}
state.pullUp = { ...defaultOption, ...props.pullUp }
state.pullDown = { ...defaultOption, ...props.pullDown }
state.refreshStyle = {
paddingTop: state.pullDown.headHeight + 'px',
paddingBottom: state.pullUp.footHeight + 'px'
}
state.loosingText = props.loosingText ?? t('ui.pullRefresh.loosing')
state.successText = props.successText ?? t('ui.pullRefresh.success')
state.failedText = props.failedText ?? t('ui.pullRefresh.failed')
@ -40,91 +35,78 @@ export const onTouchstart = (state) => (event) => {
}
export const onTouchmove =
({ props, state }) =>
({ state, refs }) =>
(event) => {
if (event.touches[0].clientY < state.draggposition) {
// 上拉刷新
if (!state.pullUp.pullUpDisabled) {
// 没有更多了
if (props.hasMore) {
state.translate3d = (event.touches[0].clientY - state.draggposition) / 2
state.pullUpReplaces =
Math.abs(state.translate3d) > state.pullUp.footHeight ? state.loosingText : state.pullUp.pullingUpText
state.pullDownReplaces = ''
}
}
pullUpTouchMove(state)
} else {
// 下拉刷新
if (!state.pullDown.pullDownDisabled) {
state.translate3d = (event.touches[0].clientY - state.draggposition) / 2
state.pullDownReplaces =
Math.abs(state.translate3d) > state.pullDown.headHeight ? state.loosingText : state.pullDown.pullingDownText
state.pullUpReplaces = ''
}
pullDownTouchMove(state, refs, event)
}
state.animationDuration = 0
state.pullUpLoading = false
state.pullDownLoading = false
}
export const pullUpTouchMove = (state) => {
if (state.disabledPullUp || state.pullUpLoading || !state.hasMore) {
return
}
}
export const pullDownTouchMove = (state, refs, event) => {
if (state.disabledPullDown || state.pullDownLoading) {
return
}
if (refs.content.scrollTop <= 0) {
state.translate3d = (event.touches[0].clientY - state.draggposition) / 2
state.pullDownReplaces =
Math.abs(state.translate3d) > state.pullDown.headHeight ? state.loosingText : state.pullDown.pullingDownText
}
state.animationDuration = 0
}
export const onTouchend =
({ api, props, state }) =>
() => {
({ api, props, state, emit, refs }) =>
(event) => {
state.animationDuration = props.animationDuration
// 上拉/下拉超过指定的高度触发刷新
if (
Math.abs(state.translate3d) >= state.pullDown.headHeight ||
Math.abs(state.translate3d) >= state.pullUp.footHeight
) {
if (state.translate3d >= 0) {
// 下拉刷新
state.translate3d = state.pullDown.headHeight
const { handler } = state.pullDown
const pullDownPromise = handler && handler()
state.pullDownLoading = true
if (pullDownPromise?.then) {
pullDownPromise.then(
() => {
api.handlerModelValue('down', 'success')
},
(e) => {
api.handlerModelValue('down', 'failed')
}
)
return
}
console.error(new Error('handler down is not promise'))
} else {
// 上拉刷新
state.translate3d = -state.pullUp.footHeight
const { handler } = state.pullUp
const pullUpPromise = handler && handler()
state.pullUpLoading = true
if (pullUpPromise?.then) {
pullUpPromise.then(
() => {
api.handlerModelValue('up', 'success')
},
(e) => {
api.handlerModelValue('up', 'failed')
}
)
return
}
console.error(new Error('handler up is not promise'))
}
api.clearPullRefresh()
if (event.changedTouches[0].clientY < state.draggposition) {
// 上拉
pullUpTouchEnd(state, emit, refs)
} else {
// 上拉/下拉未超过指定的高度,不触发刷新,回弹
state.pullUpLoading = false
state.pullDownLoading = false
api.clearPullRefresh()
// 下拉
pullDownTouchEnd(api, state, emit)
}
}
export const pullDownTouchEnd = (api, state, emit) => {
if (Math.abs(state.translate3d) < state.pullDown.headHeight) {
state.pullDownLoading = false
api.clearPullRefresh()
return
}
state.translate3d = state.pullDown.headHeight
state.pullDownLoading = true
emit('update:modelValue', true)
emit('pullDown')
}
export const pullUpTouchEnd = (state, emit, refs) => {
setTimeout(() => {
const footNode = refs.foot
if (!state.hasMore || !footNode) {
return
}
const contentNode = refs.content
const bottomDis = footNode.offsetTop + footNode.clientHeight - contentNode.scrollTop - contentNode.clientHeight
if (bottomDis <= state.pullUpDistance) {
state.pullUpLoading = true
emit('update:modelValue', true)
emit('pullUp')
}
}, 300)
}
export const mountedHandler =
({ api, refs }) =>
() => {
@ -154,7 +136,7 @@ export const handlerModelValue =
if (value === 'down') {
state.pullDownReplaces = state[`${result}Text`]
} else {
state.pullUpReplaces = state[`${result}Text`]
state.pullUpStateText = state[`${result}Text`]
}
setTimeout(() => {
api.clearPullRefresh()
@ -163,6 +145,7 @@ export const handlerModelValue =
export const clearPullRefresh = (state) => () => {
state.translate3d = 0
state.pullUpReplaces = ''
state.pullDownReplaces = ''
state.pullDownLoading = false
state.pullUpLoading = false
}

View File

@ -23,10 +23,9 @@ import {
export const api = ['state']
export const renderless = (props, { watch, onMounted, reactive, onBeforeUnmount }, { t, refs }) => {
export const renderless = (props, { watch, onMounted, reactive, onBeforeUnmount }, { t, refs, emit, nextTick }) => {
const api = {}
const state = reactive({
pullUpReplaces: '',
pullDownReplaces: '',
refreshStyle: {},
translate3d: 0,
@ -36,18 +35,24 @@ export const renderless = (props, { watch, onMounted, reactive, onBeforeUnmount
loosingText: '',
successText: '',
failedText: '',
noMoreText: '',
noMoreText: t('ui.pullRefresh.noMore'),
pullUpLoadingText: props.pullUpLoadingText,
pullDownLoadingText: props.pullDownLoadingText,
pullUp: null,
pullDown: null,
hasMore: true,
successDuration: props.successDuration,
animationDuration: props.animationDuration
animationDuration: props.animationDuration,
disabledPullDown: props.disabledPullDown,
disabledPullUp: props.disabledPullUp,
pullUpDistance: typeof props.pullUpDistance === 'string' ? Number(props.pullUpDistance) : props.pullUpDistance
})
Object.assign(api, {
state,
onTouchstart: onTouchstart(state),
onTouchmove: onTouchmove({ props, state }),
onTouchend: onTouchend({ api, props, state }),
onTouchmove: onTouchmove({ state, refs }),
onTouchend: onTouchend({ api, props, state, emit, refs }),
mountedHandler: mountedHandler({ api, refs }),
beforeUnmountHandler: beforeUnmountHandler({ api, refs }),
handlerModelValue: handlerModelValue({ api, state }),
@ -59,8 +64,15 @@ export const renderless = (props, { watch, onMounted, reactive, onBeforeUnmount
() => props.hasMore,
(value) => {
if (!value) {
// 没有更多了
state.noMoreText = t('ui.pullRefresh.noMore')
state.hasMore = false
}
}
)
watch(
() => props.modelValue,
(value) => {
if (!value) {
api.clearPullRefresh()
}
}

View File

@ -18,6 +18,8 @@
.@{pull-refresh-prefix-cls} {
height: 100%;
user-select: none;
overflow: hidden;
&__track {
position: relative;
height: 100%;
@ -25,18 +27,18 @@
}
&__tips {
position: absolute;
left: 0;
width: 100%;
overflow: hidden;
color: var(--ti-mobile-pull-refresh-head-text-color, #969799);
font-size: var(--ti-mobile-pull-refresh-head-font-size, 14px);
line-height: 50px;
color: var(--ti-mobile-pull-refresh-text-color);
font-size: var(--ti-mobile-pull-refresh-font-size);
text-align: center;
}
&__head {
transform: translateY(-100%);
height: 20px;
position: absolute;
top: -20px;
}
&__content {
@ -44,69 +46,56 @@
height: 100%;
}
&-loading-content {
display: flex;
justify-content: center;
align-items: center;
}
&__loading {
position: relative;
border-radius: 50%;
width: var(--ti-mobile-pull-refresh-head-loading-icon-size-outside);
height: var(--ti-mobile-pull-refresh-head-loading-icon-size-outside);
display: inline-block;
width: 60px;
height: 12px;
background-image: var(--ti-mobile-pull-refresh-head-loading-bg-image);
position: relative;
i {
position: absolute;
top: 0;
display: inline-block;
width: 12px;
height: 12px;
&-animation {
animation: turn-around 1.5s linear infinite;
}
&-inner {
width: var(--ti-mobile-pull-refresh-head-loading-icon-size-inside);
height: var(--ti-mobile-pull-refresh-head-loading-icon-size-inside);
border-radius: 50%;
z-index: 1;
background: var(--ti-mobile-pull-refresh-head-loading-icon-bg-color);
position: absolute;
top: 2px;
left: 2px;
}
}
&__text {
font-size: var(--ti-mobile-pull-refresh-font-size);
color: var(--ti-mobile-pull-refresh-text-color);
padding-left: 8px;
}
&__foot {
display: flex;
align-items: center;
justify-content: center;
font-size: var(--ti-mobile-pull-refresh-font-size);
color: var(--ti-mobile-pull-refresh-text-color);
}
@keyframes turn-around {
0% {
transform: rotate(0deg);
}
i:nth-child(3) {
left: 48px;
background: #1ea5e8;
animation: three 300ms infinite;
}
i:nth-child(2) {
left: 24px;
background: #f6af39;
animation: two 300ms infinite;
}
i:nth-child(1) {
left: 0;
display: block;
background: #f25e51;
animation: one 300ms infinite;
100% {
transform: rotate(360deg);
}
}
}
@keyframes three {
from {
left: 48px;
}
to {
left: 0px;
}
}
@keyframes two {
from {
left: 24px;
}
to {
left: 48px;
}
}
@keyframes one {
from {
left: 0px;
}
to {
left: 24px;
}
}

View File

@ -1,4 +1,14 @@
:root {
--ti-mobile-pull-refresh-head-text-color: #969799;
--ti-mobile-pull-refresh-head-font-size: 14px;
// 文字颜色
--ti-mobile-pull-refresh-text-color: var(--ti-mobile-color-text-placeholder, #808080);
// 文字尺寸
--ti-mobile-pull-refresh-font-size: var(--ti-mobile-font-size-s, 12px);
// 加载背景色
--ti-mobile-pull-refresh-head-loading-bg-image: conic-gradient(rgb(128 128 128 / 0%), rgb(128 128 128 / 100%));
// 加载圆圈外径
--ti-mobile-pull-refresh-head-loading-icon-size-outside: 18px;
// 加载圆圈内径
--ti-mobile-pull-refresh-head-loading-icon-size-inside: 14px;
// 加载圆圈背景色
--ti-mobile-pull-refresh-head-loading-icon-bg-color: #fff;
}

View File

@ -19,36 +19,41 @@
transform: 'translate3d(0px,' + state.translate3d + 'px,0px)'
}"
>
<div
class="tiny-mobile-pull-refresh__tips tiny-mobile-pull-refresh__head"
:style="{ height: state.pullDown.headHeight + 'px' }"
v-if="state.pullDownLoading || state.pullDownReplaces"
>
<div class="tiny-mobile-pull-refresh__tips tiny-mobile-pull-refresh__head">
<span v-if="!state.pullDownLoading">{{ state.pullDownReplaces }}</span>
<slot name="loading" v-if="state.pullDownLoading">
<ul v-if="state.pullDownLoading" class="tiny-mobile-pull-refresh__loading">
<i></i>
<i></i>
<i></i>
</ul>
<slot name="header" v-if="state.pullDownLoading">
<div v-if="state.pullDownLoading" class="tiny-mobile-pull-refresh-loading-content">
<div
:class="[
'tiny-mobile-pull-refresh__loading',
state.pullDownLoading ? 'tiny-mobile-pull-refresh__loading-animation' : null
]"
>
<div class="tiny-mobile-pull-refresh__loading-inner"></div>
</div>
<div class="tiny-mobile-pull-refresh__text" v-if="state.pullDownLoadingText">
{{ state.pullDownLoadingText }}
</div>
</div>
</slot>
</div>
<div class="tiny-mobile-pull-refresh__content">
<div class="tiny-mobile-pull-refresh__content" ref="content">
<slot></slot>
</div>
<div
class="tiny-mobile-pull-refresh__tips tiny-mobile-pull-refresh__foot"
:style="{ height: state.pullUp.footHeight + 'px' }"
v-if="state.pullUpLoading || state.pullUpReplaces"
>
<span v-if="!state.pullUpLoading">{{ state.pullUpReplaces }}</span>
<slot name="loading" v-if="state.pullUpLoading">
<ul v-if="state.pullUpLoading" class="tiny-mobile-pull-refresh__loading">
<i></i>
<i></i>
<i></i>
</ul>
</slot>
<div class="tiny-mobile-pull-refresh__foot" v-if="!state.disabledPullUp" ref="foot">
<slot name="footer">
<div v-if="state.hasMore" class="tiny-mobile-pull-refresh-loading-content">
<div class="tiny-mobile-pull-refresh__loading tiny-mobile-pull-refresh__loading-animation">
<div class="tiny-mobile-pull-refresh__loading-inner"></div>
</div>
<div class="tiny-mobile-pull-refresh__text" v-if="state.pullUpLoadingText">
{{ state.pullUpLoadingText }}
</div>
</div>
<div v-else>{{ state.noMoreText }}</div>
</slot>
</div>
</div>
</div>
</div>
@ -65,31 +70,33 @@ export default defineComponent({
...props,
modelValue: Boolean,
loosingText: String,
successText: String,
failedText: String,
successDuration: {
type: [Number, String],
default: 500
},
animationDuration: {
type: [Number, String],
default: 300
},
disabled: {
type: Boolean,
default: false
},
pullUp: {
type: Object,
default: {}
},
pullDown: {
type: Object,
default: {}
},
hasMore: {
type: Boolean,
default: true
},
disabledPullDown: {
type: Boolean,
default: false
},
disabledPullUp: {
type: Boolean,
default: false
},
pullUpDistance: {
type: [Number, String],
default: 18
},
pullUpLoadingText: {
type: String,
default: null
},
pullDownLoadingText: {
type: String,
default: null
}
},
setup(props, context) {