张延森 пре 4 година
родитељ
комит
0aaef9e904

+ 5
- 4
package.json Прегледај датотеку

@@ -43,6 +43,7 @@
43 43
     "@tarojs/taro": "3.0.13",
44 44
     "lodash": "4.17.15",
45 45
     "lodash.groupby": "^4.6.0",
46
+    "miniprogram-recycle-view": "^0.1.5",
46 47
     "react": "^16.10.0",
47 48
     "react-dom": "^16.10.0",
48 49
     "react-redux": "^7.2.2",
@@ -53,14 +54,14 @@
53 54
     "@babel/core": "^7.8.0",
54 55
     "@tarojs/mini-runner": "3.0.13",
55 56
     "@tarojs/webpack-runner": "3.0.13",
57
+    "@types/react": "^16.0.0",
56 58
     "@types/webpack-env": "^1.13.6",
57 59
     "babel-preset-taro": "3.0.13",
58 60
     "eslint": "^6.8.0",
59 61
     "eslint-config-taro": "3.0.13",
60
-    "stylelint": "9.3.0",
61
-    "@types/react": "^16.0.0",
62
-    "eslint-plugin-react": "^7.8.2",
63 62
     "eslint-plugin-import": "^2.12.0",
64
-    "eslint-plugin-react-hooks": "^1.6.1"
63
+    "eslint-plugin-react": "^7.8.2",
64
+    "eslint-plugin-react-hooks": "^1.6.1",
65
+    "stylelint": "9.3.0"
65 66
   }
66 67
 }

+ 4
- 0
src/app.config.js Прегледај датотеку

@@ -19,5 +19,9 @@ export default {
19 19
     navigationBarBackgroundColor: '#fff',
20 20
     navigationBarTitleText: 'WeChat',
21 21
     navigationBarTextStyle: 'black'
22
+  },
23
+  usingComponents: {
24
+    "recycle-view": "./compents/miniprogram-recycle-view/recycle-view",
25
+    "recycle-item": "./compents/miniprogram-recycle-view/recycle-item"
22 26
   }
23 27
 }

+ 70
- 0
src/compents/RecycleList/index.jsx Прегледај датотеку

@@ -0,0 +1,70 @@
1
+import React, { useEffect, useRef, useState } from 'react'
2
+import Taro from '@tarojs/taro'
3
+
4
+export default props => {
5
+  const id = useRef(`vlist-${Math.random().toString(36).substring(2)}`)
6
+
7
+  const [width, setWidth] = useState()
8
+  const [height, setHeight] = useState()
9
+  
10
+  const hanldeScrollToLower = e => {
11
+    if ((props.list || []).length >= (props.total - 0)) {
12
+      return
13
+    }
14
+
15
+    if (props.onScrollToLower) {
16
+      props.onScrollToLower(e)
17
+    }
18
+  }
19
+  
20
+
21
+  useEffect(() => {
22
+    let sysInfo = {}
23
+    if (!props.width || !props.height) {
24
+      sysInfo = Taro.getSystemInfoSync()
25
+    }
26
+
27
+    if (!props.width) {
28
+      setWidth(sysInfo.safeArea.width)
29
+    } else {
30
+      setWidth(props.width)
31
+    }
32
+
33
+    if (!props.height) {
34
+      setHeight(sysInfo.safeArea.height)
35
+    } else {
36
+      setHeight(props.height)
37
+    }
38
+  }, [props.width, props.height])
39
+
40
+  return (
41
+    <recycle-view
42
+      id={id.current}
43
+      width={width}
44
+      height={height}
45
+      lower-threshold={props.lowerThreshold || 50}
46
+      upper-threshold={props.upperThreshold || 50}
47
+      onScroll={props.onScroll}
48
+      onScrollToLower={hanldeScrollToLower}
49
+      onScrollToUpper={props.onScrollToUpper}
50
+      scroll-top={true}
51
+      scroll-y={true}
52
+      batch="{{batchSetRecycleData}}"
53
+      enable-back-to-top={true}
54
+    >
55
+      {
56
+        props.before && <view slot="before">{props.before}</view>
57
+      }
58
+      {
59
+        (props.list || []).map((item, inx) => (
60
+          <recycle-item key={props.dataKey? item[props.dataKey] : inx}>
61
+            {props.renderItem(item)}
62
+          </recycle-item>
63
+        ))
64
+      }
65
+      {
66
+        props.after && <view slot="after">{props.after}</view>
67
+      }      
68
+    </recycle-view>
69
+  )
70
+}

+ 45
- 0
src/compents/miniprogram-recycle-view/index.d.ts Прегледај датотеку

@@ -0,0 +1,45 @@
1
+declare namespace recycleContext {
2
+    interface itemSize {
3
+        width: number;
4
+        height: number;
5
+    }
6
+    
7
+    type Component = any;
8
+    type Page = any;
9
+    
10
+    type itemSizeFunc<T> = (item: T, index: number) => itemSize
11
+    
12
+    interface options<T> {
13
+        id: string;
14
+        dataKey: string;
15
+        page: Component | Page;
16
+        itemSize: itemSizeFunc<T> | itemSize;
17
+        useInPage?: boolean;
18
+        root?: Page;
19
+    }
20
+    
21
+    interface position {
22
+        left: number;
23
+        top: number;
24
+        width: number;
25
+        height: number;
26
+    }
27
+    
28
+    interface RecycleContext<T> {
29
+        append(list: T[], callback?: () => void): RecycleContext<T>
30
+        appendList(list: T[], callback?: () => void): RecycleContext<T>
31
+        splice(begin: number, deleteCount: number, appendList: T[], callback?: () => void): RecycleContext<T>;
32
+        updateList(beginIndex: number, list: T[], callback?: () => void): RecycleContext<T>
33
+        update(beginIndex: number, list: T[], callback?: () => void): RecycleContext<T>
34
+        destroy(): RecycleContext<T>
35
+        forceUpdate(callback: () => void, reinitSlot: boolean): RecycleContext<T>
36
+        getBoundingClientRect(index: number | undefined): position | position[]
37
+        getScrollTop(): number;
38
+        transformRpx(rpx: number, addPxSuffix?: string): number;
39
+        getViewportItems(inViewportPx: number): T[]
40
+        getList(): T[]
41
+    }
42
+}
43
+declare function createRecycleContext<T>(op: recycleContext.options<T>): recycleContext.RecycleContext<T>
44
+
45
+export = createRecycleContext;

+ 851
- 0
src/compents/miniprogram-recycle-view/index.js Прегледај датотеку

@@ -0,0 +1,851 @@
1
+module.exports =
2
+/******/ (function(modules) { // webpackBootstrap
3
+/******/ 	// The module cache
4
+/******/ 	var installedModules = {};
5
+/******/
6
+/******/ 	// The require function
7
+/******/ 	function __webpack_require__(moduleId) {
8
+/******/
9
+/******/ 		// Check if module is in cache
10
+/******/ 		if(installedModules[moduleId]) {
11
+/******/ 			return installedModules[moduleId].exports;
12
+/******/ 		}
13
+/******/ 		// Create a new module (and put it into the cache)
14
+/******/ 		var module = installedModules[moduleId] = {
15
+/******/ 			i: moduleId,
16
+/******/ 			l: false,
17
+/******/ 			exports: {}
18
+/******/ 		};
19
+/******/
20
+/******/ 		// Execute the module function
21
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22
+/******/
23
+/******/ 		// Flag the module as loaded
24
+/******/ 		module.l = true;
25
+/******/
26
+/******/ 		// Return the exports of the module
27
+/******/ 		return module.exports;
28
+/******/ 	}
29
+/******/
30
+/******/
31
+/******/ 	// expose the modules object (__webpack_modules__)
32
+/******/ 	__webpack_require__.m = modules;
33
+/******/
34
+/******/ 	// expose the module cache
35
+/******/ 	__webpack_require__.c = installedModules;
36
+/******/
37
+/******/ 	// define getter function for harmony exports
38
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
39
+/******/ 		if(!__webpack_require__.o(exports, name)) {
40
+/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
41
+/******/ 		}
42
+/******/ 	};
43
+/******/
44
+/******/ 	// define __esModule on exports
45
+/******/ 	__webpack_require__.r = function(exports) {
46
+/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
47
+/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
48
+/******/ 		}
49
+/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
50
+/******/ 	};
51
+/******/
52
+/******/ 	// create a fake namespace object
53
+/******/ 	// mode & 1: value is a module id, require it
54
+/******/ 	// mode & 2: merge all properties of value into the ns
55
+/******/ 	// mode & 4: return value when already ns object
56
+/******/ 	// mode & 8|1: behave like require
57
+/******/ 	__webpack_require__.t = function(value, mode) {
58
+/******/ 		if(mode & 1) value = __webpack_require__(value);
59
+/******/ 		if(mode & 8) return value;
60
+/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
61
+/******/ 		var ns = Object.create(null);
62
+/******/ 		__webpack_require__.r(ns);
63
+/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
64
+/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
65
+/******/ 		return ns;
66
+/******/ 	};
67
+/******/
68
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
69
+/******/ 	__webpack_require__.n = function(module) {
70
+/******/ 		var getter = module && module.__esModule ?
71
+/******/ 			function getDefault() { return module['default']; } :
72
+/******/ 			function getModuleExports() { return module; };
73
+/******/ 		__webpack_require__.d(getter, 'a', getter);
74
+/******/ 		return getter;
75
+/******/ 	};
76
+/******/
77
+/******/ 	// Object.prototype.hasOwnProperty.call
78
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
79
+/******/
80
+/******/ 	// __webpack_public_path__
81
+/******/ 	__webpack_require__.p = "";
82
+/******/
83
+/******/
84
+/******/ 	// Load entry module and return exports
85
+/******/ 	return __webpack_require__(__webpack_require__.s = 2);
86
+/******/ })
87
+/************************************************************************/
88
+/******/ ([
89
+/* 0 */
90
+/***/ (function(module, exports, __webpack_require__) {
91
+
92
+"use strict";
93
+
94
+
95
+var isIPhone = false;
96
+var deviceWidth = void 0;
97
+var deviceDPR = void 0;
98
+var BASE_DEVICE_WIDTH = 750;
99
+var checkDeviceWidth = function checkDeviceWidth() {
100
+  var info = wx.getSystemInfoSync();
101
+  // console.log('info', info)
102
+  isIPhone = info.platform === 'ios';
103
+  var newDeviceWidth = info.screenWidth || 375;
104
+  var newDeviceDPR = info.pixelRatio || 2;
105
+
106
+  if (!isIPhone) {
107
+    // HACK switch width and height when landscape
108
+    // const newDeviceHeight = info.screenHeight || 375
109
+    // 暂时不处理转屏的情况
110
+  }
111
+
112
+  if (newDeviceWidth !== deviceWidth || newDeviceDPR !== deviceDPR) {
113
+    deviceWidth = newDeviceWidth;
114
+    deviceDPR = newDeviceDPR;
115
+    // console.info('Updated device width: ' + newDeviceWidth + 'px DPR ' + newDeviceDPR)
116
+  }
117
+};
118
+checkDeviceWidth();
119
+
120
+var eps = 1e-4;
121
+var transformByDPR = function transformByDPR(number) {
122
+  if (number === 0) {
123
+    return 0;
124
+  }
125
+  number = number / BASE_DEVICE_WIDTH * deviceWidth;
126
+  number = Math.floor(number + eps);
127
+  if (number === 0) {
128
+    if (deviceDPR === 1 || !isIPhone) {
129
+      return 1;
130
+    }
131
+    return 0.5;
132
+  }
133
+  return number;
134
+};
135
+
136
+var rpxRE = /([+-]?\d+(?:\.\d+)?)rpx/gi;
137
+// const inlineRpxRE = /(?::|\s|\(|\/)([+-]?\d+(?:\.\d+)?)rpx/g
138
+
139
+var transformRpx = function transformRpx(style, inline) {
140
+  if (typeof style !== 'string') {
141
+    return style;
142
+  }
143
+  var re = rpxRE;
144
+  return style.replace(re, function (match, num) {
145
+    return transformByDPR(Number(num)) + (inline ? 'px' : '');
146
+  });
147
+};
148
+
149
+module.exports = {
150
+  transformRpx: transformRpx
151
+};
152
+
153
+/***/ }),
154
+/* 1 */
155
+/***/ (function(module, exports, __webpack_require__) {
156
+
157
+"use strict";
158
+
159
+
160
+module.exports = {};
161
+
162
+/***/ }),
163
+/* 2 */
164
+/***/ (function(module, exports, __webpack_require__) {
165
+
166
+"use strict";
167
+
168
+
169
+/**
170
+ * recycle-view组件的api使用
171
+ * 提供wx.createRecycleContext进行管理功能
172
+ */
173
+var RecycleContext = __webpack_require__(3);
174
+
175
+/**
176
+ * @params options参数是object对象,展开的结构如下
177
+      id: recycle-view的id
178
+      dataKey: recycle-item的wx:for绑定的数据变量
179
+      page: recycle-view所在的页面或组件的实例
180
+      itemSize: 函数或者是Object对象,生成每个recycle-item的宽和高
181
+ * @return RecycleContext对象
182
+ */
183
+module.exports = function (options) {
184
+  return new RecycleContext(options);
185
+};
186
+
187
+/***/ }),
188
+/* 3 */
189
+/***/ (function(module, exports, __webpack_require__) {
190
+
191
+"use strict";
192
+
193
+
194
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
195
+
196
+/* eslint complexity: ["error", {"max": 50}] */
197
+var recycleData = __webpack_require__(1);
198
+var recycleViewportChangeFunc = __webpack_require__(4);
199
+var transformRpx = __webpack_require__(0);
200
+
201
+var RECT_SIZE = 200;
202
+
203
+// eslint-disable-next-line no-complexity
204
+function RecycleContext(_ref) {
205
+  var _this = this;
206
+
207
+  var id = _ref.id,
208
+      dataKey = _ref.dataKey,
209
+      page = _ref.page,
210
+      itemSize = _ref.itemSize,
211
+      useInPage = _ref.useInPage,
212
+      placeholderClass = _ref.placeholderClass,
213
+      root = _ref.root;
214
+
215
+  if (!id || !dataKey || !page || !itemSize) {
216
+    throw new Error('parameter id, dataKey, page, itemSize is required');
217
+  }
218
+  if (typeof itemSize !== 'function' && (typeof itemSize === 'undefined' ? 'undefined' : _typeof(itemSize)) !== 'object') {
219
+    throw new Error('parameter itemSize must be function or object with key width and height');
220
+  }
221
+  if ((typeof itemSize === 'undefined' ? 'undefined' : _typeof(itemSize)) === 'object' && (!itemSize.width || !itemSize.height) && (!itemSize.props || !itemSize.queryClass || !itemSize.dataKey)) {
222
+    throw new Error('parameter itemSize must be function or object with key width and height');
223
+  }
224
+  this.id = id;
225
+  this.dataKey = dataKey;
226
+  this.page = page;
227
+  // 加root参数给useInPage单独使用
228
+  this.root = root;
229
+  this.placeholderClass = placeholderClass;
230
+  page._recycleViewportChange = recycleViewportChangeFunc;
231
+  this.comp = page.selectComponent('#' + id);
232
+  this.itemSize = itemSize;
233
+  this.itemSizeOpt = itemSize;
234
+  // if (!this.comp) {
235
+  // throw `<recycle-view> with id ${id} not found`
236
+  // }
237
+  this.useInPage = useInPage || false;
238
+  if (this.comp) {
239
+    this.comp.context = this;
240
+    this.comp.setPage(page);
241
+    this.comp.setUseInPage(this.useInPage);
242
+  }
243
+  if (this.useInPage && !this.root) {
244
+    throw new Error('parameter root is required when useInPage is true');
245
+  }
246
+  if (this.useInPage) {
247
+    this.oldPageScroll = this.root.onPageScroll;
248
+    // 重写onPageScroll事件
249
+    this.root.onPageScroll = function (e) {
250
+      // this.checkComp();
251
+      if (_this.comp) {
252
+        _this.comp._scrollViewDidScroll({
253
+          detail: {
254
+            scrollLeft: 0,
255
+            scrollTop: e.scrollTop
256
+          }
257
+        });
258
+      }
259
+      _this.oldPageScroll.apply(_this.root, [e]);
260
+    };
261
+    this.oldReachBottom = this.root.onReachBottom;
262
+    this.root.onReachBottom = function (e) {
263
+      if (_this.comp) {
264
+        _this.comp.triggerEvent('scrolltolower', {});
265
+      }
266
+      _this.oldReachBottom.apply(_this.root, [e]);
267
+    };
268
+    this.oldPullDownRefresh = this.root.onPullDownRefresh;
269
+    this.root.onPullDownRefresh = function (e) {
270
+      if (_this.comp) {
271
+        _this.comp.triggerEvent('scrolltoupper', {});
272
+      }
273
+      _this.oldPullDownRefresh.apply(_this.root, [e]);
274
+    };
275
+  }
276
+}
277
+RecycleContext.prototype.checkComp = function () {
278
+  if (!this.comp) {
279
+    this.comp = this.page.selectComponent('#' + this.id);
280
+    if (this.comp) {
281
+      this.comp.setUseInPage(this.useInPage);
282
+      this.comp.context = this;
283
+      this.comp.setPage(this.page);
284
+    } else {
285
+      throw new Error('the recycle-view correspond to this context is detached, pls create another RecycleContext');
286
+    }
287
+  }
288
+};
289
+RecycleContext.prototype.appendList = function (list, cb) {
290
+  this.checkComp();
291
+  var id = this.id;
292
+  var dataKey = this.dataKey;
293
+  if (!recycleData[id]) {
294
+    recycleData[id] = {
295
+      key: dataKey,
296
+      id: id,
297
+      list: list,
298
+      sizeMap: {},
299
+      sizeArray: []
300
+    };
301
+  } else {
302
+    recycleData[id].dataKey = dataKey;
303
+    recycleData[id].list = recycleData[id].list.concat(list);
304
+  }
305
+  this._forceRerender(id, cb);
306
+  return this;
307
+};
308
+RecycleContext.prototype._forceRerender = function (id, cb) {
309
+  this.isDataReady = true; // 首次调用说明数据已经ready了
310
+  // 动态计算高度并缓存
311
+  var that = this;
312
+  var allrect = null;
313
+  var parentRect = null;
314
+  var count = 0;
315
+
316
+  function setPlaceholderImage() {
317
+    if (!allrect || !parentRect) return;
318
+    var svgRects = [];
319
+    for (var i = 0; i < count; i++) {
320
+      svgRects.push({
321
+        left: allrect[i].left - parentRect.left,
322
+        top: allrect[i].top - parentRect.top,
323
+        width: allrect[i].width,
324
+        height: allrect[i].height
325
+      });
326
+    }
327
+    that.comp.setPlaceholderImage(svgRects, {
328
+      width: parentRect.width,
329
+      height: parentRect.height
330
+    });
331
+  }
332
+  function newcb() {
333
+    if (cb) {
334
+      cb();
335
+    }
336
+    // 计算placeholder, 只有在动态计算高度的时候才支持
337
+    if (that.autoCalculateSize && that.placeholderClass) {
338
+      var newQueryClass = [];
339
+      that.placeholderClass.forEach(function (item) {
340
+        newQueryClass.push('.' + that.itemSizeOpt.queryClass + ' .' + item);
341
+      });
342
+      // newQueryClass.push(`.` + that.itemSizeOpt.queryClass)
343
+      count = newQueryClass.length;
344
+      wx.createSelectorQuery().selectAll(newQueryClass.join(',')).boundingClientRect(function (rect) {
345
+        if (rect.length < count) return;
346
+        allrect = rect;
347
+        setPlaceholderImage();
348
+      }).exec();
349
+      wx.createSelectorQuery().select('.' + that.itemSizeOpt.queryClass).boundingClientRect(function (rect) {
350
+        parentRect = rect;
351
+        setPlaceholderImage();
352
+      }).exec();
353
+    }
354
+  }
355
+  if (Object.prototype.toString.call(this.itemSizeOpt) === '[object Object]' && this.itemSizeOpt && !this.itemSizeOpt.width) {
356
+    this._recalculateSizeByProp(recycleData[id].list, function (sizeData) {
357
+      recycleData[id].sizeMap = sizeData.map;
358
+      recycleData[id].sizeArray = sizeData.array;
359
+      // 触发强制渲染
360
+      that.comp.forceUpdate(newcb);
361
+    });
362
+    return;
363
+  }
364
+  var sizeData = this._recalculateSize(recycleData[id].list);
365
+  recycleData[id].sizeMap = sizeData.map;
366
+  // console.log('size is', sizeData.array, sizeData.map, 'totalHeight', sizeData.totalHeight)
367
+  // console.log('sizeArray', sizeData.array)
368
+  recycleData[id].sizeArray = sizeData.array;
369
+  // 触发强制渲染
370
+  this.comp.forceUpdate(cb);
371
+};
372
+function getValue(item, key) {
373
+  if (!key) return item;
374
+  if (typeof item[key] !== 'undefined') return item[key];
375
+  var keyItems = key.split('.');
376
+  for (var i = 0; i < keyItems.length; i++) {
377
+    item = item[keyItems[i]];
378
+    if (typeof item === 'undefined' || (typeof item === 'undefined' ? 'undefined' : _typeof(item)) === 'object' && !item) {
379
+      return undefined;
380
+    }
381
+  }
382
+  return item;
383
+}
384
+function getValues(item, keys) {
385
+  if (Object.prototype.toString.call(keys) !== '[object Array]') {
386
+    keys = [keys];
387
+  }
388
+  var vals = {};
389
+  for (var i = 0; i < keys.length; i++) {
390
+    vals[keys[i]] = getValue(item, keys[i]);
391
+  }
392
+  return vals;
393
+}
394
+function isArray(arr) {
395
+  return Object.prototype.toString.call(arr) === '[object Array]';
396
+}
397
+function isSamePureValue(item1, item2) {
398
+  if ((typeof item1 === 'undefined' ? 'undefined' : _typeof(item1)) !== (typeof item2 === 'undefined' ? 'undefined' : _typeof(item2))) return false;
399
+  if (isArray(item1) && isArray(item2)) {
400
+    if (item1.length !== item2.length) return false;
401
+    for (var i = 0; i < item1.length; i++) {
402
+      if (item1[i] !== item2[i]) return false;
403
+    }
404
+    return true;
405
+  }
406
+  return item1 === item2;
407
+}
408
+function isSameValue(item1, item2, keys) {
409
+  if (!isArray(keys)) {
410
+    keys = [keys];
411
+  }
412
+  for (var i = 0; i < keys.length; i++) {
413
+    if (!isSamePureValue(getValue(item1, keys[i]), getValue(item2, keys[i]))) return false;
414
+  }
415
+  return true;
416
+}
417
+RecycleContext.prototype._recalculateSizeByProp = function (list, cb) {
418
+  var itemSize = this.itemSizeOpt;
419
+  var propValueMap = this.propValueMap || [];
420
+  var calcNewItems = [];
421
+  var needCalcPropIndex = [];
422
+  if (itemSize.cacheKey) {
423
+    propValueMap = wx.getStorageSync(itemSize.cacheKey) || [];
424
+    // eslint-disable-next-line no-console
425
+    // console.log('[recycle-view] get itemSize from cache', propValueMap)
426
+  }
427
+  this.autoCalculateSize = true;
428
+  var item2PropValueMap = [];
429
+  for (var i = 0; i < list.length; i++) {
430
+    var item2PropValueIndex = propValueMap.length;
431
+    if (!propValueMap.length) {
432
+      var val = getValues(list[i], itemSize.props);
433
+      val.__index__ = i;
434
+      propValueMap.push(val);
435
+      calcNewItems.push(list[i]);
436
+      needCalcPropIndex.push(item2PropValueIndex);
437
+      item2PropValueMap.push({
438
+        index: i,
439
+        sizeIndex: item2PropValueIndex
440
+      });
441
+      continue;
442
+    }
443
+    var found = false;
444
+    for (var j = 0; j < propValueMap.length; j++) {
445
+      if (isSameValue(propValueMap[j], list[i], itemSize.props)) {
446
+        item2PropValueIndex = j;
447
+        found = true;
448
+        break;
449
+      }
450
+    }
451
+    if (!found) {
452
+      var _val = getValues(list[i], itemSize.props);
453
+      _val.__index__ = i;
454
+      propValueMap.push(_val);
455
+      calcNewItems.push(list[i]);
456
+      needCalcPropIndex.push(item2PropValueIndex);
457
+    }
458
+    item2PropValueMap.push({
459
+      index: i,
460
+      sizeIndex: item2PropValueIndex
461
+    });
462
+  }
463
+  // this.item2PropValueMap = item2PropValueMap
464
+  this.propValueMap = propValueMap;
465
+  if (propValueMap.length > 10) {
466
+    // eslint-disable-next-line no-console
467
+    console.warn('[recycle-view] get itemSize count exceed maximum of 10, now got', propValueMap);
468
+  }
469
+  // console.log('itemsize', propValueMap, item2PropValueMap)
470
+  // 预先渲染
471
+  var that = this;
472
+  function newItemSize(item, index) {
473
+    var sizeIndex = item2PropValueMap[index];
474
+    if (!sizeIndex) {
475
+      // eslint-disable-next-line no-console
476
+      console.error('[recycle-view] auto calculate size array error, no map size found', item, index, item2PropValueMap);
477
+      throw new Error('[recycle-view] auto calculate size array error, no map size found');
478
+    }
479
+    var size = propValueMap[sizeIndex.sizeIndex];
480
+    if (!size) {
481
+      // eslint-disable-next-line no-console
482
+      console.log('[recycle-view] auto calculate size array error, no size found', item, index, sizeIndex, propValueMap);
483
+      throw new Error('[recycle-view] auto calculate size array error, no size found');
484
+    }
485
+    return {
486
+      width: size.width,
487
+      height: size.height
488
+    };
489
+  }
490
+  function sizeReady(rects) {
491
+    rects.forEach(function (rect, index) {
492
+      var propValueIndex = needCalcPropIndex[index];
493
+      propValueMap[propValueIndex].width = rect.width;
494
+      propValueMap[propValueIndex].height = rect.height;
495
+    });
496
+    that.itemSize = newItemSize;
497
+    var sizeData = that._recalculateSize(list);
498
+    if (itemSize.cacheKey) {
499
+      wx.setStorageSync(itemSize.cacheKey, propValueMap); // 把数据缓存起来
500
+    }
501
+    if (cb) {
502
+      cb(sizeData);
503
+    }
504
+  }
505
+  if (calcNewItems.length) {
506
+    var obj = {};
507
+    obj[itemSize.dataKey] = calcNewItems;
508
+    this.page.setData(obj, function () {
509
+      // wx.createSelectorQuery().select(itemSize.componentClass).boundingClientRect(rects => {
510
+      //   compSize = rects;
511
+      //   if (compSize && allItemSize) {
512
+      //     sizeReady();
513
+      //   }
514
+      // }).exec();
515
+      wx.createSelectorQuery().selectAll('.' + itemSize.queryClass).boundingClientRect(function (rects) {
516
+        sizeReady(rects);
517
+      }).exec();
518
+    });
519
+  } else {
520
+    that.itemSize = newItemSize;
521
+    var sizeData = that._recalculateSize(list);
522
+    if (cb) {
523
+      cb(sizeData);
524
+    }
525
+  }
526
+};
527
+// 当before和after这2个slot发生变化的时候调用一下此接口
528
+RecycleContext.prototype._recalculateSize = function (list) {
529
+  // 遍历所有的数据
530
+  // 应该最多就千量级的, 遍历没有问题
531
+  var sizeMap = {};
532
+  var func = this.itemSize;
533
+  var funcExist = typeof func === 'function';
534
+  var comp = this.comp;
535
+  var compData = comp.data;
536
+  var offsetLeft = 0;
537
+  var offsetTop = 0;
538
+  var line = 0;
539
+  var column = 0;
540
+  var sizeArray = [];
541
+  var listLen = list.length;
542
+  // 把整个页面拆分成200*200的很多个方格, 判断每个数据落在哪个方格上
543
+  for (var i = 0; i < listLen; i++) {
544
+    list[i].__index__ = i;
545
+    var itemSize = {};
546
+    // 获取到每一项的宽和高
547
+    if (funcExist) {
548
+      // 必须保证返回的每一行的高度一样
549
+      itemSize = func && func.call(this, list[i], i);
550
+    } else {
551
+      itemSize = {
552
+        width: func.width,
553
+        height: func.height
554
+      };
555
+    }
556
+    itemSize = Object.assign({}, itemSize);
557
+    sizeArray.push(itemSize);
558
+    // 判断数据落到哪个方格上
559
+    // 超过了宽度, 移动到下一行, 再根据高度判断是否需要移动到下一个方格
560
+    if (offsetLeft + itemSize.width > compData.width) {
561
+      column = 0;
562
+      offsetLeft = itemSize.width;
563
+      // Fixed issue #22
564
+      if (sizeArray.length >= 2) {
565
+        offsetTop += sizeArray[sizeArray.length - 2].height || 0; // 加上最后一个数据的高度
566
+      } else {
567
+        offsetTop += itemSize.height;
568
+      }
569
+      // offsetTop += sizeArray[sizeArray.length - 2].height // 加上最后一个数据的高度
570
+      // 根据高度判断是否需要移动到下一个方格
571
+      if (offsetTop >= RECT_SIZE * (line + 1)) {
572
+        // fix: 当区块比较大时,会缺失块区域信息
573
+        var lastIdx = i - 1;
574
+        var lastLine = line;
575
+
576
+        line += parseInt((offsetTop - RECT_SIZE * line) / RECT_SIZE, 10);
577
+
578
+        for (var idx = lastLine; idx < line; idx++) {
579
+          var _key = idx + '.' + column;
580
+          if (!sizeMap[_key]) {
581
+            sizeMap[_key] = [];
582
+          }
583
+          sizeMap[_key].push(lastIdx);
584
+        }
585
+      }
586
+
587
+      // 新起一行的元素, beforeHeight是前一个元素的beforeHeight和height相加
588
+      if (i === 0) {
589
+        itemSize.beforeHeight = 0;
590
+      } else {
591
+        var prevItemSize = sizeArray[sizeArray.length - 2];
592
+        itemSize.beforeHeight = prevItemSize.beforeHeight + prevItemSize.height;
593
+      }
594
+    } else {
595
+      if (offsetLeft >= RECT_SIZE * (column + 1)) {
596
+        column++;
597
+      }
598
+      offsetLeft += itemSize.width;
599
+      if (i === 0) {
600
+        itemSize.beforeHeight = 0;
601
+      } else {
602
+        // 同一行的元素, beforeHeight和前面一个元素的beforeHeight一样
603
+        itemSize.beforeHeight = sizeArray[sizeArray.length - 2].beforeHeight;
604
+      }
605
+    }
606
+    var key = line + '.' + column;
607
+    if (!sizeMap[key]) {
608
+      sizeMap[key] = [];
609
+    }
610
+    sizeMap[key].push(i);
611
+
612
+    // fix: 当区块比较大时,会缺失块区域信息
613
+    if (listLen - 1 === i && itemSize.height > RECT_SIZE) {
614
+      var _lastIdx = line;
615
+      offsetTop += itemSize.height;
616
+      line += parseInt((offsetTop - RECT_SIZE * line) / RECT_SIZE, 10);
617
+      for (var _idx = _lastIdx; _idx <= line; _idx++) {
618
+        var _key2 = _idx + '.' + column;
619
+        if (!sizeMap[_key2]) {
620
+          sizeMap[_key2] = [];
621
+        }
622
+        sizeMap[_key2].push(i);
623
+      }
624
+    }
625
+  }
626
+  // console.log('sizeMap', sizeMap)
627
+  var obj = {
628
+    array: sizeArray,
629
+    map: sizeMap,
630
+    totalHeight: sizeArray.length ? sizeArray[sizeArray.length - 1].beforeHeight + sizeArray[sizeArray.length - 1].height : 0
631
+  };
632
+  comp.setItemSize(obj);
633
+  return obj;
634
+};
635
+RecycleContext.prototype.deleteList = function (beginIndex, count, cb) {
636
+  this.checkComp();
637
+  var id = this.id;
638
+  if (!recycleData[id]) {
639
+    return this;
640
+  }
641
+  recycleData[id].list.splice(beginIndex, count);
642
+  this._forceRerender(id, cb);
643
+  return this;
644
+};
645
+RecycleContext.prototype.updateList = function (beginIndex, list, cb) {
646
+  this.checkComp();
647
+  var id = this.id;
648
+  if (!recycleData[id]) {
649
+    return this;
650
+  }
651
+  var len = recycleData[id].list.length;
652
+  for (var i = 0; i < list.length && beginIndex < len; i++) {
653
+    recycleData[id].list[beginIndex++] = list[i];
654
+  }
655
+  this._forceRerender(id, cb);
656
+  return this;
657
+};
658
+RecycleContext.prototype.update = RecycleContext.prototype.updateList;
659
+RecycleContext.prototype.splice = function (begin, deleteCount, appendList, cb) {
660
+  this.checkComp();
661
+  var id = this.id;
662
+  var dataKey = this.dataKey;
663
+  // begin是数组
664
+  if ((typeof begin === 'undefined' ? 'undefined' : _typeof(begin)) === 'object' && begin.length) {
665
+    cb = deleteCount;
666
+    appendList = begin;
667
+  }
668
+  if (typeof appendList === 'function') {
669
+    cb = appendList;
670
+    appendList = [];
671
+  }
672
+  if (!recycleData[id]) {
673
+    recycleData[id] = {
674
+      key: dataKey,
675
+      id: id,
676
+      list: appendList || [],
677
+      sizeMap: {},
678
+      sizeArray: []
679
+    };
680
+  } else {
681
+    recycleData[id].dataKey = dataKey;
682
+    var list = recycleData[id].list;
683
+    if (appendList && appendList.length) {
684
+      list.splice.apply(list, [begin, deleteCount].concat(appendList));
685
+    } else {
686
+      list.splice(begin, deleteCount);
687
+    }
688
+  }
689
+  this._forceRerender(id, cb);
690
+  return this;
691
+};
692
+
693
+RecycleContext.prototype.append = RecycleContext.prototype.appendList;
694
+
695
+RecycleContext.prototype.destroy = function () {
696
+  if (this.useInPage) {
697
+    this.page.onPullDownRefresh = this.oldPullDownRefresh;
698
+    this.page.onReachBottom = this.oldReachBottom;
699
+    this.page.onPageScroll = this.oldPageScroll;
700
+    this.oldPageScroll = this.oldReachBottom = this.oldPullDownRefresh = null;
701
+  }
702
+  this.page = null;
703
+  this.comp = null;
704
+  if (recycleData[this.id]) {
705
+    delete recycleData[this.id];
706
+  }
707
+  return this;
708
+};
709
+// 重新更新下页面的数据
710
+RecycleContext.prototype.forceUpdate = function (cb, reinitSlot) {
711
+  var _this2 = this;
712
+
713
+  this.checkComp();
714
+  if (reinitSlot) {
715
+    this.comp.reRender(function () {
716
+      _this2._forceRerender(_this2.id, cb);
717
+    });
718
+  } else {
719
+    this._forceRerender(this.id, cb);
720
+  }
721
+  return this;
722
+};
723
+RecycleContext.prototype.getBoundingClientRect = function (index) {
724
+  this.checkComp();
725
+  if (!recycleData[this.id]) {
726
+    return null;
727
+  }
728
+  var sizeArray = recycleData[this.id].sizeArray;
729
+  if (!sizeArray || !sizeArray.length) {
730
+    return null;
731
+  }
732
+  if (typeof index === 'undefined') {
733
+    var list = [];
734
+    for (var i = 0; i < sizeArray.length; i++) {
735
+      list.push({
736
+        left: 0,
737
+        top: sizeArray[i].beforeHeight,
738
+        width: sizeArray[i].width,
739
+        height: sizeArray[i].height
740
+      });
741
+    }
742
+    return list;
743
+  }
744
+  index = parseInt(index, 10);
745
+  if (index >= sizeArray.length || index < 0) return null;
746
+  return {
747
+    left: 0,
748
+    top: sizeArray[index].beforeHeight,
749
+    width: sizeArray[index].width,
750
+    height: sizeArray[index].height
751
+  };
752
+};
753
+RecycleContext.prototype.getScrollTop = function () {
754
+  this.checkComp();
755
+  return this.comp.currentScrollTop || 0;
756
+};
757
+// 将px转化为rpx
758
+RecycleContext.prototype.transformRpx = RecycleContext.transformRpx = function (str, addPxSuffix) {
759
+  if (typeof str === 'number') str += 'rpx';
760
+  return parseFloat(transformRpx.transformRpx(str, addPxSuffix));
761
+};
762
+RecycleContext.prototype.getViewportItems = function (inViewportPx) {
763
+  this.checkComp();
764
+  var indexes = this.comp.getIndexesInViewport(inViewportPx);
765
+  if (indexes.length <= 0) return [];
766
+  var viewportItems = [];
767
+  var list = recycleData[this.id].list;
768
+  for (var i = 0; i < indexes.length; i++) {
769
+    viewportItems.push(list[indexes[i]]);
770
+  }
771
+  return viewportItems;
772
+};
773
+RecycleContext.prototype.getTotalHeight = function () {
774
+  this.checkComp();
775
+  return this.comp.getTotalHeight();
776
+};
777
+// 返回完整的列表数据
778
+RecycleContext.prototype.getList = function () {
779
+  if (!recycleData[this.id]) {
780
+    return [];
781
+  }
782
+  return recycleData[this.id].list;
783
+};
784
+module.exports = RecycleContext;
785
+
786
+/***/ }),
787
+/* 4 */
788
+/***/ (function(module, exports, __webpack_require__) {
789
+
790
+"use strict";
791
+
792
+
793
+/* eslint complexity: ["error", {"max": 50}] */
794
+var recycleData = __webpack_require__(1);
795
+
796
+module.exports = function (e, cb) {
797
+  var detail = e.detail;
798
+  // console.log('data change transfer use time', Date.now() - e.detail.timeStamp)
799
+  var newList = [];
800
+  var item = recycleData[detail.id];
801
+  // 边界值判断, 避免造成异常, 假设先调用了createRecycleContext, 然后再延迟2s调用append插入数据的情况
802
+  if (!item || !item.list) return;
803
+  var dataList = item.list;
804
+  var pos = detail.data;
805
+  var beginIndex = pos.beginIndex;
806
+  var endIndex = pos.endIndex;
807
+  item.pos = pos;
808
+  // 加ignoreBeginIndex和ignoreEndIndex
809
+  if (typeof beginIndex === 'undefined' || beginIndex === -1 || typeof endIndex === 'undefined' || endIndex === -1) {
810
+    newList = [];
811
+  } else {
812
+    var i = -1;
813
+    for (i = beginIndex; i < dataList.length && i <= endIndex; i++) {
814
+      if (i >= pos.ignoreBeginIndex && i <= pos.ignoreEndIndex) continue;
815
+      newList.push(dataList[i]);
816
+    }
817
+  }
818
+  var obj = {
819
+    // batchSetRecycleData: !this.data.batchSetRecycleData
820
+  };
821
+  obj[item.key] = newList;
822
+  var comp = this.selectComponent('#' + detail.id);
823
+  obj[comp.data.batchKey] = !this.data.batchSetRecycleData;
824
+  comp._setInnerBeforeAndAfterHeight({
825
+    beforeHeight: pos.minTop,
826
+    afterHeight: pos.afterHeight
827
+  });
828
+  this.setData(obj, function () {
829
+    if (typeof cb === 'function') {
830
+      cb();
831
+    }
832
+  });
833
+  // Fix #1
834
+  // 去掉了batchSetDataKey,支持一个页面内显示2个recycle-view
835
+  // const groupSetData = () => {
836
+  //   this.setData(obj)
837
+  //   comp._recycleInnerBatchDataChanged(() => {
838
+  //     if (typeof cb === 'function') {
839
+  //       cb()
840
+  //     }
841
+  //   })
842
+  // }
843
+  // if (typeof this.groupSetData === 'function') {
844
+  //   this.groupSetData(groupSetData)
845
+  // } else {
846
+  //   groupSetData()
847
+  // }
848
+};
849
+
850
+/***/ })
851
+/******/ ]);

+ 126
- 0
src/compents/miniprogram-recycle-view/recycle-item.js Прегледај датотеку

@@ -0,0 +1,126 @@
1
+module.exports =
2
+/******/ (function(modules) { // webpackBootstrap
3
+/******/ 	// The module cache
4
+/******/ 	var installedModules = {};
5
+/******/
6
+/******/ 	// The require function
7
+/******/ 	function __webpack_require__(moduleId) {
8
+/******/
9
+/******/ 		// Check if module is in cache
10
+/******/ 		if(installedModules[moduleId]) {
11
+/******/ 			return installedModules[moduleId].exports;
12
+/******/ 		}
13
+/******/ 		// Create a new module (and put it into the cache)
14
+/******/ 		var module = installedModules[moduleId] = {
15
+/******/ 			i: moduleId,
16
+/******/ 			l: false,
17
+/******/ 			exports: {}
18
+/******/ 		};
19
+/******/
20
+/******/ 		// Execute the module function
21
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22
+/******/
23
+/******/ 		// Flag the module as loaded
24
+/******/ 		module.l = true;
25
+/******/
26
+/******/ 		// Return the exports of the module
27
+/******/ 		return module.exports;
28
+/******/ 	}
29
+/******/
30
+/******/
31
+/******/ 	// expose the modules object (__webpack_modules__)
32
+/******/ 	__webpack_require__.m = modules;
33
+/******/
34
+/******/ 	// expose the module cache
35
+/******/ 	__webpack_require__.c = installedModules;
36
+/******/
37
+/******/ 	// define getter function for harmony exports
38
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
39
+/******/ 		if(!__webpack_require__.o(exports, name)) {
40
+/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
41
+/******/ 		}
42
+/******/ 	};
43
+/******/
44
+/******/ 	// define __esModule on exports
45
+/******/ 	__webpack_require__.r = function(exports) {
46
+/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
47
+/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
48
+/******/ 		}
49
+/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
50
+/******/ 	};
51
+/******/
52
+/******/ 	// create a fake namespace object
53
+/******/ 	// mode & 1: value is a module id, require it
54
+/******/ 	// mode & 2: merge all properties of value into the ns
55
+/******/ 	// mode & 4: return value when already ns object
56
+/******/ 	// mode & 8|1: behave like require
57
+/******/ 	__webpack_require__.t = function(value, mode) {
58
+/******/ 		if(mode & 1) value = __webpack_require__(value);
59
+/******/ 		if(mode & 8) return value;
60
+/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
61
+/******/ 		var ns = Object.create(null);
62
+/******/ 		__webpack_require__.r(ns);
63
+/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
64
+/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
65
+/******/ 		return ns;
66
+/******/ 	};
67
+/******/
68
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
69
+/******/ 	__webpack_require__.n = function(module) {
70
+/******/ 		var getter = module && module.__esModule ?
71
+/******/ 			function getDefault() { return module['default']; } :
72
+/******/ 			function getModuleExports() { return module; };
73
+/******/ 		__webpack_require__.d(getter, 'a', getter);
74
+/******/ 		return getter;
75
+/******/ 	};
76
+/******/
77
+/******/ 	// Object.prototype.hasOwnProperty.call
78
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
79
+/******/
80
+/******/ 	// __webpack_public_path__
81
+/******/ 	__webpack_require__.p = "";
82
+/******/
83
+/******/
84
+/******/ 	// Load entry module and return exports
85
+/******/ 	return __webpack_require__(__webpack_require__.s = 5);
86
+/******/ })
87
+/************************************************************************/
88
+/******/ ({
89
+
90
+/***/ 5:
91
+/***/ (function(module, exports, __webpack_require__) {
92
+
93
+"use strict";
94
+
95
+
96
+// components/recycle-item/recycle-item.js
97
+Component({
98
+  relations: {
99
+    './recycle-view': {
100
+      type: 'parent', // 关联的目标节点应为子节点
101
+      linked: function linked() {}
102
+    }
103
+  },
104
+  /**
105
+   * 组件的属性列表
106
+   */
107
+  properties: {},
108
+
109
+  /**
110
+   * 组件的初始数据
111
+   */
112
+  data: {
113
+    // height: 100
114
+  },
115
+
116
+  /**
117
+   * 组件的方法列表
118
+   */
119
+  methods: {
120
+    heightChange: function heightChange() {}
121
+  }
122
+});
123
+
124
+/***/ })
125
+
126
+/******/ });

+ 4
- 0
src/compents/miniprogram-recycle-view/recycle-item.json Прегледај датотеку

@@ -0,0 +1,4 @@
1
+{
2
+  "component": true,
3
+  "usingComponents": {}
4
+}

+ 4
- 0
src/compents/miniprogram-recycle-view/recycle-item.wxml Прегледај датотеку

@@ -0,0 +1,4 @@
1
+<!--components/recycle-item/recycle-item.wxml-->
2
+<view class="wx-recycle-item">
3
+  <slot></slot>
4
+</view>

+ 7
- 0
src/compents/miniprogram-recycle-view/recycle-item.wxss Прегледај датотеку

@@ -0,0 +1,7 @@
1
+/* components/recycle-item/recycle-item.wxss */
2
+:host {
3
+  display: inline-block;
4
+}
5
+.wx-recycle-item {
6
+  height: 100%;
7
+}

+ 925
- 0
src/compents/miniprogram-recycle-view/recycle-view.js Прегледај датотеку

@@ -0,0 +1,925 @@
1
+module.exports =
2
+/******/ (function(modules) { // webpackBootstrap
3
+/******/ 	// The module cache
4
+/******/ 	var installedModules = {};
5
+/******/
6
+/******/ 	// The require function
7
+/******/ 	function __webpack_require__(moduleId) {
8
+/******/
9
+/******/ 		// Check if module is in cache
10
+/******/ 		if(installedModules[moduleId]) {
11
+/******/ 			return installedModules[moduleId].exports;
12
+/******/ 		}
13
+/******/ 		// Create a new module (and put it into the cache)
14
+/******/ 		var module = installedModules[moduleId] = {
15
+/******/ 			i: moduleId,
16
+/******/ 			l: false,
17
+/******/ 			exports: {}
18
+/******/ 		};
19
+/******/
20
+/******/ 		// Execute the module function
21
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22
+/******/
23
+/******/ 		// Flag the module as loaded
24
+/******/ 		module.l = true;
25
+/******/
26
+/******/ 		// Return the exports of the module
27
+/******/ 		return module.exports;
28
+/******/ 	}
29
+/******/
30
+/******/
31
+/******/ 	// expose the modules object (__webpack_modules__)
32
+/******/ 	__webpack_require__.m = modules;
33
+/******/
34
+/******/ 	// expose the module cache
35
+/******/ 	__webpack_require__.c = installedModules;
36
+/******/
37
+/******/ 	// define getter function for harmony exports
38
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
39
+/******/ 		if(!__webpack_require__.o(exports, name)) {
40
+/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
41
+/******/ 		}
42
+/******/ 	};
43
+/******/
44
+/******/ 	// define __esModule on exports
45
+/******/ 	__webpack_require__.r = function(exports) {
46
+/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
47
+/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
48
+/******/ 		}
49
+/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
50
+/******/ 	};
51
+/******/
52
+/******/ 	// create a fake namespace object
53
+/******/ 	// mode & 1: value is a module id, require it
54
+/******/ 	// mode & 2: merge all properties of value into the ns
55
+/******/ 	// mode & 4: return value when already ns object
56
+/******/ 	// mode & 8|1: behave like require
57
+/******/ 	__webpack_require__.t = function(value, mode) {
58
+/******/ 		if(mode & 1) value = __webpack_require__(value);
59
+/******/ 		if(mode & 8) return value;
60
+/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
61
+/******/ 		var ns = Object.create(null);
62
+/******/ 		__webpack_require__.r(ns);
63
+/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
64
+/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
65
+/******/ 		return ns;
66
+/******/ 	};
67
+/******/
68
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
69
+/******/ 	__webpack_require__.n = function(module) {
70
+/******/ 		var getter = module && module.__esModule ?
71
+/******/ 			function getDefault() { return module['default']; } :
72
+/******/ 			function getModuleExports() { return module; };
73
+/******/ 		__webpack_require__.d(getter, 'a', getter);
74
+/******/ 		return getter;
75
+/******/ 	};
76
+/******/
77
+/******/ 	// Object.prototype.hasOwnProperty.call
78
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
79
+/******/
80
+/******/ 	// __webpack_public_path__
81
+/******/ 	__webpack_require__.p = "";
82
+/******/
83
+/******/
84
+/******/ 	// Load entry module and return exports
85
+/******/ 	return __webpack_require__(__webpack_require__.s = 6);
86
+/******/ })
87
+/************************************************************************/
88
+/******/ ({
89
+
90
+/***/ 0:
91
+/***/ (function(module, exports, __webpack_require__) {
92
+
93
+"use strict";
94
+
95
+
96
+var isIPhone = false;
97
+var deviceWidth = void 0;
98
+var deviceDPR = void 0;
99
+var BASE_DEVICE_WIDTH = 750;
100
+var checkDeviceWidth = function checkDeviceWidth() {
101
+  var info = wx.getSystemInfoSync();
102
+  // console.log('info', info)
103
+  isIPhone = info.platform === 'ios';
104
+  var newDeviceWidth = info.screenWidth || 375;
105
+  var newDeviceDPR = info.pixelRatio || 2;
106
+
107
+  if (!isIPhone) {
108
+    // HACK switch width and height when landscape
109
+    // const newDeviceHeight = info.screenHeight || 375
110
+    // 暂时不处理转屏的情况
111
+  }
112
+
113
+  if (newDeviceWidth !== deviceWidth || newDeviceDPR !== deviceDPR) {
114
+    deviceWidth = newDeviceWidth;
115
+    deviceDPR = newDeviceDPR;
116
+    // console.info('Updated device width: ' + newDeviceWidth + 'px DPR ' + newDeviceDPR)
117
+  }
118
+};
119
+checkDeviceWidth();
120
+
121
+var eps = 1e-4;
122
+var transformByDPR = function transformByDPR(number) {
123
+  if (number === 0) {
124
+    return 0;
125
+  }
126
+  number = number / BASE_DEVICE_WIDTH * deviceWidth;
127
+  number = Math.floor(number + eps);
128
+  if (number === 0) {
129
+    if (deviceDPR === 1 || !isIPhone) {
130
+      return 1;
131
+    }
132
+    return 0.5;
133
+  }
134
+  return number;
135
+};
136
+
137
+var rpxRE = /([+-]?\d+(?:\.\d+)?)rpx/gi;
138
+// const inlineRpxRE = /(?::|\s|\(|\/)([+-]?\d+(?:\.\d+)?)rpx/g
139
+
140
+var transformRpx = function transformRpx(style, inline) {
141
+  if (typeof style !== 'string') {
142
+    return style;
143
+  }
144
+  var re = rpxRE;
145
+  return style.replace(re, function (match, num) {
146
+    return transformByDPR(Number(num)) + (inline ? 'px' : '');
147
+  });
148
+};
149
+
150
+module.exports = {
151
+  transformRpx: transformRpx
152
+};
153
+
154
+/***/ }),
155
+
156
+/***/ 6:
157
+/***/ (function(module, exports, __webpack_require__) {
158
+
159
+"use strict";
160
+
161
+
162
+/* eslint complexity: ["error", {"max": 50}] */
163
+/* eslint-disable indent */
164
+var DEFAULT_SHOW_SCREENS = 4;
165
+var RECT_SIZE = 200;
166
+var systemInfo = wx.getSystemInfoSync();
167
+var DEBUG = false;
168
+var transformRpx = __webpack_require__(0).transformRpx;
169
+
170
+Component({
171
+  options: {
172
+    multipleSlots: true // 在组件定义时的选项中启用多slot支持
173
+  },
174
+  relations: {
175
+    '../recycle-item/recycle-item': {
176
+      type: 'child', // 关联的目标节点应为子节点
177
+      linked: function linked(target) {
178
+        // 检查第一个的尺寸就好了吧
179
+        if (!this._hasCheckSize) {
180
+          this._hasCheckSize = true;
181
+          var size = this.boundingClientRect(this._pos.beginIndex);
182
+          if (!size) {
183
+            return;
184
+          }
185
+          setTimeout(function () {
186
+            try {
187
+              target.createSelectorQuery().select('.wx-recycle-item').boundingClientRect(function (rect) {
188
+                if (rect && (rect.width !== size.width || rect.height !== size.height)) {
189
+                  // eslint-disable-next-line no-console
190
+                  console.warn('[recycle-view] the size in <recycle-item> is not the same with param ' + ('itemSize, expect {width: ' + rect.width + ', height: ' + rect.height + '} but got ') + ('{width: ' + size.width + ', height: ' + size.height + '}'));
191
+                }
192
+              }).exec();
193
+            } catch (e) {
194
+              // do nothing
195
+            }
196
+          }, 10);
197
+        }
198
+      }
199
+    }
200
+  },
201
+  /**
202
+   * 组件的属性列表
203
+   */
204
+  properties: {
205
+    debug: {
206
+      type: Boolean,
207
+      value: false
208
+    },
209
+    scrollY: {
210
+      type: Boolean,
211
+      value: true
212
+    },
213
+    batch: {
214
+      type: Boolean,
215
+      value: false,
216
+      public: true,
217
+      observer: '_recycleInnerBatchDataChanged'
218
+    },
219
+    batchKey: {
220
+      type: String,
221
+      value: 'batchSetRecycleData',
222
+      public: true
223
+    },
224
+    scrollTop: {
225
+      type: Number,
226
+      value: 0,
227
+      public: true,
228
+      observer: '_scrollTopChanged',
229
+      observeAssignments: true
230
+    },
231
+    height: {
232
+      type: Number,
233
+      value: systemInfo.windowHeight,
234
+      public: true,
235
+      observer: '_heightChanged'
236
+    },
237
+    width: {
238
+      type: Number,
239
+      value: systemInfo.windowWidth,
240
+      public: true,
241
+      observer: '_widthChanged'
242
+    },
243
+    // 距顶部/左边多远时,触发bindscrolltoupper
244
+    upperThreshold: {
245
+      type: Number,
246
+      value: 50,
247
+      public: true
248
+    },
249
+    // 距底部/右边多远时,触发bindscrolltolower
250
+    lowerThreshold: {
251
+      type: Number,
252
+      value: 50,
253
+      public: true
254
+    },
255
+    scrollToIndex: {
256
+      type: Number,
257
+      public: true,
258
+      value: 0,
259
+      observer: '_scrollToIndexChanged',
260
+      observeAssignments: true
261
+    },
262
+    scrollWithAnimation: {
263
+      type: Boolean,
264
+      public: true,
265
+      value: false
266
+    },
267
+    enableBackToTop: {
268
+      type: Boolean,
269
+      public: true,
270
+      value: false
271
+    },
272
+    // 是否节流,默认是
273
+    throttle: {
274
+      type: Boolean,
275
+      public: true,
276
+      value: true
277
+    },
278
+    placeholderImage: {
279
+      type: String,
280
+      public: true,
281
+      value: ''
282
+    },
283
+    screen: { // 默认渲染多少屏的数据
284
+      type: Number,
285
+      public: true,
286
+      value: DEFAULT_SHOW_SCREENS
287
+    }
288
+  },
289
+
290
+  /**
291
+   * 组件的初始数据
292
+   */
293
+  data: {
294
+    innerBeforeHeight: 0,
295
+    innerAfterHeight: 0,
296
+    innerScrollTop: 0,
297
+    innerScrollIntoView: '',
298
+    placeholderImageStr: '',
299
+    totalHeight: 0,
300
+    useInPage: false
301
+  },
302
+  attached: function attached() {
303
+    if (this.data.placeholderImage) {
304
+      this.setData({
305
+        placeholderImageStr: transformRpx(this.data.placeholderImage, true)
306
+      });
307
+    }
308
+    this.setItemSize({
309
+      array: [],
310
+      map: {},
311
+      totalHeight: 0
312
+    });
313
+  },
314
+  ready: function ready() {
315
+    var _this = this;
316
+
317
+    this._initPosition(function () {
318
+      _this._isReady = true; // DOM结构ready了
319
+      // 有一个更新的timer在了
320
+      if (_this._updateTimerId) return;
321
+
322
+      _this._scrollViewDidScroll({
323
+        detail: {
324
+          scrollLeft: _this._pos.left,
325
+          scrollTop: _this._pos.top,
326
+          ignoreScroll: true
327
+        }
328
+      }, true);
329
+    });
330
+  },
331
+  detached: function detached() {
332
+    this.page = null;
333
+    // 销毁对应的RecycleContext
334
+    if (this.context) {
335
+      this.context.destroy();
336
+      this.context = null;
337
+    }
338
+  },
339
+
340
+  /**
341
+   * 组件的方法列表
342
+   */
343
+  methods: {
344
+    _log: function _log() {
345
+      var _console;
346
+
347
+      if (!DEBUG && !this.data.debug) return;
348
+      var h = new Date();
349
+      var str = h.getHours() + ':' + h.getMinutes() + ':' + h.getSeconds() + '.' + h.getMilliseconds();
350
+
351
+      for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
352
+        args[_key] = arguments[_key];
353
+      }
354
+
355
+      Array.prototype.splice.call(args, 0, 0, str);
356
+      // eslint-disable-next-line no-console
357
+      (_console = console).log.apply(_console, args);
358
+    },
359
+    _scrollToUpper: function _scrollToUpper(e) {
360
+      this.triggerEvent('scrolltoupper', e.detail);
361
+    },
362
+    _scrollToLower: function _scrollToLower(e) {
363
+      this.triggerEvent('scrolltolower', e.detail);
364
+    },
365
+    _beginToScroll: function _beginToScroll() {
366
+      if (!this._lastScrollTop) {
367
+        this._lastScrollTop = this._pos && (this._pos.top || 0);
368
+      }
369
+    },
370
+    _clearList: function _clearList(cb) {
371
+      this.currentScrollTop = 0;
372
+      this._lastScrollTop = 0;
373
+      var pos = this._pos;
374
+      pos.beginIndex = this._pos.endIndex = -1;
375
+      pos.afterHeight = pos.minTop = pos.maxTop = 0;
376
+      this.page._recycleViewportChange({
377
+        detail: {
378
+          data: pos,
379
+          id: this.id
380
+        }
381
+      }, cb);
382
+    },
383
+
384
+    // 判断RecycleContext是否Ready
385
+    _isValid: function _isValid() {
386
+      return this.page && this.context && this.context.isDataReady;
387
+    },
388
+
389
+    // eslint-disable-next-line no-complexity
390
+    _scrollViewDidScroll: function _scrollViewDidScroll(e, force) {
391
+      // 如果RecycleContext还没有初始化, 不做任何事情
392
+      if (!this._isValid()) {
393
+        return;
394
+      }
395
+      // 监测白屏时间
396
+      if (!e.detail.ignoreScroll) {
397
+        this.triggerEvent('scroll', e.detail);
398
+      }
399
+      this.currentScrollTop = e.detail.scrollTop;
400
+      // 高度为0的情况, 不做任何渲染逻辑
401
+      if (!this._pos.height || !this.sizeArray.length) {
402
+        // 没有任何数据的情况下, 直接清理所有的状态
403
+        this._clearList(e.detail.cb);
404
+        return;
405
+      }
406
+
407
+      // 在scrollWithAnimation动画最后会触发一次scroll事件, 这次scroll事件必须要被忽略
408
+      if (this._isScrollingWithAnimation) {
409
+        this._isScrollingWithAnimation = false;
410
+        return;
411
+      }
412
+      var pos = this._pos;
413
+      var that = this;
414
+      var scrollLeft = e.detail.scrollLeft;
415
+      var scrollTop = e.detail.scrollTop;
416
+      var scrollDistance = Math.abs(scrollTop - this._lastScrollTop);
417
+      if (!force && Math.abs(scrollTop - pos.top) < pos.height * 1.5) {
418
+        this._log('【not exceed height');
419
+        return;
420
+      }
421
+      this._lastScrollTop = scrollTop;
422
+      var SHOW_SCREENS = this.data.screen; // 固定4屏幕
423
+      this._log('SHOW_SCREENS', SHOW_SCREENS, scrollTop);
424
+      this._calcViewportIndexes(scrollLeft, scrollTop, function (beginIndex, endIndex, minTop, afterHeight, maxTop) {
425
+        that._log('scrollDistance', scrollDistance, 'indexes', beginIndex, endIndex);
426
+        // 渲染的数据不变
427
+        if (!force && pos.beginIndex === beginIndex && pos.endIndex === endIndex && pos.minTop === minTop && pos.afterHeight === afterHeight) {
428
+          that._log('------------is the same beginIndex and endIndex');
429
+          return;
430
+        }
431
+        // 如果这次渲染的范围比上一次的范围小,则忽略
432
+        that._log('【check】before setData, old pos is', pos.minTop, pos.maxTop, minTop, maxTop);
433
+        that._throttle = false;
434
+        pos.left = scrollLeft;
435
+        pos.top = scrollTop;
436
+        pos.beginIndex = beginIndex;
437
+        pos.endIndex = endIndex;
438
+        // console.log('render indexes', endIndex - beginIndex + 1, endIndex, beginIndex)
439
+        pos.minTop = minTop;
440
+        pos.maxTop = maxTop;
441
+        pos.afterHeight = afterHeight;
442
+        pos.ignoreBeginIndex = pos.ignoreEndIndex = -1;
443
+        that.page._recycleViewportChange({
444
+          detail: {
445
+            data: that._pos,
446
+            id: that.id
447
+          }
448
+        }, function () {
449
+          if (e.detail.cb) {
450
+            e.detail.cb();
451
+          }
452
+        });
453
+      });
454
+    },
455
+
456
+    // 计算在视窗内渲染的数据
457
+    _calcViewportIndexes: function _calcViewportIndexes(left, top, cb) {
458
+      var that = this;
459
+      // const st = +new Date
460
+      this._getBeforeSlotHeight(function () {
461
+        var _that$__calcViewportI = that.__calcViewportIndexes(left, top),
462
+            beginIndex = _that$__calcViewportI.beginIndex,
463
+            endIndex = _that$__calcViewportI.endIndex,
464
+            minTop = _that$__calcViewportI.minTop,
465
+            afterHeight = _that$__calcViewportI.afterHeight,
466
+            maxTop = _that$__calcViewportI.maxTop;
467
+
468
+        if (cb) {
469
+          cb(beginIndex, endIndex, minTop, afterHeight, maxTop);
470
+        }
471
+      });
472
+    },
473
+    _getBeforeSlotHeight: function _getBeforeSlotHeight(cb) {
474
+      if (typeof this.data.beforeSlotHeight !== 'undefined') {
475
+        if (cb) {
476
+          cb(this.data.beforeSlotHeight);
477
+        }
478
+      } else {
479
+        this.reRender(cb);
480
+      }
481
+    },
482
+    _getAfterSlotHeight: function _getAfterSlotHeight(cb) {
483
+      if (typeof this.data.afterSlotHeight !== 'undefined') {
484
+        if (cb) {
485
+          cb(this.data.afterSlotHeight);
486
+        }
487
+        // cb && cb(this.data.afterSlotHeight)
488
+      } else {
489
+        this.reRender(cb);
490
+      }
491
+    },
492
+    _getIndexes: function _getIndexes(minTop, maxTop) {
493
+      if (minTop === maxTop && maxTop === 0) {
494
+        return {
495
+          beginIndex: -1,
496
+          endIndex: -1
497
+        };
498
+      }
499
+      var startLine = Math.floor(minTop / RECT_SIZE);
500
+      var endLine = Math.ceil(maxTop / RECT_SIZE);
501
+      var rectEachLine = Math.floor(this.data.width / RECT_SIZE);
502
+      var beginIndex = void 0;
503
+      var endIndex = void 0;
504
+      var sizeMap = this.sizeMap;
505
+      for (var i = startLine; i <= endLine; i++) {
506
+        for (var col = 0; col < rectEachLine; col++) {
507
+          var key = i + '.' + col;
508
+          // 找到sizeMap里面的最小值和最大值即可
509
+          if (!sizeMap[key]) continue;
510
+          for (var j = 0; j < sizeMap[key].length; j++) {
511
+            if (typeof beginIndex === 'undefined') {
512
+              beginIndex = endIndex = sizeMap[key][j];
513
+              continue;
514
+            }
515
+            if (beginIndex > sizeMap[key][j]) {
516
+              beginIndex = sizeMap[key][j];
517
+            } else if (endIndex < sizeMap[key][j]) {
518
+              endIndex = sizeMap[key][j];
519
+            }
520
+          }
521
+        }
522
+      }
523
+      return {
524
+        beginIndex: beginIndex,
525
+        endIndex: endIndex
526
+      };
527
+    },
528
+    _isIndexValid: function _isIndexValid(beginIndex, endIndex) {
529
+      if (typeof beginIndex === 'undefined' || beginIndex === -1 || typeof endIndex === 'undefined' || endIndex === -1 || endIndex >= this.sizeArray.length) {
530
+        return false;
531
+      }
532
+      return true;
533
+    },
534
+    __calcViewportIndexes: function __calcViewportIndexes(left, top) {
535
+      if (!this.sizeArray.length) return {};
536
+      var pos = this._pos;
537
+      if (typeof left === 'undefined') {
538
+        left = pos.left;
539
+      }
540
+      if (typeof top === 'undefined') {
541
+        top = pos.top;
542
+      }
543
+      // top = Math.max(top, this.data.beforeSlotHeight)
544
+      var beforeSlotHeight = this.data.beforeSlotHeight || 0;
545
+      // 和direction无关了
546
+      var SHOW_SCREENS = this.data.screen;
547
+      var minTop = top - pos.height * SHOW_SCREENS - beforeSlotHeight;
548
+      var maxTop = top + pos.height * SHOW_SCREENS - beforeSlotHeight;
549
+      // maxTop或者是minTop超出了范围
550
+      if (maxTop > this.totalHeight) {
551
+        minTop -= maxTop - this.totalHeight;
552
+        maxTop = this.totalHeight;
553
+      }
554
+      if (minTop < beforeSlotHeight) {
555
+        maxTop += Math.min(beforeSlotHeight - minTop, this.totalHeight);
556
+        minTop = 0;
557
+      }
558
+      // 计算落在minTop和maxTop之间的方格有哪些
559
+      var indexObj = this._getIndexes(minTop, maxTop);
560
+      var beginIndex = indexObj.beginIndex;
561
+      var endIndex = indexObj.endIndex;
562
+      if (endIndex >= this.sizeArray.length) {
563
+        endIndex = this.sizeArray.length - 1;
564
+      }
565
+      // 校验一下beginIndex和endIndex的有效性,
566
+      if (!this._isIndexValid(beginIndex, endIndex)) {
567
+        return {
568
+          beginIndex: -1,
569
+          endIndex: -1,
570
+          minTop: 0,
571
+          afterHeight: 0,
572
+          maxTop: 0
573
+        };
574
+      }
575
+      // 计算白屏的默认占位的区域
576
+      var maxTopFull = this.sizeArray[endIndex].beforeHeight + this.sizeArray[endIndex].height;
577
+      var minTopFull = this.sizeArray[beginIndex].beforeHeight;
578
+
579
+      // console.log('render indexes', beginIndex, endIndex)
580
+      var afterHeight = this.totalHeight - maxTopFull;
581
+      return {
582
+        beginIndex: beginIndex,
583
+        endIndex: endIndex,
584
+        minTop: minTopFull, // 取整, beforeHeight的距离
585
+        afterHeight: afterHeight,
586
+        maxTop: maxTop
587
+      };
588
+    },
589
+    setItemSize: function setItemSize(size) {
590
+      this.sizeArray = size.array;
591
+      this.sizeMap = size.map;
592
+      if (size.totalHeight !== this.totalHeight) {
593
+        // console.log('---totalHeight is', size.totalHeight);
594
+        this.setData({
595
+          totalHeight: size.totalHeight,
596
+          useInPage: this.useInPage || false
597
+        });
598
+      }
599
+      this.totalHeight = size.totalHeight;
600
+    },
601
+    setList: function setList(key, newList) {
602
+      this._currentSetDataKey = key;
603
+      this._currentSetDataList = newList;
604
+    },
605
+    setPage: function setPage(page) {
606
+      this.page = page;
607
+    },
608
+    forceUpdate: function forceUpdate(cb, reInit) {
609
+      var _this2 = this;
610
+
611
+      if (!this._isReady) {
612
+        if (this._updateTimerId) {
613
+          // 合并多次的forceUpdate
614
+          clearTimeout(this._updateTimerId);
615
+        }
616
+        this._updateTimerId = setTimeout(function () {
617
+          _this2.forceUpdate(cb, reInit);
618
+        }, 10);
619
+        return;
620
+      }
621
+      this._updateTimerId = null;
622
+      var that = this;
623
+      if (reInit) {
624
+        this.reRender(function () {
625
+          that._scrollViewDidScroll({
626
+            detail: {
627
+              scrollLeft: that._pos.left,
628
+              scrollTop: that.currentScrollTop || that.data.scrollTop || 0,
629
+              ignoreScroll: true,
630
+              cb: cb
631
+            }
632
+          }, true);
633
+        });
634
+      } else {
635
+        this._scrollViewDidScroll({
636
+          detail: {
637
+            scrollLeft: that._pos.left,
638
+            scrollTop: that.currentScrollTop || that.data.scrollTop || 0,
639
+            ignoreScroll: true,
640
+            cb: cb
641
+          }
642
+        }, true);
643
+      }
644
+    },
645
+    _initPosition: function _initPosition(cb) {
646
+      var that = this;
647
+      that._pos = {
648
+        left: that.data.scrollLeft || 0,
649
+        top: that.data.scrollTop || 0,
650
+        width: this.data.width,
651
+        height: Math.max(500, this.data.height), // 一个屏幕的高度
652
+        direction: 0
653
+      };
654
+      this.reRender(cb);
655
+    },
656
+    _widthChanged: function _widthChanged(newVal) {
657
+      if (!this._isReady) return newVal;
658
+      this._pos.width = newVal;
659
+      this.forceUpdate();
660
+      return newVal;
661
+    },
662
+    _heightChanged: function _heightChanged(newVal) {
663
+      if (!this._isReady) return newVal;
664
+      this._pos.height = Math.max(500, newVal);
665
+      this.forceUpdate();
666
+      return newVal;
667
+    },
668
+    reRender: function reRender(cb) {
669
+      var _this3 = this;
670
+
671
+      var beforeSlotHeight = void 0;
672
+      var afterSlotHeight = void 0;
673
+      var that = this;
674
+      // const reRenderStart = Date.now()
675
+      function newCb() {
676
+        if (that._lastBeforeSlotHeight !== beforeSlotHeight || that._lastAfterSlotHeight !== afterSlotHeight) {
677
+          that.setData({
678
+            hasBeforeSlotHeight: true,
679
+            hasAfterSlotHeight: true,
680
+            beforeSlotHeight: beforeSlotHeight,
681
+            afterSlotHeight: afterSlotHeight
682
+          });
683
+        }
684
+        that._lastBeforeSlotHeight = beforeSlotHeight;
685
+        that._lastAfterSlotHeight = afterSlotHeight;
686
+        // console.log('_getBeforeSlotHeight use time', Date.now() - reRenderStart)
687
+        if (cb) {
688
+          cb();
689
+        }
690
+      }
691
+      // 重新渲染事件发生
692
+      var beforeReady = false;
693
+      var afterReady = false;
694
+      // fix:#16 确保获取slot节点实际高度
695
+      this.setData({
696
+        hasBeforeSlotHeight: false,
697
+        hasAfterSlotHeight: false
698
+      }, function () {
699
+        _this3.createSelectorQuery().select('.slot-before').boundingClientRect(function (rect) {
700
+          beforeSlotHeight = rect.height;
701
+          beforeReady = true;
702
+          if (afterReady) {
703
+            if (newCb) {
704
+              newCb();
705
+            }
706
+          }
707
+        }).exec();
708
+        _this3.createSelectorQuery().select('.slot-after').boundingClientRect(function (rect) {
709
+          afterSlotHeight = rect.height;
710
+          afterReady = true;
711
+          if (beforeReady) {
712
+            if (newCb) {
713
+              newCb();
714
+            }
715
+          }
716
+        }).exec();
717
+      });
718
+    },
719
+    _setInnerBeforeAndAfterHeight: function _setInnerBeforeAndAfterHeight(obj) {
720
+      if (typeof obj.beforeHeight !== 'undefined') {
721
+        this._tmpBeforeHeight = obj.beforeHeight;
722
+      }
723
+      if (obj.afterHeight) {
724
+        this._tmpAfterHeight = obj.afterHeight;
725
+      }
726
+    },
727
+    _recycleInnerBatchDataChanged: function _recycleInnerBatchDataChanged(cb) {
728
+      var _this4 = this;
729
+
730
+      if (typeof this._tmpBeforeHeight !== 'undefined') {
731
+        var setObj = {
732
+          innerBeforeHeight: this._tmpBeforeHeight || 0,
733
+          innerAfterHeight: this._tmpAfterHeight || 0
734
+        };
735
+        if (typeof this._tmpInnerScrollTop !== 'undefined') {
736
+          setObj.innerScrollTop = this._tmpInnerScrollTop;
737
+        }
738
+        var pageObj = {};
739
+        var hasPageData = false;
740
+        if (typeof this._currentSetDataKey !== 'undefined') {
741
+          pageObj[this._currentSetDataKey] = this._currentSetDataList;
742
+          hasPageData = true;
743
+        }
744
+        var saveScrollWithAnimation = this.data.scrollWithAnimation;
745
+        var groupSetData = function groupSetData() {
746
+          // 如果有分页数据的话
747
+          if (hasPageData) {
748
+            _this4.page.setData(pageObj);
749
+          }
750
+          _this4.setData(setObj, function () {
751
+            _this4.setData({
752
+              scrollWithAnimation: saveScrollWithAnimation
753
+            });
754
+            if (typeof cb === 'function') {
755
+              cb();
756
+            }
757
+          });
758
+        };
759
+        groupSetData();
760
+        delete this._currentSetDataKey;
761
+        delete this._currentSetDataList;
762
+        this._tmpBeforeHeight = undefined;
763
+        this._tmpAfterHeight = undefined;
764
+        this._tmpInnerScrollTop = undefined;
765
+      }
766
+    },
767
+    _renderByScrollTop: function _renderByScrollTop(scrollTop) {
768
+      // 先setData把目标位置的数据补齐
769
+      this._scrollViewDidScroll({
770
+        detail: {
771
+          scrollLeft: this._pos.scrollLeft,
772
+          scrollTop: scrollTop,
773
+          ignoreScroll: true
774
+        }
775
+      }, true);
776
+      if (this.data.scrollWithAnimation) {
777
+        this._isScrollingWithAnimation = true;
778
+      }
779
+      this.setData({
780
+        innerScrollTop: scrollTop
781
+      });
782
+    },
783
+    _scrollTopChanged: function _scrollTopChanged(newVal, oldVal) {
784
+      var _this5 = this;
785
+
786
+      // if (newVal === oldVal && newVal === 0) return
787
+      if (!this._isInitScrollTop && newVal === 0) {
788
+        this._isInitScrollTop = true;
789
+        return newVal;
790
+      }
791
+      this.currentScrollTop = newVal;
792
+      if (!this._isReady) {
793
+        if (this._scrollTopTimerId) {
794
+          clearTimeout(this._scrollTopTimerId);
795
+        }
796
+        this._scrollTopTimerId = setTimeout(function () {
797
+          _this5._scrollTopChanged(newVal, oldVal);
798
+        }, 10);
799
+        return newVal;
800
+      }
801
+      this._isInitScrollTop = true;
802
+      this._scrollTopTimerId = null;
803
+      // this._lastScrollTop = oldVal
804
+      if (typeof this._lastScrollTop === 'undefined') {
805
+        this._lastScrollTop = this.data.scrollTop;
806
+      }
807
+      // 滑动距离小于一个屏幕的高度, 直接setData
808
+      if (Math.abs(newVal - this._lastScrollTop) < this._pos.height) {
809
+        this.setData({
810
+          innerScrollTop: newVal
811
+        });
812
+        return newVal;
813
+      }
814
+      if (!this._isScrollTopChanged) {
815
+        // 首次的值需要延后一点执行才能生效
816
+        setTimeout(function () {
817
+          _this5._isScrollTopChanged = true;
818
+          _this5._renderByScrollTop(newVal);
819
+        }, 10);
820
+      } else {
821
+        this._renderByScrollTop(newVal);
822
+      }
823
+      return newVal;
824
+    },
825
+    _scrollToIndexChanged: function _scrollToIndexChanged(newVal, oldVal) {
826
+      var _this6 = this;
827
+
828
+      // if (newVal === oldVal && newVal === 0) return
829
+      // 首次滚动到0的不执行
830
+      if (!this._isInitScrollToIndex && newVal === 0) {
831
+        this._isInitScrollToIndex = true;
832
+        return newVal;
833
+      }
834
+      if (!this._isReady) {
835
+        if (this._scrollToIndexTimerId) {
836
+          clearTimeout(this._scrollToIndexTimerId);
837
+        }
838
+        this._scrollToIndexTimerId = setTimeout(function () {
839
+          _this6._scrollToIndexChanged(newVal, oldVal);
840
+        }, 10);
841
+        return newVal;
842
+      }
843
+      this._isInitScrollToIndex = true;
844
+      this._scrollToIndexTimerId = null;
845
+      if (typeof this._lastScrollTop === 'undefined') {
846
+        this._lastScrollTop = this.data.scrollTop;
847
+      }
848
+      var rect = this.boundingClientRect(newVal);
849
+      if (!rect) return newVal;
850
+      // console.log('rect top', rect, this.data.beforeSlotHeight)
851
+      var calScrollTop = rect.top + (this.data.beforeSlotHeight || 0);
852
+      this.currentScrollTop = calScrollTop;
853
+      if (Math.abs(calScrollTop - this._lastScrollTop) < this._pos.height) {
854
+        this.setData({
855
+          innerScrollTop: calScrollTop
856
+        });
857
+        return newVal;
858
+      }
859
+      if (!this._isScrollToIndexChanged) {
860
+        setTimeout(function () {
861
+          _this6._isScrollToIndexChanged = true;
862
+          _this6._renderByScrollTop(calScrollTop);
863
+        }, 10);
864
+      } else {
865
+        this._renderByScrollTop(calScrollTop);
866
+      }
867
+      return newVal;
868
+    },
869
+
870
+    // 提供给开发者使用的接口
871
+    boundingClientRect: function boundingClientRect(idx) {
872
+      if (idx < 0 || idx >= this.sizeArray.length) {
873
+        return null;
874
+      }
875
+      return {
876
+        left: 0,
877
+        top: this.sizeArray[idx].beforeHeight,
878
+        width: this.sizeArray[idx].width,
879
+        height: this.sizeArray[idx].height
880
+      };
881
+    },
882
+
883
+    // 获取当前出现在屏幕内数据项, 返回数据项组成的数组
884
+    // 参数inViewportPx表示当数据项至少有多少像素出现在屏幕内才算是出现在屏幕内,默认是1
885
+    getIndexesInViewport: function getIndexesInViewport(inViewportPx) {
886
+      if (!inViewportPx) {
887
+        inViewportPx = 1;
888
+      }
889
+      var scrollTop = this.currentScrollTop;
890
+      var minTop = scrollTop + inViewportPx;
891
+      if (minTop < 0) minTop = 0;
892
+      var maxTop = scrollTop + this.data.height - inViewportPx;
893
+      if (maxTop > this.totalHeight) maxTop = this.totalHeight;
894
+      var indexes = [];
895
+      for (var i = 0; i < this.sizeArray.length; i++) {
896
+        if (this.sizeArray[i].beforeHeight + this.sizeArray[i].height >= minTop && this.sizeArray[i].beforeHeight <= maxTop) {
897
+          indexes.push(i);
898
+        }
899
+        if (this.sizeArray[i].beforeHeight > maxTop) break;
900
+      }
901
+      return indexes;
902
+    },
903
+    getTotalHeight: function getTotalHeight() {
904
+      return this.totalHeight;
905
+    },
906
+    setUseInPage: function setUseInPage(useInPage) {
907
+      this.useInPage = useInPage;
908
+    },
909
+    setPlaceholderImage: function setPlaceholderImage(svgs, size) {
910
+      var fill = 'style=\'fill:rgb(204,204,204);\'';
911
+      var placeholderImages = ['data:image/svg+xml,%3Csvg height=\'' + size.height + '\' width=\'' + size.width + '\' xmlns=\'http://www.w3.org/2000/svg\'%3E'];
912
+      svgs.forEach(function (svg) {
913
+        placeholderImages.push('%3Crect width=\'' + svg.width + '\' x=\'' + svg.left + '\' height=\'' + svg.height + '\' y=\'' + svg.top + '\' ' + fill + ' /%3E');
914
+      });
915
+      placeholderImages.push('%3C/svg%3E');
916
+      this.setData({
917
+        placeholderImageStr: placeholderImages.join('')
918
+      });
919
+    }
920
+  }
921
+});
922
+
923
+/***/ })
924
+
925
+/******/ });

+ 4
- 0
src/compents/miniprogram-recycle-view/recycle-view.json Прегледај датотеку

@@ -0,0 +1,4 @@
1
+{
2
+  "component": true,
3
+  "usingComponents": {}
4
+}

+ 21
- 0
src/compents/miniprogram-recycle-view/recycle-view.wxml Прегледај датотеку

@@ -0,0 +1,21 @@
1
+<!--components/recycle-view/recycle-view.wxml-->
2
+<view bindtouchstart='_beginToScroll' style="height:{{useInPage ? totalHeight + (hasBeforeSlotHeight ? beforeSlotHeight : 0) + (hasAfterSlotHeight ? afterSlotHeight : 0) : height}}px;width:{{width}}px;transform:translateZ(0);-webkit-transform:translateZ(0);" id="content" class="wrap">
3
+  <scroll-view bindscroll="_scrollViewDidScroll" class="content" style='height:100%;position: relative;' scroll-y="{{useInPage ? false : scrollY}}" scroll-x="{{false}}" upper-threshold="{{upperThreshold}}" lower-threshold="{{lowerThreshold}}" scroll-top="{{innerScrollTop}}" scroll-into-view="{{innerScrollIntoView}}" scroll-with-animation="{{scrollWithAnimation}}" bindscrolltoupper="_scrollToUpper" bindscrolltolower="_scrollToLower" scroll-anchoring enable-back-to-top="{{enableBackToTop}}" throttle="{{throttle}}">
4
+    <view style="position: absolute;z-index:1;width:100%;left: 0;top: 0;opacity: 0;visibility: hidden;">
5
+      <slot name="itemsize"></slot>
6
+    </view>
7
+    <view style="height:{{hasBeforeSlotHeight ? beforeSlotHeight + 'px' : 'auto'}}" class="slot-before">
8
+      <slot name="before"></slot>
9
+    </view>
10
+    <view style='position:relative;width:100%;z-index:10;background: url("{{placeholderImageStr}}") repeat;height:{{totalHeight}}px;'>
11
+      <!-- <view class='before' style="height:{{innerBeforeHeight}}px"></view> -->
12
+      <view style="position: absolute;left:0;width:100%;top:{{innerBeforeHeight}}px;">
13
+        <slot></slot>
14
+      </view>
15
+      <!-- <view class='after' style="height:{{innerAfterHeight}}px"></view> -->
16
+    </view>
17
+    <view style="height:{{hasAfterSlotHeight ? afterSlotHeight + 'px' : 'auto'}}" class="slot-after">
18
+      <slot name="after"></slot>
19
+    </view>
20
+  </scroll-view>
21
+</view>

+ 5
- 0
src/compents/miniprogram-recycle-view/recycle-view.wxss Прегледај датотеку

@@ -0,0 +1,5 @@
1
+/* components/recycle-view/recycle-view.wxss */
2
+:host {
3
+  display: block;
4
+  width: 100%;
5
+}

+ 76
- 36
src/pages/house/list/index.jsx Прегледај датотеку

@@ -5,11 +5,17 @@ import { View, Text, Input, Image } from '@tarojs/components'
5 5
 import ContainerLayout from '../../../compents/container/index'
6 6
 import radio from '../../../assets/radio.png'
7 7
 import request from '@/util/request'
8
+import RecycleList from '@/compents/RecycleList'
9
+
10
+
8 11
 let house = (props, ref) => {
9 12
 
10 13
   const [list, setList] = useState([])
11 14
   const [radioHouse, setRadioHouse] = useState()
12
-
15
+  const [width, setWidth] = useState()
16
+  // const [height, setHeight] = useState()
17
+  const [queryParams, setQueryParams] = useState({pageNum: 1, pageSize: 10})
18
+  const [total, setTotal] = useState(0)
13 19
 
14 20
   useImperativeHandle(ref, () => ({
15 21
     // changeVal 就是暴露给父组件的方法
@@ -18,30 +24,33 @@ let house = (props, ref) => {
18 24
     }
19 25
   }));
20 26
 
21
-
22
-
23 27
   const radioHouseState = useMemo(() => props.radioHouseState, [props.radioHouseState])
28
+
24 29
   wx.setNavigationBarTitle({
25 30
     title: '民宿房源管理'
26 31
   })
27
-  console.log(props, radioHouseState)
28
-  useEffect(() => {
29
-    console.log(props, ref,'props, ref')
30
-    getHouseList()
31
-  }, [])
32
-  useDidShow(() => {
33 32
 
34
-    getHouseList()
35
-  })
36 33
 
37 34
   const getHouseList = () => {
38 35
     request({
39 36
       url: '/taHouse',
40
-      // method: 'get',
41
-      params: { pageSize: 1, pageSize: 9999 }
37
+      params: queryParams
42 38
     }).then((res) => {
43 39
       const { records, ...page } = res.data.data
44
-      setList(records)
40
+
41
+      if (page.current !== 1) {
42
+        setList(list.concat(records))
43
+      } else {
44
+        setList(records)
45
+      }
46
+      setTotal(page.total)
47
+    })
48
+  }
49
+
50
+  const handleScrollBottom = e => {
51
+    setQueryParams({
52
+      ...queryParams,
53
+      pageNum: queryParams.pageNum + 1
45 54
     })
46 55
   }
47 56
 
@@ -49,34 +58,65 @@ let house = (props, ref) => {
49 58
     Taro.navigateTo({ url: `/pages/share/index?houseId=${x.houseId}` })
50 59
   }
51 60
 
61
+  useEffect(() => {
62
+    console.log(props, ref,'props, ref')
63
+    getHouseList()
52 64
 
65
+    Taro.nextTick(() => {
53 66
 
54
-  return <View className='houselist'>
55
-    {list.map((x, index) => {
56
-      return <View className='houselist-view'>
57
-        {/* <Text className='houselist-view-title'>房源编号:{x.houseId}</Text> */}
58
-        <ContainerLayout className='houselist-view-card'>
59
-          {radioHouseState && <View className='houselist-view-card-radio' onClick={() => { props.onChange(x), setRadioHouse(x) }}>
60
-            {radioHouse?.houseId == x?.houseId && <Image src={radio} style={{ width: '30rpx', height: '20rpx' }}></Image>}
61
-          </View>}
62
-          <View className='top' onClick={() => { if (!radioHouseState) Taro.navigateTo({ url: `/pages/house/index?houseId=${x.houseId}` }) }}>{x.title}</View>
63
-          <View className='bottom'>
64
-            <Text onClick={() => { if (!radioHouseState) Taro.navigateTo({ url: `/pages/house/household/index?houseId=${x.houseId}` }) }}>查看入住人</Text>
65
-            <Text onClick={()=>onShare(x)}>分享链接</Text>
66
-          </View>
67
-        </ContainerLayout>
68
-
69
-      </View>
67
+      Taro.createSelectorQuery()
68
+        .selectAll('.houselist')
69
+        .boundingClientRect(res => {
70
+          if (res) {
71
+            setWidth(res[0].width)
72
+            // setHeight(res[0].height)
73
+          }
74
+        }).exec()
75
+    })
70 76
 
71
-    })}
77
+  }, [])
72 78
 
79
+  useEffect(() => {
80
+    getHouseList()
81
+  }, [queryParams])
73 82
 
74
-    {
75
-      list.length == 0 &&
76
-      < View className='nodata'> 暂无数据</View>
83
+  useDidShow(() => {
84
+    setQueryParams({ ...queryParams })
85
+  })
77 86
 
78
-    }
79
-  </View >
87
+  return (
88
+    <View className='houselist'>
89
+      <RecycleList
90
+        height={508}
91
+        width={width}
92
+        list={list}
93
+        total={total}
94
+        dataKey="houseId"
95
+        onScrollToLower={handleScrollBottom}
96
+        renderItem={x => (
97
+          <View className='houselist-view'>
98
+            {/* <Text className='houselist-view-title'>房源编号:{x.houseId}</Text> */}
99
+            <ContainerLayout className='houselist-view-card'>
100
+              {radioHouseState && <View className='houselist-view-card-radio' onClick={() => { props.onChange(x), setRadioHouse(x) }}>
101
+                {radioHouse?.houseId == x?.houseId && <Image src={radio} style={{ width: '30rpx', height: '20rpx' }}></Image>}
102
+              </View>}
103
+              <View className='top' onClick={() => { if (!radioHouseState) Taro.navigateTo({ url: `/pages/house/index?houseId=${x.houseId}` }) }}>{x.title}</View>
104
+              <View className='bottom'>
105
+                <Text onClick={() => { if (!radioHouseState) Taro.navigateTo({ url: `/pages/house/household/index?houseId=${x.houseId}` }) }}>查看入住人</Text>
106
+                <Text onClick={()=>onShare(x)}>分享链接</Text>
107
+              </View>
108
+            </ContainerLayout>
109
+          </View>
110
+        
111
+        )}
112
+      />
113
+
114
+      {
115
+        (!list || !list.length) && < View className='nodata'> 暂无数据</View>
116
+      }
117
+    </View>
118
+  )
119
+  
80 120
 }
81 121
 house = forwardRef(house)
82 122
 export default house

+ 2
- 1
src/pages/house/list/index.scss Прегледај датотеку

@@ -1,7 +1,8 @@
1 1
 .houselist {
2 2
 
3
-  // padding: 13.33px 0;
4 3
   &-view {
4
+    // app padding: 41.3px
5
+    width: calc(100vw - 82.6px);
5 6
     margin: 40px 0 0;
6 7
 
7 8
     // padding: 0;

+ 5
- 0
yarn.lock Прегледај датотеку

@@ -8751,6 +8751,11 @@ miniprogram-exparser@2.11.2:
8751 8751
   resolved "https://registry.npm.taobao.org/miniprogram-exparser/download/miniprogram-exparser-2.11.2.tgz#e60b4a1433c249025f7e9a922bfe369c7436ae61"
8752 8752
   integrity sha1-5gtKFDPCSQJffpqSK/42nHQ2rmE=
8753 8753
 
8754
+miniprogram-recycle-view@^0.1.5:
8755
+  version "0.1.5"
8756
+  resolved "https://registry.npm.taobao.org/miniprogram-recycle-view/download/miniprogram-recycle-view-0.1.5.tgz#854c2fe0df63c2165f739c8e0446aa35aa6c7d78"
8757
+  integrity sha1-hUwv4N9jwhZfc5yOBEaqNapsfXg=
8758
+
8754 8759
 miniprogram-simulate@^1.1.5:
8755 8760
   version "1.2.8"
8756 8761
   resolved "https://registry.npm.taobao.org/miniprogram-simulate/download/miniprogram-simulate-1.2.8.tgz#b40fb6fd3b0a43e97f292b2da28c9bb84ae4c5e1"