wk-scroll-view.vue 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <template>
  2. <scroll-view
  3. :lower-threshold="lowerThreshold"
  4. :refresher-triggered="refresherTriggered"
  5. :refresher-threshold="1"
  6. :refresher-enabled="refresh"
  7. :scroll-top="scrollTop"
  8. refresher-background="rgba(0,0,0,0)"
  9. refresher-default-style="none"
  10. scroll-y
  11. scroll-with-animation
  12. class="wk-scroll-view"
  13. @refresherpulling="refresherpulling"
  14. @refresherrefresh="refresherrefresh"
  15. @refresherrestore="refresherRestoreOrAbort"
  16. @refresherabort="refresherRestoreOrAbort"
  17. @scroll="handleScroll"
  18. @scrolltoupper="handleScrollUpper"
  19. @scrolltolower="handleLoadMore">
  20. <!-- 刷新提示 -->
  21. <uni-load-more
  22. v-if="showRefresh && refresh"
  23. :status="status"
  24. :content-text="contentText"
  25. icon-type="circle"
  26. class="wk-scroll-view-top" />
  27. <!-- 内容 -->
  28. <slot />
  29. <!-- 暂无数据 -->
  30. <!-- #ifdef MP-WEIXIN -->
  31. <view
  32. v-if="showNoData && showEmpty"
  33. :style="{backgroundImage: bgUrl('images/no_data.png')}"
  34. class="no-data">
  35. 暂无数据
  36. </view>
  37. <!-- #endif -->
  38. <!-- #ifndef MP-WEIXIN -->
  39. <view
  40. v-if="!$slots.default && showEmpty"
  41. :style="{backgroundImage: bgUrl('images/no_data.png')}"
  42. class="no-data">
  43. 暂无数据
  44. </view>
  45. <!-- #endif -->
  46. <!-- 加载更多提示 -->
  47. <uni-load-more
  48. v-if="showLoadMore"
  49. :status="status"
  50. :content-text="contentText"
  51. icon-type="circle"
  52. class="wk-scroll-view-bottom" />
  53. </scroll-view>
  54. </template>
  55. <script>
  56. /**
  57. * 上拉加载下拉刷新控件
  58. * @desc 容器必须有一个固定的高度
  59. * props:
  60. * {string} | status | loading 状态; 可选项:more(loading前)、loading(loading中)、noMore(没有更多了)
  61. * {number} | lowerThreshold | 距底部/右边多远时(单位px),触发加载更多事件(默认20)
  62. * {number} | scrollTop | 设置竖向滚动条位置
  63. * {boolean} | refresh | 是否开启下拉刷新,默认开启
  64. * {boolean} | loadMore | 是否开启上拉加载,默认开启
  65. * {boolean} | showEmpty | 列表无数据时是否显示无数据提示,默认true显示
  66. * event
  67. * refresh | 下拉刷新时触发 | 回调:null
  68. * loadmore | 上拉加载时触发 | 回调:null
  69. * scroll | 滚动时触发 | 回调:evt
  70. *
  71. * @author yxk
  72. */
  73. export default {
  74. name: 'WkScrollView',
  75. props: {
  76. status: { // loading 状态
  77. type: String,
  78. default: 'loading',
  79. validator: value => {
  80. return ['more', 'loading', 'noMore', ''].includes(value)
  81. }
  82. },
  83. lowerThreshold: {
  84. type: Number,
  85. default: 20
  86. },
  87. refresh: {
  88. type: Boolean,
  89. default: true
  90. },
  91. loadMore: {
  92. type: Boolean,
  93. default: true
  94. },
  95. showEmpty: {
  96. type: Boolean,
  97. default: true
  98. },
  99. scrollTop: {
  100. type: Number,
  101. default: 0
  102. }
  103. },
  104. data() {
  105. return {
  106. showRefresh: false,
  107. showLoadMore: false,
  108. refresherTriggered: false, // 设置当前下拉刷新状态
  109. restoreFlag: false,
  110. showNoData: false
  111. }
  112. },
  113. computed: {
  114. contentText() {
  115. if (this.showRefresh) {
  116. return {
  117. contentdown: '下拉刷新',
  118. contentrefresh: '正在刷新...',
  119. contentnomore: ''
  120. }
  121. }
  122. return {
  123. contentdown: '加载更多',
  124. contentrefresh: '正在加载中...',
  125. contentnomore: '已经到底啦~'
  126. }
  127. }
  128. },
  129. watch: {
  130. status: {
  131. handler(val) {
  132. if (val === 'loading') {
  133. this.refresherTriggered = true
  134. } else {
  135. this.refresherTriggered = 'restore'
  136. this.$nextTick(() => {
  137. this.refresherTriggered = false
  138. })
  139. this.showRefresh = false
  140. }
  141. this.calcNoData()
  142. },
  143. immediate: true
  144. }
  145. },
  146. activated() {
  147. console.log('activated')
  148. this.calcNoData()
  149. },
  150. mounted() {
  151. console.log('mounted')
  152. this.calcNoData()
  153. },
  154. methods: {
  155. bgUrl(val) {
  156. return `url(${this.$static(val)})`
  157. },
  158. calcNoData() {
  159. // #ifdef MP-WEIXIN
  160. this.$nextTick(() => {
  161. const old = !this.showNoData
  162. this.showNoData = old
  163. this.$nextTick(() => {
  164. this.showNoData = Boolean(!this.$children || this.$children.length === 0)
  165. // console.log('showNoData: ', this.showNoData)
  166. })
  167. })
  168. // #endif
  169. },
  170. /**
  171. * 自定义下拉刷新控件被下拉
  172. */
  173. refresherpulling() {
  174. // console.log('refresherpulling run')
  175. },
  176. /**
  177. * 自定义下拉刷新被触发
  178. */
  179. refresherrefresh(e) {
  180. // console.log('refresherrefresh run', e)
  181. if (this.status !== 'loading') {
  182. this.showRefresh = true
  183. this.refresherTriggered = true
  184. this.handleRefresh()
  185. }
  186. },
  187. /**
  188. * 自定义下拉刷新被复位/中止
  189. */
  190. refresherRestoreOrAbort(e) {
  191. // console.log('refresherRestoreOrAbort')
  192. if (!this.restoreFlag) {
  193. this.refresherTriggered = 'restore'
  194. this.restoreFlag = true
  195. }
  196. this.$nextTick(function() {
  197. this.refresherTriggered = false
  198. })
  199. this.showRefresh = false
  200. },
  201. /**
  202. * 滚动到顶部
  203. */
  204. handleScrollUpper() {
  205. // console.log('handleScrollUpper')
  206. this.restoreFlag = false
  207. this.refresherTriggered = 'restore'
  208. this.$nextTick(function() {
  209. this.refresherTriggered = false
  210. })
  211. },
  212. /**
  213. * 监听滚动
  214. * @param {Object} evt
  215. */
  216. handleScroll(evt) {
  217. this.restoreFlag = false
  218. this.$emit('scroll', evt)
  219. // evt.detail.scrollTop
  220. },
  221. /**
  222. * 触发下拉刷新
  223. */
  224. handleRefresh() {
  225. if (this.timer) {
  226. clearTimeout(this.timer)
  227. this.timer = null
  228. }
  229. // 处理 refresherrefresh 事件被触发多次问题,节流
  230. this.timer = setTimeout(() => {
  231. this.$emit('refresh')
  232. clearTimeout(this.timer)
  233. this.timer = null
  234. }, 30)
  235. },
  236. /**
  237. * 触发上拉加载
  238. */
  239. handleLoadMore() {
  240. if (!this.loadMore) return
  241. // console.log('loadmore')
  242. this.showLoadMore = true
  243. if (this.status === 'noMore') return
  244. this.$emit('loadmore')
  245. }
  246. }
  247. }
  248. </script>
  249. <style scoped lang="scss">
  250. .wk-scroll-view {
  251. position: relative;
  252. width: 100%;
  253. height: 100%;
  254. overflow: auto;
  255. }
  256. .no-data {
  257. width: 100%;
  258. text-align: center;
  259. font-size: 26rpx;
  260. color: #BBBBBB;
  261. background-position: center 200rpx;
  262. background-repeat: no-repeat;
  263. background-size: 36%;
  264. padding-top: 520rpx;
  265. }
  266. </style>