bee-table
Version:
Table ui component for react
589 lines (513 loc) • 24.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports["default"] = bigDataX;
var _react = require("react");
var _react2 = _interopRequireDefault(_react);
var _propTypes = require("prop-types");
var _propTypes2 = _interopRequireDefault(_propTypes);
var _utils = require("./utils");
var _Table = require("../Table");
var _Table2 = _interopRequireDefault(_Table);
var _util = require("./util");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _defaults(obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; }
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : _defaults(subClass, superClass); }
var DEFAULT_ROW_HEIGHT = _Table2["default"].defaultProps.height || 30; //缺省的默认行高
// 确保索引不超出数据范围
function validIndex(totalCount, currentIndex) {
var cursorIndex = currentIndex || 0;
if (currentIndex < 0) cursorIndex = 0; //确保不小于0
if (currentIndex > totalCount - 1) cursorIndex = totalCount - 1; //确保不超出总数据行
return cursorIndex;
}
/**
* 获取数据开始和结束的索引位置
* @param totalCount 数据总行数
* @param showCount 可视区域显示的总行数
* @param hiddenCount 单个缓冲区隐藏的行数
* @param currentIndex 可视区域显示的首行索引(游标)
* @return {Array}
*/
function computeIndex(totalCount, showCount, hiddenCount, currentIndex) {
var screenCount = showCount + hiddenCount * 2; //渲染的总行数
var startIndex = 0,
endIndex = 0;
var cursorIndex = validIndex(totalCount, currentIndex);
if (totalCount <= screenCount) {
//总数不足或刚好一屏
startIndex = 0;
endIndex = totalCount > 1 ? totalCount - 1 : 1; //注意只有1行的场景
} else {
//总数超过一屏
if (cursorIndex == 0) {
//等于总数据顶边界
startIndex = 0;
endIndex = screenCount - 1;
} else if (cursorIndex == totalCount - 1) {
//等于总数据底边界
endIndex = totalCount - 1;
startIndex = endIndex - screenCount;
} else {
startIndex = cursorIndex - (hiddenCount - 1);
if (startIndex < 0) startIndex = 0; //注意上行有计算出为负值的情况
endIndex = startIndex + (screenCount - 1);
}
if (endIndex < 0) endIndex = 0; // 确保结束位置区间不越界
//如果结束位置超过总记录数,则把超过的部分在开始位置往前移动
if (endIndex > totalCount - 1) {
startIndex = startIndex - (endIndex - (totalCount - 1));
endIndex = totalCount - 1;
}
if (startIndex < 0) startIndex = 0; // 确保开始位置区间不越界
}
return { screenCount: screenCount, startIndex: startIndex, endIndex: endIndex };
}
//获取每行默认高度
function getDefaultRowHeight(nextProps) {
var rowHeight = nextProps.height ? nextProps.height : DEFAULT_ROW_HEIGHT;
return rowHeight;
}
//获取显示在可视区域的行数
function getShowCount(nextProps) {
var scrollY = nextProps.scroll && nextProps.scroll.y ? parseInt(nextProps.scroll.y) : 0; //默认可视区域高度
var rowHeight = getDefaultRowHeight(nextProps); //默认每行高度
var showCount = scrollY && rowHeight ? Math.floor(scrollY / rowHeight) : 20;
return showCount;
}
// 新重构版bigData
function bigDataX(Table) {
var _class, _temp, _initialiseProps;
return _temp = _class = function (_Component) {
_inherits(BigDataX, _Component);
function BigDataX(props) {
_classCallCheck(this, BigDataX);
var _this2 = _possibleConstructorReturn(this, _Component.call(this, props));
_initialiseProps.call(_this2);
_this2.state = {
needRender: false,
scrollTop: 0,
showCount: getShowCount(props),
treeType: _this2.checkIsTreeType(props.isTree, props.data)
};
_this2.currentIndex = 0;
_this2.cachedRowHeight = {}; //缓存每行的高度
var expandedRowKeys = [];
var rows = [].concat(_toConsumableArray(props.data));
if (props.defaultExpandAllRows) {
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
expandedRowKeys.push(_this2.getRowKey(row, i));
rows = rows.concat(row[props.childrenColumnName] || []);
}
} else {
expandedRowKeys = props.expandedRowKeys || props.defaultExpandedRowKeys;
}
_this2.expandedRowKeys = props.expandedRowKeys || expandedRowKeys || []; //缓存展开的行键值
_this2.flatTreeKeysMap = {}; //树表,扁平结构数据的 Map 映射,方便获取各节点信息
_this2.flatTreeData = []; //深度遍历处理后的data数组,拉平的数据,包含展开的子集行(未展开的不包含)
if (_this2.state.treeType) {
var deep = _this2.deepTraversal(props.data, null, _this2.expandedRowKeys);
_this2.flatTreeKeysMap = deep.flatTreeKeysMap;
_this2.flatTreeData = deep.flatTreeData;
}
return _this2;
}
BigDataX.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps, nextContext) {
var props = this.props;
var newExpandedKeys = nextProps.expandedRowKeys,
newData = nextProps.data;
var _this = this,
dataLen = newData.length;
var treeType = this.state.treeType;
// if ('expandedRowKeys' in nextProps){
// this.expandedRowKeys = newExpandedKeys;
// }
if ('defaultExpandAllRows' in nextProps) {
var defaultExpandAllRows = nextProps.defaultExpandAllRows,
data = nextProps.data,
expandedRowKeys = nextProps.expandedRowKeys,
_nextProps$childrenCo = nextProps.childrenColumnName,
childrenColumnName = _nextProps$childrenCo === undefined ? 'children' : _nextProps$childrenCo;
if (defaultExpandAllRows && !expandedRowKeys) {
var _expandedRowKeys = [];
var rows = [].concat(_toConsumableArray(data || []));
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
_expandedRowKeys.push(this.getRowKey(row, i));
rows = rows.concat(row[childrenColumnName] || []);
}
this.expandedRowKeys = _expandedRowKeys;
} else if (!defaultExpandAllRows && !expandedRowKeys) {
this.expandedRowKeys = [];
}
}
if ('expandedRowKeys' in nextProps) {
this.expandedRowKeys = newExpandedKeys;
}
if ('isTree' in nextProps || 'data' in nextProps) {
treeType = _this.checkIsTreeType(nextProps.isTree, nextProps.data);
this.setState({ treeType: treeType });
}
if ('height' in nextProps && nextProps.height !== props.height) {
// _this.cachedRowHeight = {}; //清除缓存每行的高度
_this.cachedRowHeight = _this.setCachedRowHeight(_this.cachedRowHeight);
}
// 可视滚动区域变化时
if ('scroll' in nextProps && nextProps.scroll.y !== props.scroll.y) {
// _this.cachedRowHeight = {}; //清除缓存每行的高度
_this.cachedRowHeight = _this.setCachedRowHeight(_this.cachedRowHeight);
if (!this.props.ignoreScrollYChange) {
//y不变化则无需重置currentIndex
_this.currentIndex = 0;
}
//显示在可视区域的行数
this.setState({ showCount: getShowCount(nextProps) });
}
if ('data' in nextProps) {
//fix: 滚动加载场景中,数据动态改变下占位计算错误的问题(26 Jun)
if (newData.toString() !== props.data.toString()) {
// _this.cachedRowHeight = {}; //清除缓存每行的高度
_this.cachedRowHeight = _this.setCachedRowHeight(_this.cachedRowHeight);
if (_this.state.scrollTop <= 0) {
// 增加scrollTop 判断,ncc场景下滚动条不在最上层,数据变更时 会出现空白,因为重置了currentIndex没有重置滚动条
_this.currentIndex = 0;
}
}
this.flatTreeKeysMap = {}; //树表,扁平结构数据的 Map 映射,方便获取各节点信息
this.flatTreeData = []; //深度遍历处理后的data数组
if (treeType) {
var deep = this.deepTraversal(newData, null, this.expandedRowKeys);
this.flatTreeKeysMap = deep.flatTreeKeysMap;
this.flatTreeData = deep.flatTreeData;
}
}
//为外部定位currentIndex提供支持,以确保currentIndex行显示在可视区域
if ('currentIndex' in nextProps) {
//如果传currentIndex,会判断该条数据是否在可视区域,如果没有的话,则重新计算startIndex和endIndex
if (nextProps.currentIndex !== -1) {
var totalCount = this.state.treeType ? this.flatTreeData.length : dataLen;
this.currentIndex = validIndex(totalCount, nextProps.currentIndex);
var newScrollTop = _this.getSumHeight(0, this.currentIndex, this.state.treeType); //重新设定scrollTop值
if (newScrollTop !== this.state.scrollTop) {
this.setState({ scrollTop: newScrollTop });
}
}
}
};
/**
* 深度遍历树形 data,把数据拍平,变为一维数组
* @param {*} treeData
* @param {*} parentKey 标识父节点
* @param {*} arrData 排平后的数组数据
* @param {*} keyMap key与数据的关系
*/
/**
* 将截取后的 List 数组转换为 Tree 结构
*/
//获取每行的唯一键
BigDataX.prototype.getRowKey = function getRowKey(record, index) {
var rowKey = this.props.rowKey;
var key = typeof rowKey === "function" ? rowKey(record, index) : record[rowKey];
return key;
};
/**
*判断是否是树形结构
*/
BigDataX.prototype.checkIsTreeType = function checkIsTreeType(isTree, data) {
if (typeof isTree == 'boolean') return isTree;
var tempData = data || [];
var hasChildren = tempData.some(function (item) {
return item.children !== undefined;
});
return hasChildren;
};
BigDataX.prototype.componentWillUnmount = function componentWillUnmount() {
this.cachedRowHeight = {}; //缓存每行的高度
this.expandedRowKeys = []; //缓存展开的行键值
this.flatTreeKeysMap = {}; //树表,扁平结构数据的 Map 映射,方便获取各节点信息
this.flatTreeData = []; //拉平的树表数据
};
//获取当前索引行的高度
//计算总行高
BigDataX.prototype.getSumHeight = function getSumHeight(start, end, isTreeType) {
var defaultRowHeight = getDefaultRowHeight(this.props);
var sumHeight = 0;
for (var index = start; index < end; index++) {
var currentRowHeight = this.getRowHeightByIndex(isTreeType, index, defaultRowHeight); //获取行高
sumHeight += currentRowHeight;
}
return sumHeight;
};
//表格尺寸变化时,重新计算滚动及显示位置
/**
*@description 根据返回的scrollTop计算当前的索引。此处做了两次缓存一个是根据上一次的currentIndex计算当前currentIndex。另一个是根据当前内容区的数据是否在缓存中如果在则不重新render页面
*@param 最新一次滚动的scrollTop
*@param treeType是否是树状表
*@param callback表体滚动过程中触发的回调
*/
//行拖拽-大数据表格下的实现
//行拖拽-大数据表格下的实现
BigDataX.prototype.render = function render() {
var _this3 = this;
var _props = this.props,
data = _props.data,
loadBuffer = _props.loadBuffer;
var _state = this.state,
scrollTop = _state.scrollTop,
showCount = _state.showCount,
treeType = _state.treeType;
var expandedRowKeys = this.props.expandedRowKeys ? this.props.expandedRowKeys : this.expandedRowKeys;
var totalCount = treeType ? this.flatTreeData.length : data.length;
var buffer = computeIndex(totalCount, showCount, loadBuffer, this.currentIndex); //计算出缓冲区各索引值
var lazyLoad = {
startIndex: buffer.startIndex,
endIndex: buffer.endIndex,
startParentIndex: buffer.startIndex //为树状节点做准备
};
lazyLoad.preHeight = this.getSumHeight(0, lazyLoad.startIndex, treeType);
lazyLoad.sufHeight = this.getSumHeight(lazyLoad.endIndex, totalCount - 1, treeType);
// console.log("AAA--->总数据:"+totalCount+"渲染总行数:"+buffer.screenCount+"缓冲区行数:"+hiddenCount+"可视区域行数:"+showCount);
// console.log("AAA--->startIndex:"+buffer.startIndex+"--->endIndex:"+buffer.endIndex+"--->currentIndex:"+this.currentIndex+"-->scrollTop:"+scrollTop);
var dataSource = []; // 存放截取需要显示的数据
if (treeType) {
var sliceTreeList = this.flatTreeData.slice(buffer.startIndex, buffer.endIndex + 1); //从拉平的数据中截取
dataSource = this.getTreeDataFromList(sliceTreeList); //将截取的list数据重新组织成tree结构
} else {
dataSource = data.slice(buffer.startIndex, buffer.endIndex + 1);
}
return _react2["default"].createElement(Table, _extends({}, this.props, {
data: dataSource,
lazyLoad: lazyLoad,
ref: function ref(el) {
return _this3.table = el;
},
handleScrollY: this.handleScrollY,
scrollTop: scrollTop,
setRowHeight: this.setRowHeight
// setRowParentIndex={this.setRowParentIndex}
, onResize: this.handleResize,
onExpand: this.onExpand,
onExpandedRowsChange: this.props.onExpandedRowsChange,
expandedRowKeys: expandedRowKeys,
onRowDragStart: this.__onRowDragStart,
onRowDragDrop: this.__onRowDragDrop
// className={'lazy-table'}
}));
};
return BigDataX;
}(_react.Component), _class.defaultProps = {
data: [],
loadBuffer: 50, //单个缓冲区大小,缓冲区有前后两个区,所以总缓冲区是x2
rowKey: "key",
onExpand: function onExpand() {},
scroll: {},
currentIndex: -1,
isTree: null,
height: null,
childrenColumnName: 'children'
}, _class.propTypes = {
loadBuffer: _propTypes2["default"].number,
height: _propTypes2["default"].number,
scroll: _propTypes2["default"].any,
expandedRowKeys: _propTypes2["default"].string,
rowKey: _propTypes2["default"].string,
currentIndex: _propTypes2["default"].number,
isTree: _propTypes2["default"].bool,
data: _propTypes2["default"].any,
onExpandedRowsChange: _propTypes2["default"].func,
childrenColumnName: _propTypes2["default"].string
}, _initialiseProps = function _initialiseProps() {
var _this4 = this;
this.setCachedRowHeight = function () {
var cachedRowHeight = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var target = {};
Object.keys(cachedRowHeight).forEach(function (key) {
// 如果是展开的行,也要缓存
if (typeof key === 'string' && (key || "").includes('_expanded')) {
target[key] = cachedRowHeight[key];
}
});
return target;
};
this.deepTraversal = function (treeData) {
var parentKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
var expandedKeys = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
var arrData = arguments[3];
var keyMap = arguments[4];
var flatTreeData = arrData || [],
flatTreeKeysMap = keyMap || {},
dataCopy = treeData;
if (Array.isArray(dataCopy)) {
for (var i = 0, l = dataCopy.length; i < l; i++) {
var _dataCopy$i = dataCopy[i],
children = _dataCopy$i.children,
props = _objectWithoutProperties(_dataCopy$i, ["children"]),
key = _this4.getRowKey(dataCopy[i], i),
_isLeaf = children && children.length > 0 ? false : true,
isExpanded = expandedKeys.includes(key);
var dataCopyI = _extends({
key: key,
isExpanded: isExpanded,
parentKey: parentKey,
_isLeaf: _isLeaf, //是否叶子节点
index: flatTreeData.length
}, props);
flatTreeData.push(dataCopyI); // 取每项数据放入一个新数组
flatTreeKeysMap[key] = dataCopyI;
// 优化递归逻辑,如果当前节点是收起状态,则不遍历其子节点
if (Array.isArray(children) && children.length > 0 && isExpanded) {
_this4.deepTraversal(children, key, expandedKeys, flatTreeData, flatTreeKeysMap);
}
}
}
return { flatTreeData: flatTreeData, flatTreeKeysMap: flatTreeKeysMap };
};
this.getTreeDataFromList = function (treeList) {
// 属性配置设置
var attr = { id: 'key', parendId: 'parentKey', rootId: null, _isLeaf: '_isLeaf' };
var treeData = (0, _utils.convertListToTree)(treeList, attr, _this4.flatTreeKeysMap);
return treeData;
};
this.getRowHeightByIndex = function (isTreeType, index, defaultRowHeight) {
var _props2 = _this4.props,
height = _props2.height,
data = _props2.data;
var currentRowHeight = height;
var records = isTreeType ? _this4.flatTreeData : data;
if (!records.length) return 0;
var record = records[index];
var key = _this4.getRowKey(record, index);
if (!height) {
//如果没有指定固定行高(即动态行高)
var cacheRowH = _this4.cachedRowHeight[key];
if (cacheRowH !== undefined) {
currentRowHeight = cacheRowH; //缓存中存在则使用缓存的行高
} else {
currentRowHeight = defaultRowHeight; //如果缓存行高也不存在则使用默认行高
}
}
currentRowHeight = currentRowHeight + (_this4.cachedRowHeight[key + "_expanded"] || 0);
return currentRowHeight;
};
this.handleResize = function () {
var _state2 = _this4.state,
scrollTop = _state2.scrollTop,
treeType = _state2.treeType;
_this4.handleScrollY(scrollTop, treeType);
};
this.handleScrollY = function (nextScrollTop, treeType, callback) {
var defaultRowHeight = getDefaultRowHeight(_this4.props);
var tempScrollTop = nextScrollTop;
var data = _this4.props.data;
var records = treeType ? _this4.flatTreeData : data;
var index = 0; //滚动后的位置索引
while (tempScrollTop > 0 && index < records.length) {
var currentRowHeight = _this4.getRowHeightByIndex(treeType, index, defaultRowHeight); //获取行高
tempScrollTop -= currentRowHeight;
if (tempScrollTop > -1) {
//确保scrollTop存在小数误差1px的情况也能正确取值
index += 1;
}
}
// console.log('AAA-->newIndex****',index);
//如果之前的索引和下一次的不一样则更新索引和滚动的位置
if (_this4.currentIndex !== index) {
_this4.currentIndex = index;
}
_this4.setState({ scrollTop: nextScrollTop });
callback && callback(nextScrollTop);
};
this.setRowHeight = function (height, index, rowKey, isExpandedRow) {
// this.cachedRowHeight[rowKey] = height;
if (isExpandedRow) {
// this.cachedRowHeight[rowKey] = this.cachedRowHeight[rowKey] + height;
_this4.cachedRowHeight[rowKey + "_expanded"] = height;
} else {
_this4.cachedRowHeight[rowKey] = height;
}
};
this.onExpand = function (expandState, record, index) {
var _this = _this4;
var _state3 = _this4.state,
treeType = _state3.treeType,
needRender = _state3.needRender;
var _props3 = _this4.props,
data = _props3.data,
onExpand = _props3.onExpand;
var rowKey = _this4.getRowKey(record, index);
//滚动加载expandedRowKeys自己维护,否则有展开不全的问题
if (!_this4.props.expandedRowKeys) {
if (expandState) {
//展开
_this4.expandedRowKeys.push(rowKey); //追加折叠行key
_this4.setState({ needRender: !needRender });
} else {
//折叠
_this4.expandedRowKeys = _this4.expandedRowKeys.filter(function (val) {
return val != rowKey;
}); //移除折叠行key
_this4.setState({ needRender: !needRender });
}
}
// expandState为true时,记录下
onExpand && onExpand(expandState, record, index);
if (treeType) {
//重新递归数据
var deep = _this4.deepTraversal(data, null, _this4.expandedRowKeys);
_this.flatTreeData = deep.flatTreeData;
_this.flatTreeKeysMap = deep.flatTreeKeysMap;
}
//展开/折叠由于行数据变化所以得清除缓存的行高
// this.cachedRowHeight = {};
if (!expandState) {
_this4.cachedRowHeight[rowKey + "_expanded"] = 0;
}
};
this.__onRowDragStart = function (options) {
var dragStartKey = options.dragStartKey;
var data = _this4.props.data,
currentIndex = void 0,
record = void 0;
data.forEach(function (da, i) {
// tr 的唯一标识通过 data.key 或 rowKey 两种方式传进来
var trKey = da.key ? da.key : _this4.getRowKey(da, i);
if (trKey == dragStartKey) {
currentIndex = i;
record = da;
}
});
_this4.props.onDragRowStart && _this4.props.onDragRowStart(record, currentIndex);
};
this.__onRowDragDrop = function (options) {
var dragTargetKey = options.dragTargetKey,
dragTargetIndex = options.dragTargetIndex,
dropTargetKey = options.dropTargetKey,
dropTargetIndex = options.dropTargetIndex;
var data = _this4.props.data,
record = void 0;
for (var i = 0; i < data.length; i++) {
var da = data[i];
// tr 的唯一标识通过 data.key 或 rowKey 两种方式传进来
var trKey = da.key ? da.key : _this4.getRowKey(da, i);
if (trKey == dragTargetKey) {
record = da;break; //匹配到后则退出减少性能开销
}
}
if (dragTargetIndex > -1) {
data = (0, _util.arrayMoveTo)(data, dragTargetIndex, dropTargetIndex);
_this4.props.onDropRow && _this4.props.onDropRow(data, record, dropTargetIndex);
_this4.setState({ needRender: !_this4.state.needRender });
} else {
_this4.props.onDropRow && _this4.props.onDropRow(data, record, dropTargetIndex);
}
};
}, _temp;
}
module.exports = exports["default"];