Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
京东前端
nutui
提交
18cfcb0c
N
nutui
项目概览
京东前端
/
nutui
通知
37
Star
4
Fork
1
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
N
nutui
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
18cfcb0c
编写于
1月 28, 2021
作者:
Y
yangxiaolu3
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: infiniteLoading 添加下拉刷新功能
上级
16e486f8
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
300 addition
and
56 deletion
+300
-56
src/packages/infiniteloading/demo.vue
src/packages/infiniteloading/demo.vue
+62
-21
src/packages/infiniteloading/doc.md
src/packages/infiniteloading/doc.md
+66
-16
src/packages/infiniteloading/index.scss
src/packages/infiniteloading/index.scss
+35
-0
src/packages/infiniteloading/index.vue
src/packages/infiniteloading/index.vue
+137
-19
未找到文件。
src/packages/infiniteloading/demo.vue
浏览文件 @
18cfcb0c
...
...
@@ -6,9 +6,7 @@
<nut-infiniteloading
containerId=
"scroll"
:useWindow=
"false"
:isLoading=
"isLoading"
:hasMore=
"hasMore"
@
scrollChange=
"scrollChange"
@
loadMore=
"loadMore"
>
<li
...
...
@@ -21,13 +19,33 @@
</ul>
</nut-cell>
<h2>
下拉刷新
</h2>
<nut-cell>
<ul
class=
"infiniteUl"
id=
"refreshScroll"
>
<nut-infiniteloading
containerId=
"refreshScroll"
:useWindow=
"false"
:isOpenRefresh=
"true"
:hasMore=
"refreshHasMore"
@
loadMore=
"refreshLoadMore"
@
refresh=
"refresh"
>
<li
class=
"infiniteLi"
v-for=
"(item, index) in refreshList"
:key=
"index"
>
{{
item
}}
</li
>
</nut-infiniteloading>
</ul>
</nut-cell>
<h2>
自定义加载文案
</h2>
<nut-cell>
<ul
class=
"infiniteUl"
id=
"customScroll"
>
<nut-infiniteloading
containerId=
"customScroll"
:useWindow=
"false"
:isLoading=
"customIsLoading"
:hasMore=
"customHasMore"
@
loadMore=
"customLoadMore"
>
...
...
@@ -56,26 +74,21 @@
import
{
onMounted
,
ref
,
reactive
,
toRefs
}
from
'
vue
'
;
import
{
createComponent
}
from
'
@/utils/create
'
;
const
{
createDemo
}
=
createComponent
(
'
infiniteloading
'
);
import
{
Toast
}
from
'
../toast/toast
'
;
export
default
createDemo
({
props
:
{},
setup
()
{
const
isLoading
=
ref
(
false
);
const
hasMore
=
ref
(
true
);
const
customIsLoading
=
ref
(
false
);
const
customHasMore
=
ref
(
true
);
const
refreshHasMore
=
ref
(
true
);
const
data
=
reactive
({
defultList
:
[
''
],
customList
:
[
''
]
customList
:
[
''
],
refreshList
:
[
''
]
});
const
scrollChange
=
dis
=>
{
console
.
log
(
'
滚动的距离
'
,
dis
);
};
const
loadMore
=
()
=>
{
isLoading
.
value
=
true
;
const
loadMore
=
done
=>
{
setTimeout
(()
=>
{
const
curLen
=
data
.
defultList
.
length
;
...
...
@@ -85,14 +98,13 @@ export default createDemo({
);
}
isLoading
.
value
=
false
;
if
(
data
.
defultList
.
length
>
30
)
hasMore
.
value
=
false
;
done
();
},
500
);
};
const
customLoadMore
=
()
=>
{
customIsLoading
.
value
=
true
;
const
customLoadMore
=
done
=>
{
setTimeout
(()
=>
{
const
curLen
=
data
.
customList
.
length
;
for
(
let
i
=
curLen
;
i
<
curLen
+
10
;
i
++
)
{
...
...
@@ -100,11 +112,31 @@ export default createDemo({
`
${
i
}
-- 塑像本来就在石头里,我只是把不要的部分去掉`
);
}
customIsLoading
.
value
=
false
;
if
(
data
.
customList
.
length
>
30
)
customHasMore
.
value
=
false
;
done
();
},
500
);
};
const
refreshLoadMore
=
done
=>
{
setTimeout
(()
=>
{
const
curLen
=
data
.
refreshList
.
length
;
for
(
let
i
=
curLen
;
i
<
curLen
+
10
;
i
++
)
{
data
.
refreshList
.
push
(
`
${
i
}
-- 塑像本来就在石头里,我只是把不要的部分去掉`
);
}
if
(
data
.
refreshList
.
length
>
30
)
refreshHasMore
.
value
=
false
;
done
();
},
500
);
};
const
refresh
=
done
=>
{
setTimeout
(()
=>
{
Toast
.
success
(
'
刷新成功
'
);
done
();
},
1000
);
};
const
init
=
()
=>
{
for
(
let
i
=
0
;
i
<
10
;
i
++
)
{
data
.
defultList
.
push
(
...
...
@@ -113,6 +145,9 @@ export default createDemo({
data
.
customList
.
push
(
`
${
i
}
-- 塑像本来就在石头里,我只是把不要的部分去掉`
);
data
.
refreshList
.
push
(
`
${
i
}
-- 塑像本来就在石头里,我只是把不要的部分去掉`
);
}
};
onMounted
(()
=>
{
...
...
@@ -120,13 +155,13 @@ export default createDemo({
});
return
{
scrollChange
,
loadMore
,
isLoading
,
hasMore
,
customIsLoading
,
customHasMore
,
customLoadMore
,
refreshHasMore
,
refreshLoadMore
,
refresh
,
...
toRefs
(
data
)
};
}
...
...
@@ -145,4 +180,10 @@ export default createDemo({
font-size
:
14px
;
color
:
rgba
(
100
,
100
,
100
,
1
);
}
.loading
{
display
:
block
;
width
:
100%
;
text-align
:
center
;
}
</
style
>
src/packages/infiniteloading/doc.md
浏览文件 @
18cfcb0c
...
...
@@ -23,9 +23,7 @@
<nut-infiniteloading
containerId =
'scroll'
:useWindow=
'false'
:isLoading=
"isLoading"
:hasMore=
"hasMore"
@
scrollChange=
"scrollChange"
@
loadMore=
"loadMore"
>
<li
class=
"infiniteLi"
v-for=
"(item, index) in defultList"
:key=
"index"
>
{{item}}
</li>
...
...
@@ -34,21 +32,18 @@
```
```
javascript
setup
()
{
const
isLoading
=
ref
(
false
);
const
hasMore
=
ref
(
true
);
const
data
=
reactive
({
defultList
:
[]
});
const
scrollChange
=
(
dis
)
=>
{};
const
loadMore
=
()
=>
{
isLoading
.
value
=
true
;
const
loadMore
=
done
=>
{
setTimeout
(()
=>
{
const
curLen
=
data
.
defultList
.
length
;
for
(
let
i
=
curLen
;
i
<
curLen
+
10
;
i
++
)
{
data
.
defultList
.
push
(
`
${
i
}
-- 塑像本来就在石头里,我只是把不要的部分去掉`
);
}
isLoading
.
value
=
false
;
if
(
data
.
defultList
.
length
>
30
)
hasMore
.
value
=
false
;
done
()
},
500
);
};
const
init
=
()
=>
{
...
...
@@ -59,10 +54,66 @@ setup() {
onMounted
(()
=>
{
init
()
});
return
{
scrollChange
,
loadMore
,
isLoading
,
hasMore
,
...
toRefs
(
data
)
};
return
{
loadMore
,
hasMore
,
...
toRefs
(
data
)
};
}
```
### 下拉刷新
```
html
<ul
class=
"infiniteUl"
id=
"refreshScroll"
>
<nut-infiniteloading
containerId=
"refreshScroll"
:useWindow=
"false"
:isOpenRefresh=
"true"
:hasMore=
"refreshHasMore"
@
loadMore=
"refreshLoadMore"
@
refresh=
"refresh"
>
<li
class=
"infiniteLi"
v-for=
"(item, index) in refreshList"
:key=
"index"
>
{{ item }}
</li
>
</nut-infiniteloading>
</ul>
```
```
javascript
setup
()
{
const
refreshHasMore
=
ref
(
true
);
const
data
=
reactive
({
refreshList
:
[]
});
const
refreshLoadMore
=
done
=>
{
setTimeout
(()
=>
{
const
curLen
=
data
.
refreshList
.
length
;
for
(
let
i
=
curLen
;
i
<
curLen
+
10
;
i
++
)
{
data
.
refreshList
.
push
(
`
${
i
}
-- 塑像本来就在石头里,我只是把不要的部分去掉`
);
}
if
(
data
.
refreshList
.
length
>
30
)
refreshHasMore
.
value
=
false
;
done
()
},
500
);
};
const
refresh
=
(
done
)
=>
{
setTimeout
(()
=>
{
Toast
.
success
(
'
刷新成功
'
);
done
()
},
1000
)
}
const
init
=
()
=>
{
for
(
let
i
=
0
;
i
<
10
;
i
++
)
{
data
.
refreshList
.
push
(
`
${
i
}
-- 塑像本来就在石头里,我只是把不要的部分去掉`
);
}
}
onMounted
(()
=>
{
init
()
});
return
{
refreshLoadMore
,
refreshHasMore
,
refresh
,
...
toRefs
(
data
)
};
}
```
### 自定义加载文案
```
html
...
...
@@ -70,7 +121,6 @@ setup() {
<nut-infiniteloading
containerId =
'customScroll'
:useWindow=
'false'
:isLoading=
"customIsLoading"
:hasMore=
"customHasMore"
@
loadMore=
"customLoadMore"
>
...
...
@@ -88,20 +138,18 @@ setup() {
```
```
javascript
setup
()
{
const
customIsLoading
=
ref
(
false
);
const
customHasMore
=
ref
(
true
);
const
data
=
reactive
({
customList
:
[
''
]
});
const
customLoadMore
=
()
=>
{
customIsLoading
.
value
=
true
;
const
customLoadMore
=
done
=>
{
setTimeout
(()
=>
{
const
curLen
=
data
.
customList
.
length
;
for
(
let
i
=
curLen
;
i
<
curLen
+
10
;
i
++
)
{
data
.
customList
.
push
(
`
${
i
}
-- 塑像本来就在石头里,我只是把不要的部分去掉`
);
}
customIsLoading
.
value
=
false
;
if
(
data
.
customList
.
length
>
30
)
customHasMore
.
value
=
false
;
done
()
},
500
);
};
const
init
=
()
=>
{
...
...
@@ -112,7 +160,7 @@ setup() {
onMounted
(()
=>
{
init
()
});
return
{
custom
IsLoading
,
custom
HasMore
,
customLoadMore
,...
toRefs
(
data
)
};
return
{
customHasMore
,
customLoadMore
,...
toRefs
(
data
)
};
}
```
...
...
@@ -123,12 +171,12 @@ setup() {
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| hasMore | 是否还有更多数据 | Boolean | true |
| isLoading | 是否加载中 | Boolean | false |
| threshold | 距离底部多远加载 | Number | 200 |
| useWindow | 将滚动侦听器添加到 window 否则侦听组件的父节点 | Boolean | true |
| useCapture | 是否使用捕获模式 true 捕获 false 冒泡 | Boolean | false |
| containerId | 在 useWindow 属性为 false 的时候,自定义设置节点ID | String | '' |
| unloadMoreTxt | “没有更多数”据展示文案 | String | '哎呀,这里是底部了啦' |
| isOpenRefresh | 是否开启下拉刷新 | Boolean | false |
### Slot
...
...
@@ -136,11 +184,13 @@ setup() {
|--------|----------------|
| loading | 自定义“加载中”的展示形式 |
| unloadMore | 自定义“没有更多数据”的展示形式 |
| refreshLoading | 自定义下拉刷新中“加载中”的展示形式 |
### Events
| 事件名 | 说明 | 回调参数 |
|--------|----------------|--------------|
| loadMore | 继续加载的回调函数 |
-
|
| loadMore | 继续加载的回调函数 |
done 函数,用于关闭加载中状态
|
| scrollChange | 实时监听滚动高度 | 滚动高度 |
| refresh | 下拉刷新事件回调 | done 函数,用于关闭加载中状态 |
\ No newline at end of file
src/packages/infiniteloading/index.scss
浏览文件 @
18cfcb0c
.nut-infiniteloading
{
display
:
block
;
width
:
100%
;
.nut-infinite-top
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
width
:
100%
;
overflow
:
hidden
;
.top-box
{
width
:
100%
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
justify-content
:
center
;
.top-img
{
width
:
28px
;
height
:
24px
;
}
.top-text
{
font-size
:
10px
;
color
:
rgba
(
128
,
128
,
128
,
1
);
}
}
}
.nut-infinite-bottom
{
display
:
block
;
width
:
100%
;
...
...
@@ -8,5 +31,17 @@
font-size
:
12px
;
color
:
rgba
(
200
,
200
,
200
,
1
);
text-align
:
center
;
.bottom-box
{
.bottom-img
{
margin-right
:
5px
;
width
:
28px
;
height
:
24px
;
}
.bottom-text
{
font-size
:
10px
;
color
:
rgba
(
128
,
128
,
128
,
1
);
}
}
}
}
src/packages/infiniteloading/index.vue
浏览文件 @
18cfcb0c
<
template
>
<view
class=
"nut-infiniteloading"
ref=
"scroller"
>
<view
class=
"nut-infinite-top"
></view>
<view
class=
"nut-infiniteloading"
ref=
"scroller"
@
touchstart=
"touchStart"
@
touchmove=
"touchMove"
@
touchend=
"touchEnd"
>
<view
class=
"nut-infinite-top"
ref=
"refreshTop"
:style=
"getStyle"
>
<view
class=
"top-box"
v-if=
"!slotRefreshLoading"
>
<nut-icon
class=
"top-img"
name=
"https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png"
></nut-icon>
<view
class=
"top-text"
>
松开刷新
</view>
</view>
<slot
name=
"refreshLoading"
v-else
></slot>
</view>
<view
class=
"nut-infinite-container"
><slot></slot></view>
<view
class=
"nut-infinite-bottom"
>
<template
v-if=
"isLoading"
>
<nut-icon
name=
"refresh"
v-if=
"!slotLoading"
></nut-icon>
<template
v-if=
"isInfiniting"
>
<view
v-if=
"!slotLoading"
class=
"bottom-box"
>
<nut-icon
class=
"bottom-img"
name=
"https://img10.360buyimg.com/imagetools/jfs/t1/169863/6/4565/6306/60125948E7e92774e/40b3a0cf42852bcb.png"
></nut-icon>
<view
class=
"bottom-text"
>
加载中···
</view>
</view>
<slot
name=
"loading"
v-else
></slot>
</
template
>
<
template
v-else-if=
"!hasMore"
>
...
...
@@ -17,7 +40,15 @@
</view>
</template>
<
script
lang=
"ts"
>
import
{
ref
,
toRefs
,
onMounted
,
onUnmounted
}
from
'
vue
'
;
import
{
ref
,
toRefs
,
onMounted
,
onUnmounted
,
reactive
,
computed
,
CSSProperties
}
from
'
vue
'
;
import
{
createComponent
}
from
'
@/utils/create
'
;
const
{
componentName
,
create
}
=
createComponent
(
'
infiniteloading
'
);
...
...
@@ -27,10 +58,6 @@ export default create({
type
:
Boolean
,
default
:
true
},
isLoading
:
{
type
:
Boolean
,
default
:
false
},
threshold
:
{
type
:
Number
,
default
:
200
...
...
@@ -50,28 +77,63 @@ export default create({
useCapture
:
{
type
:
Boolean
,
default
:
false
},
isOpenRefresh
:
{
type
:
Boolean
,
default
:
false
}
},
components
:
{},
emits
:
[
'
scrollChange
'
,
'
loadMore
'
],
emits
:
[
'
scrollChange
'
,
'
loadMore
'
,
'
refresh
'
],
setup
(
props
,
{
emit
,
slots
})
{
console
.
log
(
'
componentName
'
,
componentName
);
const
{
hasMore
,
isLoading
,
threshold
,
containerId
,
useWindow
,
useCapture
useCapture
,
isOpenRefresh
}
=
toRefs
(
props
);
let
scrollEl
:
Window
|
HTMLElement
=
window
;
const
scroller
=
ref
<
null
|
HTMLElement
>
(
null
);
const
refreshTop
=
ref
<
null
|
HTMLElement
>
(
null
);
const
beforeScrollTop
=
ref
(
0
);
const
slotLoading
=
ref
(
false
);
const
slotUnloadMore
=
ref
(
false
);
const
isTouching
=
ref
(
false
);
const
isInfiniting
=
ref
(
false
);
const
refreshMaxH
=
ref
(
0
);
const
slot
=
reactive
({
slotLoading
:
false
,
slotUnloadMore
:
false
,
slotRefreshLoading
:
false
});
const
pageStart
=
reactive
({
y
:
0
,
x
:
0
,
distance
:
0
});
const
getStyle
=
computed
(()
=>
{
const
style
:
CSSProperties
=
{};
if
(
pageStart
.
distance
<
0
)
{
style
.
height
=
0
+
'
px
'
;
}
else
{
style
.
height
=
pageStart
.
distance
+
'
px
'
;
}
if
(
isTouching
.
value
)
{
style
.
transition
=
`height 0s cubic-bezier(0.25,0.1,0.25,1)`
;
}
else
{
style
.
transition
=
`height 0.2s cubic-bezier(0.25,0.1,0.25,1)`
;
}
return
style
;
});
/** 获取监听自定义滚动节点 */
const
getParentElement
=
el
=>
{
...
...
@@ -147,13 +209,18 @@ export default create({
return
offsetDistance
<=
threshold
.
value
&&
direction
==
'
down
'
;
};
const
infiniteDone
=
()
=>
{
isInfiniting
.
value
=
false
;
};
/** 滚动函数 */
const
handleScroll
=
()
=>
{
requestAniFrame
()(()
=>
{
if
(
!
isScrollAtBottom
()
||
!
hasMore
.
value
||
is
Load
ing
.
value
)
{
if
(
!
isScrollAtBottom
()
||
!
hasMore
.
value
||
is
Infinit
ing
.
value
)
{
return
false
;
}
else
{
emit
(
'
loadMore
'
);
console
.
log
(
'
无限加载更多
'
);
isInfiniting
.
value
=
true
;
emit
(
'
loadMore
'
,
infiniteDone
);
}
});
};
...
...
@@ -163,6 +230,47 @@ export default create({
scrollEl
.
addEventListener
(
'
scroll
'
,
handleScroll
,
useCapture
.
value
);
};
/** 下拉加载完成回到初始状态 */
const
refreshDone
=
()
=>
{
pageStart
.
distance
=
0
;
isTouching
.
value
=
false
;
};
const
touchStart
=
event
=>
{
if
(
beforeScrollTop
.
value
==
0
&&
!
isTouching
.
value
&&
isOpenRefresh
.
value
)
{
pageStart
.
y
=
event
.
touches
[
0
].
pageY
;
isTouching
.
value
=
true
;
const
childHeight
=
((
refreshTop
.
value
as
HTMLElement
)
.
firstElementChild
as
HTMLElement
).
offsetHeight
;
refreshMaxH
.
value
=
Math
.
floor
(
childHeight
*
1
+
10
);
}
};
const
touchMove
=
event
=>
{
pageStart
.
distance
=
event
.
touches
[
0
].
pageY
-
pageStart
.
y
;
if
(
pageStart
.
distance
>
0
&&
isTouching
.
value
)
{
event
.
preventDefault
();
if
(
pageStart
.
distance
>=
refreshMaxH
.
value
)
pageStart
.
distance
=
refreshMaxH
.
value
;
}
else
{
pageStart
.
distance
=
0
;
isTouching
.
value
=
false
;
}
};
const
touchEnd
=
()
=>
{
if
(
pageStart
.
distance
<
refreshMaxH
.
value
)
{
pageStart
.
distance
=
0
;
}
else
{
emit
(
'
refresh
'
,
refreshDone
);
}
};
/** 生命周期 首次加载 */
onMounted
(()
=>
{
const
parentElement
=
getParentElement
(
scroller
);
...
...
@@ -176,8 +284,9 @@ export default create({
scrollListener
();
slotUnloadMore
.
value
=
slots
.
unloadMore
?
true
:
false
;
slotLoading
.
value
=
slots
.
loading
?
true
:
false
;
slot
.
slotUnloadMore
=
slots
.
unloadMore
?
true
:
false
;
slot
.
slotLoading
=
slots
.
loading
?
true
:
false
;
slot
.
slotRefreshLoading
=
slots
.
refreshLoading
?
true
:
false
;
});
/** 移除监听 */
...
...
@@ -185,7 +294,16 @@ export default create({
scrollEl
.
removeEventListener
(
'
scroll
'
,
handleScroll
,
useCapture
.
value
);
});
return
{
scroller
,
slotLoading
,
slotUnloadMore
};
return
{
scroller
,
refreshTop
,
touchStart
,
touchMove
,
touchEnd
,
getStyle
,
isInfiniting
,
...
toRefs
(
slot
)
};
}
});
</
script
>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录