fineui是帆软报表和BI产品线所使用的前端框架。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

154 lines
4.6 KiB

import { VerticalLayout, Widget, shortcut, extend, isFunction, ResizeDetector } from "@/core";
/**
* 边滚动边加载的列表控件
*
* Created by GUY on 2017/5/23.
* @class ListView
* @extends Widget
*/
@shortcut()
export class ListView extends Widget {
props() {
return {
baseCls: "bi-list-view",
overscanHeight: 100,
blockSize: 10,
scrollTop: 0,
el: {},
items: [],
itemFormatter: (item, index) => item,
};
}
init() {
this.renderedIndex = -1;
this.cache = {};
}
static xtype = "bi.list_view";
render() {
const { el } = this.options;
return {
type: VerticalLayout.xtype,
items: [
extend(
{
type: VerticalLayout.xtype,
scrolly: false,
ref: _ref => {
this.container = _ref;
},
},
el
)
],
element: this,
};
}
// mounted之后绑定事件
mounted() {
const o = this.options;
// 这里无法进行结构,因为存在赋值操作,如果使用结构则this.options的值不会跟随变化
o.items = isFunction(o.items)
? this.__watch(o.items, (context, newValue) => {
this.populate(newValue);
})
: o.items;
this._populate();
this.element.scroll(e => {
o.scrollTop = this.element.scrollTop();
this._calculateBlocksToRender();
});
let lastWidth = this.element.width(),
lastHeight = this.element.height();
ResizeDetector.addResizeListener(this, () => {
if (!this.element.is(":visible")) {
return;
}
const width = this.element.width(),
height = this.element.height();
if (width !== lastWidth || height !== lastHeight) {
lastWidth = width;
lastHeight = height;
this._calculateBlocksToRender();
}
});
}
_renderMoreIf() {
const { scrollTop, overscanHeight, blockSize, items, itemFormatter } = this.options;
const height = this.element.height();
const minContentHeight = scrollTop + height + overscanHeight;
let index = (this.cache[this.renderedIndex] && this.cache[this.renderedIndex].index + blockSize) || 0;
let cnt = this.renderedIndex + 1;
let lastHeight;
const getElementHeight = () => this.container.element.height();
lastHeight = getElementHeight();
while (lastHeight < minContentHeight && index < items.length) {
const itemsArr = items.slice(index, index + blockSize);
this.container.addItems(
// eslint-disable-next-line no-loop-func
itemsArr.map((item, i) => itemFormatter(item, index + i)),
this
);
const addedHeight = getElementHeight() - lastHeight;
this.cache[cnt] = {
index,
scrollTop: lastHeight,
height: addedHeight,
};
this.renderedIndex = cnt;
cnt++;
index += blockSize;
lastHeight = getElementHeight();
}
}
_calculateBlocksToRender() {
// BI-115750 不可见状态下依赖元素实际尺寸构造的线段树会分段错误,所以不进行后续计算和线段树的初始化。
// 这样从不可见状态变为可见状态能够重新触发线段树初始化
if (!this.element.is(":visible")) {
return;
}
this._renderMoreIf();
}
_populate(items) {
const { scrollTop } = this.options;
if (items && this.options.items !== items) {
this.options.items = items;
}
this._calculateBlocksToRender();
this.element.scrollTop(scrollTop);
}
restore() {
this.renderedIndex = -1;
this.container.empty();
this.cache = {};
}
scrollTo(scrollTop) {
this.options.scrollTop = scrollTop;
this._calculateBlocksToRender();
this.element.scrollTop(scrollTop);
}
populate(items) {
if (items && this.options.items !== items) {
this.restore();
}
this._populate(items);
}
beforeDestroy() {
ResizeDetector.removeResizeListener(this);
this.restore();
}
}