UNPKG

56.1 kBJavaScriptView Raw
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import classes from 'component-classes';
4import TableRow from './TableRow';
5import TableHeader from './TableHeader';
6import { measureScrollbar, debounce, warningOnce ,getMaxColChildrenLength} from './lib/utils';
7import shallowequal from 'shallowequal';
8import addEventListener from 'tinper-bee-core/lib/addEventListener';
9import ColumnManager from './ColumnManager';
10import createStore from './createStore';
11import Loading from 'bee-loading';
12import Icon from 'bee-icon';
13import { Event,EventUtil,closest} from "./lib/utils";
14import i18n from "./lib/i18n";
15import { getComponentLocale } from "bee-locale/build/tool";
16
17const propTypes = {
18 data: PropTypes.array,
19 expandIconAsCell: PropTypes.bool,
20 defaultExpandAllRows: PropTypes.bool,
21 expandedRowKeys: PropTypes.array,
22 defaultExpandedRowKeys: PropTypes.array,
23 useFixedHeader: PropTypes.bool,
24 columns: PropTypes.array,
25 clsPrefix: PropTypes.string,
26 bodyStyle: PropTypes.object,
27 style: PropTypes.object,
28 //特殊的渲染规则的key值
29 rowKey: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
30 rowClassName: PropTypes.func,
31 //column的主键,和 column.key 作用相同
32 columnKey: PropTypes.string,
33 expandedRowClassName: PropTypes.func,
34 childrenColumnName: PropTypes.string,
35 onExpand: PropTypes.func,
36 onRowHover:PropTypes.func,
37 onExpandedRowsChange: PropTypes.func,
38 indentSize: PropTypes.number,
39 onRowClick: PropTypes.func,
40 onRowDoubleClick: PropTypes.func,
41 expandIconColumnIndex: PropTypes.number,
42 //是否显示表头
43 showHeader: PropTypes.bool,
44 title: PropTypes.func,
45 footer: PropTypes.func,
46 emptyText: PropTypes.func,
47 scroll: PropTypes.object,
48 rowRef: PropTypes.func,
49 getBodyWrapper: PropTypes.func,
50 children: PropTypes.node,
51 draggable: PropTypes.bool,
52 minColumnWidth: PropTypes.number,
53 filterable: PropTypes.bool,
54 filterDelay: PropTypes.number,
55 onFilterChange: PropTypes.func,
56 onFilterClear: PropTypes.func,
57 syncHover: PropTypes.bool,
58 tabIndex:PropTypes.string,
59 hoverContent:PropTypes.func,
60 size: PropTypes.oneOf(['sm', 'md', 'lg']),
61 rowDraggAble: PropTypes.bool,
62 hideDragHandle: PropTypes.bool, // 隐藏行拖拽把手
63 onDropRow: PropTypes.func,
64 onDragRowStart: PropTypes.func,
65 onBodyScroll: PropTypes.func,
66 bodyDisplayInRow: PropTypes.bool, // 表格内容超出列宽度时进行换行 or 以...形式展现
67 headerDisplayInRow: PropTypes.bool, // 表头内容超出列宽度时进行换行 or 以...形式展现
68 showRowNum: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), // 表格是否自动生成序号,格式为{base:number || 0,defaultKey:string || '_index',defaultName:string || '序号'}
69 onPaste:PropTypes.func,
70};
71
72const defaultProps = {
73 data: [],
74 useFixedHeader: false,
75 expandIconAsCell: false,
76 defaultExpandAllRows: false,
77 defaultExpandedRowKeys: [],
78 columnKey: 'key',
79 rowKey: 'key',
80 rowClassName: () => '',
81 expandedRowClassName: () => '',
82 onExpand() { },
83 onExpandedRowsChange() { },
84 onRowClick() { },
85 // onRowDoubleClick() { },
86 clsPrefix: 'u-table',
87 bodyStyle: {},
88 style: {},
89 childrenColumnName: 'children',
90 indentSize: 15,
91 expandIconColumnIndex: 0,
92 showHeader: true,
93 scroll: {},
94 rowRef: () => null,
95 getBodyWrapper: body => body,
96 // emptyText: () => <div><Icon type="uf-nodata" className="table-nodata"></Icon><span>{locale["no_data"]}</span></div>,
97 columns:[],
98 minColumnWidth: 80,
99 locale:{},
100 syncHover: true,
101 // setRowHeight:()=>{},
102 setRowParentIndex:()=>{},
103 tabIndex:'0',
104 heightConsistent:false,
105 size: 'md',
106 rowDraggAble:false,
107 hideDragHandle:false,
108 onDropRow: ()=>{},
109 onDragRowStart: ()=>{},
110 onBodyScroll: ()=>{},
111 bodyDisplayInRow: true,
112 headerDisplayInRow: true,
113 showRowNum: false,
114 onPaste:()=>{}
115};
116
117const expandIconCellWidth = Number(43);
118
119class Table extends Component {
120 constructor(props) {
121 super(props);
122 let expandedRowKeys = [];
123 let rows = [...props.data];
124 const showDragHandle = !props.hideDragHandle && props.rowDraggAble;
125 this.columnManager = new ColumnManager(props.columns, props.children, props.originWidth, showDragHandle, props.showRowNum); // 加入props.showRowNum参数
126 this.store = createStore({ currentHoverKey: null });
127 this.firstDid = true;
128 if (props.defaultExpandAllRows) {
129 for (let i = 0; i < rows.length; i++) {
130 const row = rows[i];
131 expandedRowKeys.push(this.getRowKey(row, i));
132 rows = rows.concat(row[props.childrenColumnName] || []);
133 }
134 } else {
135 expandedRowKeys = props.expandedRowKeys || props.defaultExpandedRowKeys;
136 }
137
138 this.state = {
139 expandedRowKeys,
140 data: props.data,
141 currentHoverKey: null,
142 scrollPosition: 'left',
143 fixedColumnsHeadRowsHeight: [],
144 fixedColumnsBodyRowsHeight: [],
145 fixedColumnsExpandedRowsHeight: {}, //扩展行的高度
146 }
147
148 this.onExpandedRowsChange = this.onExpandedRowsChange.bind(this);
149 this.onExpanded = this.onExpanded.bind(this);
150 this.onRowDestroy = this.onRowDestroy.bind(this);
151 this.getRowKey = this.getRowKey.bind(this);
152 this.getExpandedRows = this.getExpandedRows.bind(this);
153 this.getHeader = this.getHeader.bind(this);
154 this.getHeaderRows = this.getHeaderRows.bind(this);
155 this.getExpandedRow = this.getExpandedRow.bind(this);
156 this.getRowsByData = this.getRowsByData.bind(this);
157 this.getRows = this.getRows.bind(this);
158 this.getColGroup = this.getColGroup.bind(this);
159 this.getLeftFixedTable = this.getLeftFixedTable.bind(this);
160 this.getRightFixedTable = this.getRightFixedTable.bind(this);
161 this.getTable = this.getTable.bind(this);
162 this.getTitle = this.getTitle.bind(this);
163 this.getFooter = this.getFooter.bind(this);
164 this.getEmptyText = this.getEmptyText.bind(this);
165 this.getHeaderRowStyle = this.getHeaderRowStyle.bind(this);
166 this.syncFixedTableRowHeight = this.syncFixedTableRowHeight.bind(this);
167 this.resetScrollX = this.resetScrollX.bind(this);
168 this.findExpandedRow = this.findExpandedRow.bind(this);
169 this.isRowExpanded = this.isRowExpanded.bind(this);
170 this.detectScrollTarget = this.detectScrollTarget.bind(this);
171 this.handleBodyScroll = this.handleBodyScroll.bind(this);
172 this.handleRowHover = this.handleRowHover.bind(this);
173 this.computeTableWidth = this.computeTableWidth.bind(this);
174 this.onBodyMouseLeave = this.onBodyMouseLeave.bind(this);
175 this.tableUid = null;
176 this.contentTable = null;
177 this.leftColumnsLength //左侧固定列的长度
178 this.centerColumnsLength //非固定列的长度
179 this.columnsChildrenList = [];//复杂表头、所有叶子节点
180 }
181 componentWillMount() {
182 this.centerColumnsLength = this.columnManager.centerColumns().length
183 this.leftColumnsLength = this.columnManager.leftColumns().length
184 }
185
186 componentDidMount() {
187 this.getTableUID();
188 EventUtil.addHandler(this.contentTable,'keydown',this.onKeyDown);
189 EventUtil.addHandler(this.contentTable,'focus',this.onFocus);
190 setTimeout(this.resetScrollX, 300);
191 //含有纵向滚动条
192 // if(this.props.scroll.y){
193 this.scrollbarWidth = measureScrollbar();
194 // }
195 //后续也放在recevice里面
196 if (!this.props.originWidth) {
197 this.computeTableWidth();
198 }
199 if (this.columnManager.isAnyColumnsFixed()) {
200 this.syncFixedTableRowHeight();
201 this.resizeEvent = addEventListener(
202 window, 'resize', this.resize
203 );
204 }
205
206 }
207
208 componentWillReceiveProps(nextProps) {
209 let { hideDragHandle, rowDraggAble, showRowNum } = this.props;
210 if ('data' in nextProps) {
211 this.setState({
212 data: nextProps.data,
213 });
214 }
215 if ('expandedRowKeys' in nextProps) {
216 this.setState({
217 expandedRowKeys: nextProps.expandedRowKeys,
218 });
219 }
220 if (nextProps.columns && nextProps.columns !== this.props.columns) {
221 this.columnManager.reset(nextProps.columns, null, showRowNum, !hideDragHandle && rowDraggAble); // 加入this.props.showRowNum参数
222 if(nextProps.columns.length !== this.props.columns.length && this.refs && this.bodyTable){
223 this.scrollTop = this.bodyTable.scrollTop;
224 }
225 } else if (nextProps.children !== this.props.children) {
226 this.columnManager.reset(null, nextProps.children, showRowNum, !hideDragHandle && rowDraggAble); // 加入this.props.showRowNum参数
227 }
228 //适配lazyload
229 if(nextProps.scrollTop > -1){
230 // this.bodyTable.scrollTop = nextProps.scrollTop;
231 this.scrollTop = nextProps.scrollTop;
232 }
233 // fix:模态框中使用table,计算的滚动条宽度为0的bug
234 // fix:表格首次渲染时 display:none,再显示时,未重新计算,导致表行出现错位的bug
235 if(this.scrollbarWidth<=0 && this.props.scroll.y){
236 this.scrollbarWidth = measureScrollbar();
237 }
238 if (!nextProps.originWidth) {
239 this.computeTableWidth();
240 this.firstDid = true;//避免重复update
241 }
242 if(nextProps.resetScroll){
243 this.resetScrollX();
244 }
245
246 // console.log('this.scrollTop**********',this.scrollTop);
247
248 }
249
250 componentDidUpdate(prevProps, prevState) {
251 // todo: IE 大数据渲染,行高不固定,且设置了 heightConsistent={true} 时,滚动加载操作会导致 ie11 浏览器崩溃
252 // https://github.com/tinper-bee/bee-table/commit/bd2092cdbaad236ff89477304e58dea93325bf09
253 if(this.columnManager.isAnyColumnsFixed()) {
254 this.syncFixedTableRowHeight();
255 }
256
257 //适应模态框中表格、以及父容器宽度变化的情况
258 if (typeof (this.props.scroll.x) !== 'number' && this.contentTable.getBoundingClientRect().width !== this.contentDomWidth && this.firstDid) {
259 this.computeTableWidth();
260 this.firstDid = false;//避免重复update
261 }
262 if(this.scrollTop > -1){
263 this.refs.fixedColumnsBodyLeft && ( this.refs.fixedColumnsBodyLeft.scrollTop = this.scrollTop);
264 this.refs.fixedColumnsBodyRight && ( this.refs.fixedColumnsBodyRight.scrollTop = this.scrollTop);
265 this.bodyTable.scrollTop = this.scrollTop;
266 this.scrollTop = -1;
267 }
268 if (prevProps.data.length === 0 || this.props.data.length === 0 ) {
269 this.resetScrollX();
270 }
271
272 // 是否传入 scroll中的y属性,如果传入判断是否是整数,如果是则进行比较 。bodyTable 的clientHeight进行判断
273 this.isShowScrollY();
274 }
275
276 componentWillUnmount() {
277 // 移除绑定事件,避免内存泄漏
278 this.contentTable = null;
279 EventUtil.removeHandler(this.contentTable,'keydown',this.onKeyDown);
280 EventUtil.removeHandler(this.contentTable,'focus',this.onFocus);
281 if (this.resizeEvent) {
282 this.resizeEvent.remove();
283 }
284 }
285
286 resize = ()=>{
287 debounce(this.syncFixedTableRowHeight, 150);
288 this.computeTableWidth();
289 let renderFlag = this.state.renderFlag;
290 this.setState({
291 renderFlag: !renderFlag
292 });
293 }
294
295 getTableUID =()=>{
296 let uid = "_table_uid_"+new Date().getTime();
297 this.tableUid = uid;
298 let div = document.createElement("div");
299 // div.className = "u-table-drag-hidden-cont";
300 div.className = "u-table-drag-hidden-cont";
301 div.id = uid;
302 this.contentTable.appendChild(div);
303 }
304
305 computeTableWidth() {
306 let {expandIconAsCell} = this.props;
307 //如果用户传了scroll.x按用户传的为主
308 let setWidthParam = this.props.scroll.x
309
310 if (typeof (setWidthParam) == 'number') {
311 let numSetWidthParam = parseInt(setWidthParam);
312 this.contentWidth = numSetWidthParam;
313 } else {
314 // this.preContentDomWidth = this.contentDomWidth;
315 //计算总表格宽度、根据表格宽度和各列的宽度和比较,重置最后一列
316 this.contentDomWidth = this.contentTable.getBoundingClientRect().width//表格容器宽度
317
318 this.contentWidth = this.contentDomWidth;//默认与容器宽度一样
319
320 }
321 const computeObj = this.columnManager.getColumnWidth(this.contentWidth);
322 const expandColWidth = expandIconAsCell ? expandIconCellWidth : 0;
323 let lastShowIndex = computeObj.lastShowIndex;
324 this.computeWidth = computeObj.computeWidth + expandColWidth;
325
326 this.domWidthDiff = this.contentDomWidth - this.computeWidth;
327 if (typeof (setWidthParam) == 'string' && setWidthParam.indexOf('%')) {
328 this.contentWidth = this.contentWidth * parseInt(setWidthParam) / 100;
329 this.domWidthDiff = this.contentDomWidth - this.contentWidth;
330 }
331
332 if (this.computeWidth < this.contentWidth) {
333 let contentWidthDiff = this.scrollbarWidth?this.contentWidth - this.computeWidth-this.scrollbarWidth:this.contentWidth - this.computeWidth;
334 //bordered的表格需要减去边框的差值1
335 if(this.props.bordered){
336 contentWidthDiff = contentWidthDiff-1;
337 }
338 this.setState({ contentWidthDiff, lastShowIndex });
339 } else {
340 this.contentWidth = this.computeWidth;
341 this.setState({ contentWidthDiff: 0, lastShowIndex });//重新渲染,为了显示滚动条
342 }
343 }
344 //根据内容动态的判断是否显示纵向滚动条
345 isShowScrollY(){
346 const props = this.props;
347 const y = props.scroll && props.scroll.y;
348 if(y){
349 const bodyH = this.bodyTable.clientHeight;
350 const bodyContentH = this.bodyTable.querySelector('table').clientHeight;
351 const rightBodyTable = this.refs.fixedColumnsBodyRight;
352 // const leftBodyTable = this.refs.fixedColumnsBodyLeft;
353 const overflowy = bodyContentH <= bodyH ? 'auto':'scroll';
354 this.bodyTable.style.overflowY = overflowy;
355
356 this.headTable.style.overflowY = overflowy;
357 rightBodyTable && (rightBodyTable.style.overflowY = overflowy);
358 // 没有纵向滚动条时,表头横向滚动条根据内容动态显示 待验证
359 // if(overflowy == 'auto'){
360 // this.fixedHeadTable && (this.fixedHeadTable.style.overflowX = 'auto');
361 // rightBodyTable && (rightBodyTable.style.overflowX = 'auto');
362 // leftBodyTable && (leftBodyTable.style.overflowX = 'auto');
363 // }
364
365
366 }
367 }
368 onExpandedRowsChange(expandedRowKeys) {
369 if (!this.props.expandedRowKeys) {
370 this.setState({ expandedRowKeys });
371 }
372 this.props.onExpandedRowsChange(expandedRowKeys);
373 }
374
375 onExpanded(expanded, record, index, e) {
376 if (e) {
377 e.preventDefault();
378 e.stopPropagation();
379 }
380 const info = this.findExpandedRow(record);
381 if (typeof info !== 'undefined' && !expanded) {
382 this.onRowDestroy(record, index, true);
383 } else if (!info && expanded) {
384 const expandedRows = this.getExpandedRows().concat();
385 expandedRows.push(this.getRowKey(record, index));
386 this.onExpandedRowsChange(expandedRows);
387 }
388 this.props.onExpand(expanded, record,index);
389 }
390
391 onRowDestroy(record, rowIndex, isExpandOperation) {
392 const expandedRows = this.getExpandedRows().concat();
393 const rowKey = this.getRowKey(record, rowIndex);
394 let index = -1;
395 expandedRows.forEach((r, i) => {
396 if (r === rowKey) {
397 index = i;
398 }
399 });
400 if (index !== -1) {
401 expandedRows.splice(index, 1);
402 }
403 //
404 if(this.currentHoverKey == rowKey && this.hoverDom){
405 this.hoverDom.style.display = 'none';
406 }
407 // todo:如果是TableRow组件卸载触发的该方法,需要加判断,解决懒加载时,持续触发onExpandedRowsChange的问题
408 if(isExpandOperation){
409 this.onExpandedRowsChange(expandedRows);
410 } else {
411 const info = this.findExpandedRow(record);
412 if(typeof info === 'undefined'){
413 this.onExpandedRowsChange(expandedRows);
414 }
415 }
416 }
417
418 getRowKey(record, index) {
419 const rowKey = this.props.rowKey;
420 const key = (typeof rowKey === 'function') ?
421 rowKey(record, index) : record[rowKey];
422 warningOnce(
423 key !== undefined,
424 'Each record in table should have a unique `key` prop,' +
425 'or set `rowKey` to an unique primary key.'
426 );
427 return key;
428
429
430 }
431
432 getExpandedRows() {
433 return this.props.expandedRowKeys || this.state.expandedRowKeys;
434 }
435
436 getHeader(columns, fixed, leftFixedWidth, rightFixedWidth) {
437 const { lastShowIndex } = this.state;
438 const { filterDelay, onFilterChange, onFilterClear, filterable, showHeader, expandIconAsCell, clsPrefix, onDragStart, onDragEnter, onDragOver, onDrop,onDragEnd, draggable,
439 onMouseDown, onMouseMove, onMouseUp, dragborder, onThMouseMove, dragborderKey, minColumnWidth, headerHeight,afterDragColWidth,headerScroll ,bordered,onDropBorder,onDraggingBorder, bodyDisplayInRow} = this.props;
440 this.columnsChildrenList = []; //复杂表头拖拽,重新render表头前,将其置空
441 const rows = this.getHeaderRows(columns);
442 if (expandIconAsCell && fixed !== 'right') {
443 rows[0].unshift({
444 key: 'u-table-expandIconAsCell',
445 className: `${clsPrefix}-expand-icon-th`,
446 title: '',
447 rowSpan: rows.length,
448 width: expandIconCellWidth
449 });
450 this.columnsChildrenList.unshift({
451 className: "u-table-expand-icon-column",
452 key: "expand-icon"
453 })
454 }
455 const trStyle = headerHeight&&!fixed ? { height: headerHeight } : (fixed ? this.getHeaderRowStyle(columns, rows) : null);
456 let drop = draggable ? { onDragStart, onDragOver, onDrop,onDragEnd, onDragEnter, draggable } : {};
457 let dragBorder = dragborder ? { onMouseDown, onMouseMove, onMouseUp, dragborder, onThMouseMove, dragborderKey,onDropBorder,onDraggingBorder } : {};
458 let contentWidthDiff = 0;
459 //非固定表格,宽度不够时自动扩充
460 if (!fixed) {
461 contentWidthDiff = this.state.contentWidthDiff
462 }
463 return showHeader ? (
464 <TableHeader
465 {...drop}
466 {...dragBorder}
467 columnsChildrenList={this.columnsChildrenList}
468 locale={this.props.locale}
469 minColumnWidth={minColumnWidth}
470 contentWidthDiff={contentWidthDiff}
471 contentWidth={this.contentWidth}
472 lastShowIndex={expandIconAsCell ? parseInt(lastShowIndex) + 1 : lastShowIndex}
473 clsPrefix={clsPrefix}
474 rows={rows}
475 contentTable={this.contentTable}
476 rowStyle={trStyle}
477 fixed={fixed}
478 filterable={filterable}
479 onFilterChange={onFilterChange}
480 onFilterClear={onFilterClear}
481 filterDelay={filterDelay}
482 afterDragColWidth = {afterDragColWidth}
483 contentDomWidth={this.contentDomWidth}
484 scrollbarWidth = {this.scrollbarWidth}
485 headerScroll = {headerScroll}
486 bordered = {bordered}
487 leftFixedWidth = {leftFixedWidth}
488 rightFixedWidth = {rightFixedWidth}
489 bodyDisplayInRow = {bodyDisplayInRow}
490 />
491 ) : null;
492 }
493
494 getHeaderRows(columns, currentRow = 0, rows) {
495 const { columnKey } = this.props;
496 let { contentWidthDiff = 0, lastShowIndex = -1 } = this.state;
497 let filterCol = [];
498 rows = rows || [];
499 rows[currentRow] = rows[currentRow] || [];
500
501 columns.forEach((column,i) => {
502 if (!column.key) {
503 column.key = column[columnKey];
504 }
505 if (column.rowSpan && rows.length < column.rowSpan) {
506 while (rows.length < column.rowSpan) {
507 rows.push([]);
508 }
509 }
510 let width = column.width;
511 if (typeof (width) == 'string' && width.indexOf('%') > -1 && this.contentWidth) {
512 width = parseInt(this.contentWidth * parseInt(width) / 100);
513 } else if (width) {
514 width = parseInt(width);
515 }
516 if (!column.fixed && lastShowIndex == i && width) {
517 width = width + contentWidthDiff;
518 }
519 const cell = {
520 key: column.key,
521 className: column.className || '',
522 children: column.title,
523 drgHover: column.drgHover,
524 fixed: column.fixed,
525 width: width,
526 dataindex:column.dataIndex,
527 textAlign:column.textAlign,
528 titleAlign: column.titleAlign, // 标题水平对齐方式
529 required: column.required, // 标题是否展示必填标志
530 };
531 if (column.onHeadCellClick) {
532 cell.onClick = column.onHeadCellClick;
533 }
534 if (column.children) {
535 this.getHeaderRows(column.children, currentRow + 1, rows);
536 } else {
537 this.columnsChildrenList.push(column); //复杂表头拖拽,所有叶子节点
538 }
539 if ('colSpan' in column) {
540 cell.colSpan = column.colSpan;
541 }
542 if ('rowSpan' in column) {
543 cell.rowSpan = column.rowSpan;
544 }
545 if (cell.colSpan !== 0) {
546 rows[currentRow].push(cell);
547 }
548 //判断是否启用过滤
549 if (this.props.filterable) {
550 //组装Filter需要的Col
551 filterCol.push({
552 key: column.key,
553 children: "过滤渲染",
554 width: column.width,
555 filtertype: column.filterType,//下拉的类型 包括['text','dropdown','date','daterange','number']
556 dataindex: column.dataIndex,//field
557 datasource: this.props.data,//需要单独拿到数据处理
558 format: column.format,//设置日期的格式
559 filterdropdown: column.filterDropdown,//是否显示 show hide
560 filterdropdownauto: column.filterDropdownAuto,//是否自定义数据
561 filterdropdowndata: column.filterDropdownData,//自定义数据格式
562 filterdropdownfocus: column.filterDropdownFocus,//焦点触发函数回调
563 filterdropdowntype: column.filterDropdownType,//下拉的类型分为 String,Number 默认是String
564 filterdropdownincludekeys: column.filterDropdownIncludeKeys,//下拉条件按照指定的keys去显示
565 filterinputnumberoptions: column.filterInputNumberOptions//设置数值框内的详细属性
566 });
567 }
568 });
569 if (this.props.filterable) {
570 rows.push(filterCol);
571 }
572 return rows.filter(row => row.length > 0);
573 }
574
575 getExpandedRow(key, content, visible, className, fixed) {
576 const { clsPrefix, expandIconAsCell,onPaste } = this.props;
577 let colCount;
578 if (fixed === 'left') {
579 colCount = this.columnManager.leftLeafColumns().length;
580 } else if (fixed === 'right') {
581 colCount = this.columnManager.rightLeafColumns().length;
582 } else {
583 colCount = this.columnManager.centerColumns().length; //计算非固定列的个数,fix: 嵌套表格场景,右侧列断开的问题
584 }
585
586 let expandedRowHeight = this.state.fixedColumnsExpandedRowsHeight[key] || 'auto';
587 function contentContainer() {
588 if (content && content.props && content.props.style) {
589 return (
590 <div style={{ height: content.props.style.height }}></div>
591 )
592 } else {
593 return ' '
594 }
595 }
596
597 const columns = [{
598 key: 'extra-row',
599 render: () => ({
600 props: {
601 colSpan: colCount,
602 },
603 children: !fixed ? content : contentContainer(),
604 }),
605 }];
606 if (expandIconAsCell && fixed !== 'right') {
607 columns.unshift({
608 key: 'expand-icon-placeholder',
609 render: () => null,
610 });
611 }
612 return (
613 <TableRow
614 onPaste={onPaste}
615 columns={columns}
616 visible={visible}
617 className={className}
618 key={`${key}-extra-row`}
619 clsPrefix={`${clsPrefix}-expanded-row`}
620 indent={1}
621 expandable={false}
622 store={this.store}
623 dragborderKey={this.props.dragborderKey}
624 rowDraggAble={this.props.rowDraggAble}
625 useDragHandle={this.props.useDragHandle}
626 onDragRow={this.onDragRow}
627 onDragRowStart={this.onDragRowStart}
628 height={expandedRowHeight}
629 />
630 );
631 }
632
633 /**
634 * 行拖拽开始时触发
635 * @param currentKey 当前拖拽目标的key
636 */
637 onDragRowStart = (currentKey) => {
638 let {data} = this.state,currentIndex,record;
639 data.forEach((da,i)=>{
640 // tr 的唯一标识通过 data.key 或 rowKey 两种方式传进来
641 let trKey = da.key ? da.key : this.getRowKey(da, i);
642 if(trKey == currentKey){
643 currentIndex = i;
644 record = da;
645 }
646 });
647 this.props.onDragRowStart && this.props.onDragRowStart(record,currentIndex);
648 }
649
650 /**
651 * 行拖拽结束时触发
652 * @param currentKey 当前拖拽目标的key
653 * @param targetKey 拖拽结束时,目标位置的key
654 */
655 onDragRow = (currentKey,targetKey)=>{
656 let {data} = this.state,currentIndex,targetIndex,record;
657 data.forEach((da,i)=>{
658 // tr 的唯一标识通过 data.key 或 rowKey 两种方式传进来
659 let trKey = da.key ? da.key : this.getRowKey(da, i);
660 if(trKey == currentKey){
661 currentIndex = i;
662 record = da;
663 }
664 if(trKey == targetKey){
665 targetIndex = i;
666 }
667 });
668 if(currentIndex > -1) {
669 data = this.swapArray(data,currentIndex,targetIndex);
670 this.props.onDropRow && this.props.onDropRow(data,record,targetIndex);
671 this.setState({
672 data,
673 });
674 } else {
675 this.props.onDropRow && this.props.onDropRow(data,record,targetIndex);
676 }
677 }
678 /**
679 * 数组元素交换位置
680 * @param {array} arr 数组
681 * @param {number} index1 添加项目的位置
682 * @param {number} index2 删除项目的位置
683 */
684 swapArray = (arr, index1, index2) => {
685 var value1 = arr[index1]
686 arr.splice(index1,1)
687 if(index1<index2){
688 arr.splice(index2,0,value1)
689
690 }else {
691 arr.splice(index2+1,0,value1)
692 }
693
694 return arr;
695 }
696
697 /**
698 *
699 *
700 * @param {*} data
701 * @param {*} visible
702 * @param {*} indent 层级
703 * @param {*} columns
704 * @param {*} fixed
705 * @param {number} [rootIndex=-1] 祖级节点
706 * @returns
707 * @memberof Table
708 */
709 getRowsByData(data, visible, indent, columns, fixed,rootIndex=-1) {
710 const props = this.props;
711 const childrenColumnName = props.childrenColumnName;
712 const expandedRowRender = props.expandedRowRender;
713 const expandRowByClick = props.expandRowByClick;
714 const onPaste = props.onPaste;
715 const { fixedColumnsBodyRowsHeight } = this.state;
716 let rst = [];
717 let height;
718 const rowClassName = props.rowClassName;
719 const rowRef = props.rowRef;
720 const expandedRowClassName = props.expandedRowClassName;
721 const needIndentSpaced = props.data.some(record => record[childrenColumnName]);
722 const onRowClick = props.onRowClick;
723 const onRowDoubleClick = props.onRowDoubleClick;
724
725 const expandIconAsCell = fixed !== 'right' ? props.expandIconAsCell : false;
726 const expandIconColumnIndex = props.expandIconColumnIndex
727 if(props.lazyLoad && props.lazyLoad.preHeight && indent == 0){
728 rst.push(
729 <TableRow onPaste={onPaste} height={props.lazyLoad.preHeight} columns={[]} className='' key={'table_row_first'} store={this.store} visible = {true}/>
730 )
731 }
732 const lazyCurrentIndex = props.lazyLoad && props.lazyLoad.startIndex ?props.lazyLoad.startIndex :0;
733 const lazyParentIndex = props.lazyLoad && props.lazyLoad.startParentIndex ?props.lazyLoad.startParentIndex :0;
734 const lazyEndIndex = props.lazyLoad && props.lazyLoad.endIndex ?props.lazyLoad.endIndex :-1;
735 for (let i = 0; i < data.length; i++) {
736 let isHiddenExpandIcon;
737 const record = data[i];
738 const key = this.getRowKey(record, i);
739 // 兼容 NCC 以前的业务逻辑,支持外部通过 record 中的 isleaf 字段,判断是否为叶子节点
740 record['_isLeaf'] = typeof record['isleaf'] === 'boolean' ? record['isleaf'] : record['_isLeaf'];
741 // _isLeaf 字段是在 bigData 里添加的,只有层级树大数据场景需要该字段
742 // _isLeaf 有三种取值情况:true / false / null。(Table内部字段)
743 const _isLeaf = typeof record['_isLeaf'] === 'boolean' ? record['_isLeaf'] : null;
744 const childrenColumn = _isLeaf ? false : record[childrenColumnName];
745 const isRowExpanded = this.isRowExpanded(record, i);
746 let expandedRowContent;
747 let expandedContentHeight = 0;
748 //fixedIndex一般是跟index是一个值的,只有是树结构时,会讲子节点的值也累计上
749 let fixedIndex = i;
750 //判断是否是tree结构
751 if (this.treeType) {
752 fixedIndex = this.treeRowIndex;
753 }
754 if (expandedRowRender && isRowExpanded) {
755 expandedRowContent = expandedRowRender(record, fixedIndex+lazyCurrentIndex, indent);
756 expandedContentHeight = parseInt(expandedRowContent.props && expandedRowContent.props.style && expandedRowContent.props.style.height?expandedRowContent.props.style.height:0);
757 }
758 //只有当使用expandedRowRender参数的时候才去识别isHiddenExpandIcon(隐藏行展开的icon)
759 if (expandedRowRender && typeof props.haveExpandIcon == 'function') {
760 isHiddenExpandIcon = props.haveExpandIcon(record, i);
761 }
762
763
764 const onHoverProps = {};
765
766 onHoverProps.onHover = this.handleRowHover;
767
768
769 if (props.bodyDisplayInRow && props.height) {
770 height = props.height
771 } else if(fixed || props.heightConsistent) {
772 height = fixedColumnsBodyRowsHeight[fixedIndex];
773 }
774
775 let leafColumns;
776 if (fixed === 'left') {
777 leafColumns = this.columnManager.leftLeafColumns();
778 } else if (fixed === 'right') {
779 leafColumns = this.columnManager.rightLeafColumns();
780 } else {
781 leafColumns = this.columnManager.leafColumns();
782 }
783 let className = rowClassName(record, fixedIndex+lazyCurrentIndex, indent);
784
785 //合计代码如果是最后一行并且有合计功能时,最后一行为合计列
786 if(i == data.length -1 && props.showSum){
787 className = className + ' sumrow';
788 }
789
790 let paramRootIndex = rootIndex;
791 //小于0说明为第一层节点,她的子孙节点要保存自己的根节点
792 if(paramRootIndex<0){
793 paramRootIndex = i+lazyParentIndex;
794 }
795 let index = i;
796 if(rootIndex ==-1){
797 index = i+lazyParentIndex
798 }
799 rst.push(
800 <TableRow
801 onPaste={onPaste}
802 indent={indent}
803 indentSize={props.indentSize}
804 needIndentSpaced={needIndentSpaced}
805 className={`${className} ${props.rowDraggAble && !props.useDragHandle?'row-dragg-able ':''}`}
806 record={record}
807 expandIconAsCell={expandIconAsCell}
808 onDestroy={this.onRowDestroy}
809 index={index}
810 visible={visible}
811 expandRowByClick={expandRowByClick}
812 onExpand={this.onExpanded}
813 expandable={expandedRowRender || ((childrenColumn && childrenColumn.length > 0) ? true : _isLeaf === false)}
814 expanded={isRowExpanded}
815 clsPrefix={`${props.clsPrefix}-row`}
816 childrenColumnName={childrenColumnName}
817 columns={leafColumns}
818 expandIconColumnIndex={expandIconColumnIndex}
819 onRowClick={onRowClick}
820 onRowDoubleClick={onRowDoubleClick}
821 height={height}
822 isHiddenExpandIcon={isHiddenExpandIcon}
823 {...onHoverProps}
824 key={"table_row_"+key+"_"+index}
825 hoverKey={key}
826 ref={rowRef}
827 store={this.store}
828 fixed={fixed}
829 expandedContentHeight={expandedContentHeight}
830 setRowHeight={props.setRowHeight}
831 setRowParentIndex={props.setRowParentIndex}
832 treeType={childrenColumn||this.treeType?true:false}
833 fixedIndex={fixedIndex+lazyCurrentIndex}
834 rootIndex = {rootIndex}
835 syncHover = {props.syncHover}
836 bodyDisplayInRow = {props.bodyDisplayInRow}
837 rowDraggAble={props.rowDraggAble}
838 useDragHandle={props.useDragHandle}
839 onDragRow={this.onDragRow}
840 onDragRowStart={this.onDragRowStart}
841 contentTable={this.contentTable}
842 tableUid = {this.tableUid}
843 expandedIcon={props.expandedIcon}
844 collapsedIcon={props.collapsedIcon}
845 lazyStartIndex = {lazyCurrentIndex}
846 lazyEndIndex = {lazyEndIndex}
847 centerColumnsLength={this.centerColumnsLength}
848 leftColumnsLength={this.leftColumnsLength}
849 expandIconCellWidth={expandIconCellWidth}
850 />
851 );
852 this.treeRowIndex++;
853 const subVisible = visible && isRowExpanded;
854
855 if (expandedRowContent && isRowExpanded) {
856 rst.push(this.getExpandedRow(
857 key, expandedRowContent, subVisible, expandedRowClassName(record, i, indent), fixed
858 ));
859 }
860 if (childrenColumn) {
861 this.isTreeType = true; //增加该标志位,为了兼容老版本,不修改以前的 `this.treeType` 的相关逻辑
862 this.treeType = true;//证明是tree表形式visible = {true}
863 rst = rst.concat(this.getRowsByData(
864 childrenColumn, subVisible, indent + 1, columns, fixed,paramRootIndex
865 ));
866 }
867 }
868
869 if(props.lazyLoad && props.lazyLoad.sufHeight && indent == 0){
870 rst.push(
871 <TableRow onPaste={onPaste} height={props.lazyLoad.sufHeight} key={'table_row_end'} columns={[]} className='' store={this.store} visible = {true}/>
872 )
873 }
874 if (!this.isTreeType) {
875 this.treeType = false;
876 }
877 return rst;
878 }
879
880 getRows(columns, fixed) {
881 //统计index,只有含有树表结构才有用,因为树表结构时,固定列的索引取值有问题
882 this.treeRowIndex = 0;
883 //每次遍历 data 前,将this.isTreeType置为 false,若遍历完 data,此变量仍为 false,说明是普通表格
884 this.isTreeType = false;
885 let rs = this.getRowsByData(this.state.data, true, 0, columns, fixed);
886 return rs;
887 }
888
889 getColGroup(columns, fixed) {
890 let cols = [];
891 let self = this;
892
893 let { contentWidthDiff = 0, lastShowIndex = 0 } = this.state;
894 if (this.props.expandIconAsCell && fixed !== 'right') {
895 cols.push(
896 <col
897 className={`${this.props.clsPrefix}-expand-icon-col`}
898 key="u-table-expand-icon-col"
899 />
900 );
901 }
902 let leafColumns;
903 if (fixed === 'left') {
904 contentWidthDiff = 0;
905 leafColumns = this.columnManager.leftLeafColumns();
906 } else if (fixed === 'right') {
907 contentWidthDiff = 0;
908 leafColumns = this.columnManager.rightLeafColumns();
909 } else {
910 leafColumns = this.columnManager.leafColumns();
911 }
912 cols = cols.concat(leafColumns.map((c, i, arr) => {
913 let fixedClass ='';
914 let width = c.width;
915 if (typeof (width) == 'string' && width.indexOf('%') > -1 && self.contentWidth) {
916 width = parseInt(self.contentWidth * parseInt(width) / 100);
917 } else if (width) {
918 width = parseInt(width);
919 }
920 if (lastShowIndex == i && width) {
921 width = width + contentWidthDiff;
922 }
923 if (!fixed && c.fixed) {
924 fixedClass = ` ${this.props.clsPrefix}-row-fixed-columns-in-body`;
925 }
926 return <col key={c.key} style={{ width: width, minWidth: c.width }} className={fixedClass}/>;
927 }));
928 return <colgroup id="bee-table-colgroup">{cols}</colgroup>;
929 }
930
931 renderDragHideTable = () => {
932 const { columns, dragborder, dragborderKey } = this.props;
933 if (!dragborder) return null;
934 let sum = 0;
935 return (<div id={`u-table-drag-hide-table-${dragborderKey}`} className={`${this.props.clsPrefix}-hiden-drag`} >
936 {
937 columns.map((da, i) => {
938 sum += da.width ? da.width : 0;
939 return (<div className={`${this.props.clsPrefix}-hiden-drag-li`} key={da + "_hiden_" + i} style={{ left: sum + "px" }}></div>);
940 })
941 }
942 </div>);
943 }
944
945 getLeftFixedTable() {
946 return this.getTable({
947 columns: this.columnManager.leftColumns(),
948 fixed: 'left',
949 });
950 }
951
952 getRightFixedTable() {
953 return this.getTable({
954 columns: this.columnManager.rightColumns(),
955 fixed: 'right',
956 });
957 }
958
959 getTable(options = {}) {
960 const { columns, fixed } = options;
961 const { clsPrefix, scroll = {}, getBodyWrapper, footerScroll,headerScroll,hideHeaderScroll = false,expandIconAsCell } = this.props;
962 let { useFixedHeader,data } = this.props;
963 const bodyStyle = { ...this.props.bodyStyle }; // 这里为什么不写在上面?
964 const headStyle = {};
965 const innerBodyStyle = {};
966 const leftFixedWidth = this.columnManager.getLeftColumnsWidth(this.contentWidth);
967 const rightFixedWidth = this.columnManager.getRightColumnsWidth(this.contentWidth);
968
969 let tableClassName = '';
970 //表格元素的宽度大于容器的宽度也显示滚动条
971 if (scroll.x || fixed || this.contentDomWidth < this.contentWidth) {
972 tableClassName = `${clsPrefix}-fixed`;
973 //没有数据并且含有顶部菜单时
974 if(this.props.data.length == 0 && this.props.headerScroll ){
975 bodyStyle.overflowX = 'hidden';
976 }
977 if (!footerScroll) {
978 bodyStyle.overflowX = bodyStyle.overflowX || 'auto';
979 }
980 }
981
982 if (scroll.y) {
983 // maxHeight will make fixed-Table scrolling not working
984 // so we only set maxHeight to body-Table here
985 if (fixed) {
986 // bodyStyle.height = bodyStyle.height || scroll.y;
987 innerBodyStyle.maxHeight = bodyStyle.maxHeight || scroll.y;
988 innerBodyStyle.overflowY = bodyStyle.overflowY || 'scroll';
989 } else {
990 bodyStyle.maxHeight = bodyStyle.maxHeight || scroll.y;
991 }
992 bodyStyle.overflowY = bodyStyle.overflowY || 'scroll';
993 useFixedHeader = true;
994
995 // Add negative margin bottom for scroll bar overflow bug
996 const scrollbarWidth = this.scrollbarWidth;
997 if (scrollbarWidth >= 0) {
998 (fixed ? bodyStyle : headStyle).paddingBottom = '0px';
999 //显示表头滚动条
1000 if(headerScroll){
1001 if(fixed){
1002
1003 if(this.domWidthDiff <= 0){
1004 headStyle.marginBottom = `${scrollbarWidth}px`;
1005 bodyStyle.marginBottom = `-${scrollbarWidth}px`;
1006 }else{
1007 innerBodyStyle.overflowX = 'auto';
1008 }
1009 }else{
1010 //内容少,不用显示滚动条
1011 if(this.domWidthDiff > 0){
1012 headStyle.overflowX = 'hidden';
1013 }
1014 headStyle.marginBottom = `0px`;
1015 }
1016 }else{
1017 if(fixed){
1018 if(this.domWidthDiff > 0){
1019 headStyle.overflow = 'hidden';
1020 innerBodyStyle.overflowX = 'auto'; //兼容expand场景、子表格含有固定列的场景
1021 }else{
1022 bodyStyle.marginBottom = `-${scrollbarWidth}px`;
1023 }
1024
1025 }else{
1026 // 没有数据时,表头滚动条隐藏问题
1027 if(data.length == 0 && this.domWidthDiff < 0){
1028 headStyle.marginBottom = '0px';
1029 }else{
1030 headStyle.marginBottom = `-${scrollbarWidth}px`;
1031 }
1032
1033 }
1034
1035 }
1036 }
1037 }
1038
1039 if(data.length == 0 && hideHeaderScroll){
1040 //支持 NCC 需求:表格无数据时,去掉表头滚动条 (https://github.com/iuap-design/tinper-bee/issues/207)
1041 headStyle.marginBottom = `-${this.scrollbarWidth}px`;
1042 }
1043
1044 const renderTable = (hasHead = true, hasBody = true) => {
1045 const tableStyle = {};
1046 if (!fixed && scroll.x) {
1047 // not set width, then use content fixed width
1048 if (scroll.x === true) {
1049 tableStyle.tableLayout = 'fixed';
1050 } else {
1051 tableStyle.width = this.contentWidth - this.columnManager.getLeftColumnsWidth(this.contentWidth) - this.columnManager.getRightColumnsWidth(this.contentWidth);
1052 }
1053 }
1054 // 自动出现滚动条
1055 if ( !fixed && this.contentDomWidth < this.contentWidth) {
1056 tableStyle.width = this.contentWidth - this.columnManager.getLeftColumnsWidth(this.contentWidth) - this.columnManager.getRightColumnsWidth(this.contentWidth);
1057 }
1058 const tableBody = hasBody ? getBodyWrapper(
1059 <tbody className={`${clsPrefix}-tbody`} onMouseLeave={this.onBodyMouseLeave}>
1060 {this.getRows(columns, fixed)}
1061 </tbody>
1062 ) : null;
1063 let _drag_class = this.props.dragborder ? "table-drag-bordered" : ""
1064 return (
1065 <table className={` ${tableClassName} table-bordered ${_drag_class} `} style={tableStyle} >
1066 {/* {this.props.dragborder?null:this.getColGroup(columns, fixed)} */}
1067 {this.getColGroup(columns, fixed)}
1068 {hasHead ? this.getHeader(columns, fixed, leftFixedWidth, rightFixedWidth) : null}
1069 {tableBody}
1070 </table>
1071 );
1072 };
1073
1074 let headTable;
1075
1076 if (useFixedHeader) {
1077 headTable = (
1078 <div
1079 className={`${clsPrefix}-header`}
1080 ref={(el)=>{fixed ? this.fixedHeadTable=el : this.headTable=el}}
1081 style={headStyle}
1082 onMouseOver={this.detectScrollTarget}
1083 onTouchStart={this.detectScrollTarget}
1084 onScroll={this.handleBodyScroll}
1085 >
1086 {renderTable(true, false)}
1087 </div>
1088 );
1089 }
1090 let BodyTable = (
1091 <div
1092 className={`${clsPrefix}-body`}
1093 style={bodyStyle}
1094 ref={(el)=>{this.bodyTable = el}}
1095 onMouseOver={this.detectScrollTarget}
1096 onTouchStart={this.detectScrollTarget}
1097 onScroll={this.handleBodyScroll}
1098 onMouseLeave={this.onBodyMouseLeave}
1099 >
1100 {this.renderDragHideTable()}
1101 {renderTable(!useFixedHeader)}
1102 </div>
1103 );
1104
1105 if (fixed && columns.length) {
1106 let refName;
1107 if (columns[0].fixed === 'left' || columns[0].fixed === true) {
1108 refName = 'fixedColumnsBodyLeft';
1109 } else if (columns[0].fixed === 'right') {
1110 refName = 'fixedColumnsBodyRight';
1111 }
1112 delete bodyStyle.overflowX;
1113 delete bodyStyle.overflowY;
1114 BodyTable = (
1115 <div
1116 className={`${clsPrefix}-body-outer`}
1117 style={{ ...bodyStyle }}
1118 >
1119 <div
1120 style={{...innerBodyStyle}}
1121 className={`${clsPrefix}-body-inner`}
1122 ref={refName}
1123 onMouseOver={this.detectScrollTarget}
1124 onTouchStart={this.detectScrollTarget}
1125 onScroll={this.handleBodyScroll}
1126 >
1127 {renderTable(!useFixedHeader)}
1128 {/* <div className="scroll-dom" style={{height:`${this.scrollbarWidth}px`}}></div> */}
1129 </div>
1130 </div>
1131 );
1132 }
1133 // const leftFixedWidth = this.columnManager.getLeftColumnsWidth(this.contentWidth);
1134 // const rightFixedWidth = this.columnManager.getRightColumnsWidth(this.contentWidth);
1135 let expandIconWidth = expandIconAsCell ? 32 : 0;
1136 let parStyle = {}
1137 if(!fixed){
1138 parStyle = {'marginLeft':leftFixedWidth + expandIconWidth,'marginRight':rightFixedWidth}
1139 }
1140 return <div style={parStyle}>{headTable}{BodyTable}</div>;
1141 }
1142
1143 getTitle() {
1144 const { title, clsPrefix } = this.props;
1145 return title ? (
1146 <div className={`${clsPrefix}-title`}>
1147 {title(this.state.data)}
1148 </div>
1149 ) : null;
1150 }
1151
1152 getFooter() {
1153 const { footer, clsPrefix } = this.props;
1154 return footer ? (
1155 <div className={`${clsPrefix}-footer`}>
1156 {footer(this.state.data)}
1157 </div>
1158 ) : null;
1159 }
1160
1161 getEmptyText() {
1162 const { emptyText : defaultEmptyText, clsPrefix, data } = this.props;
1163 let locale = getComponentLocale(this.props, this.context, 'Table', () => i18n);
1164 let emptyText = defaultEmptyText !== undefined ? defaultEmptyText : () => <div><Icon type="uf-nodata" className="table-nodata"></Icon><span>{locale["no_data"]}</span></div>;
1165
1166 return !data.length ? (
1167 <div className={`${clsPrefix}-placeholder`}>
1168 {emptyText()}
1169 </div>
1170 ) : null;
1171 }
1172
1173 getHeaderRowStyle(columns, rows) {
1174 const { fixedColumnsHeadRowsHeight } = this.state;
1175 const headerHeight = fixedColumnsHeadRowsHeight[0];
1176
1177 if (headerHeight && columns) {
1178 if (headerHeight === 'auto') {
1179 return { height: 'auto' };
1180 }
1181 return { height: headerHeight / rows.length };
1182 }
1183 return null;
1184 }
1185 getStyle(obj,attr){
1186 if(obj.currentStyle){
1187 return obj.currentStyle[attr];
1188 }
1189 else{
1190 return document.defaultView.getComputedStyle(obj,null)[attr];
1191 }
1192 }
1193 getTdPadding=(td)=>{
1194 let tdPaddingTop = this.getStyle(td,'paddingTop'),tdPaddingBottom = this.getStyle(td,'paddingBottom'),
1195 tdBorderTop = this.getStyle(td,'borderTopWidth'),tdBorderBottom = this.getStyle(td,'borderBottomWidth');
1196 return Number(tdPaddingTop.replace('px',''))+Number(tdPaddingBottom.replace('px',''))+Number(tdBorderTop.replace('px',''))+Number(tdBorderBottom.replace('px',''))
1197
1198 }
1199
1200 syncFixedTableRowHeight() {
1201 //this.props.height、headerHeight分别为用户传入的行高和表头高度,如果有值,所有行的高度都是固定的,主要为了避免在千行数据中有固定列时获取行高度有问题
1202 const { clsPrefix, height, headerHeight,columns,heightConsistent, bodyDisplayInRow } = this.props;
1203 const headRows = this.headTable ?
1204 this.headTable.querySelectorAll('thead') :
1205 this.bodyTable.querySelectorAll('thead');
1206 const expandedRows = this.bodyTable.querySelectorAll(`.${clsPrefix}-expanded-row`) || [];
1207 const bodyRows = this.bodyTable.querySelectorAll(`.${clsPrefix}-row`) || [];
1208 const leftBodyRows = this.refs.fixedColumnsBodyLeft && this.refs.fixedColumnsBodyLeft.querySelectorAll(`.${clsPrefix}-row`) || [];
1209 const rightBodyRows = this.refs.fixedColumnsBodyRight && this.refs.fixedColumnsBodyRight.querySelectorAll(`.${clsPrefix}-row`) || [];
1210 const fixedColumnsHeadRowsHeight = [].map.call(
1211 headRows, row =>{
1212 let height = headerHeight;
1213 if(headerHeight){
1214 height = (getMaxColChildrenLength(columns)+1)*headerHeight;
1215 }
1216 return headerHeight ? height : (row.getBoundingClientRect().height || 'auto')}
1217 );
1218 const fixedColumnsBodyRowsHeight = [].map.call(
1219 bodyRows, (row,index) =>{
1220 let rsHeight = height;
1221 if(bodyDisplayInRow && rsHeight){
1222 return rsHeight;
1223 }else{
1224 // 为了提高性能,默认获取主表的高度,但是有的场景中固定列的高度比主表的高度高,所以提供此属性,会统计所有列的高度取最大的,设置
1225 // 内容折行显示,并又设置了 height 的情况下,也要获取主表高度
1226 if(heightConsistent || (!bodyDisplayInRow && rsHeight)){
1227 let leftHeight,rightHeight,currentHeight,maxHeight;
1228 leftHeight = leftBodyRows[index]?leftBodyRows[index].getBoundingClientRect().height:0;
1229 rightHeight = rightBodyRows[index]?rightBodyRows[index].getBoundingClientRect().height:0;
1230 currentHeight = row.getBoundingClientRect().height
1231 maxHeight = Math.max(leftHeight,rightHeight,currentHeight);
1232 return maxHeight || 'auto'
1233 }else{
1234 return row.getBoundingClientRect().height || 'auto'
1235 }
1236 }
1237 }
1238 );
1239 const fixedColumnsExpandedRowsHeight = {};
1240 // expandedRows为NodeList Array.prototype.forEach ie 下报错 对象不支持 “forEach” 方法
1241 expandedRows.length > 0 && Array.prototype.forEach.call(expandedRows,row => {
1242 let parentRowKey = row && row.previousSibling && row.previousSibling.getAttribute("data-row-key"),
1243 height = row && row.getBoundingClientRect().height || 'auto';
1244 try {//子表数据减少时,动态计算高度
1245 let td = row.querySelector('td');
1246 let tdPadding = this.getTdPadding(td);
1247 let trueheight = row.querySelectorAll('.u-table')[0].getBoundingClientRect().height;
1248 height = trueheight+tdPadding;
1249 } catch (error) {
1250
1251 }
1252 fixedColumnsExpandedRowsHeight[parentRowKey] = height;
1253 })
1254 if (shallowequal(this.state.fixedColumnsHeadRowsHeight, fixedColumnsHeadRowsHeight) &&
1255 shallowequal(this.state.fixedColumnsBodyRowsHeight, fixedColumnsBodyRowsHeight) &&
1256 shallowequal(this.state.fixedColumnsExpandedRowsHeight, fixedColumnsExpandedRowsHeight)) {
1257 return;
1258 }
1259 this.setState({
1260 fixedColumnsHeadRowsHeight,
1261 fixedColumnsBodyRowsHeight,
1262 fixedColumnsExpandedRowsHeight,
1263 });
1264 }
1265
1266 resetScrollX() {
1267 if (this.headTable) {
1268 this.headTable.scrollLeft = 0;
1269 }
1270 if (this.bodyTable) {
1271 this.bodyTable.scrollLeft = 0;
1272 }
1273 }
1274
1275 findExpandedRow(record, index) {
1276 const rows = this.getExpandedRows().filter(i => i === this.getRowKey(record, index));
1277 return rows[0];
1278 }
1279
1280 isRowExpanded(record, index) {
1281 return typeof this.findExpandedRow(record, index) !== 'undefined';
1282 }
1283 onBodyMouseLeave(e){
1284 this.hideHoverDom(e);
1285 }
1286
1287 detectScrollTarget(e) {
1288 if (this.scrollTarget !== e.currentTarget) {
1289 this.scrollTarget = e.currentTarget;
1290 }
1291 }
1292
1293 hideHoverDom(e){
1294 if(this.hoverDom){
1295 this.hoverDom.style.display = 'none';
1296 }
1297 }
1298
1299
1300 handleBodyScroll(e) {
1301 const headTable = this.headTable;
1302 const { scroll = {},clsPrefix,handleScrollY, handleScrollX, onBodyScroll} = this.props;
1303 const {fixedColumnsBodyLeft, fixedColumnsBodyRight } = this.refs;
1304 // Prevent scrollTop setter trigger onScroll event
1305 // http://stackoverflow.com/q/1386696
1306 if (e.currentTarget !== e.target) {
1307 return;
1308 }
1309 if (e.target.scrollLeft !== this.lastScrollLeft) {
1310 let position = '';
1311 if (e.target === this.bodyTable && headTable) {
1312 headTable.scrollLeft = e.target.scrollLeft;
1313 } else if (e.target === headTable && this.bodyTable) {
1314 this.bodyTable.scrollLeft = e.target.scrollLeft;
1315 }
1316 if (e.target.scrollLeft === 0) {
1317 position='left';
1318 } else if (e.target.scrollLeft + 1 >=
1319 e.target.children[0].getBoundingClientRect().width -
1320 e.target.getBoundingClientRect().width) {
1321 position='right';
1322 } else if (this.state.scrollPosition !== 'middle') {
1323 position='middle';
1324 }
1325 if(position){
1326 classes(this.contentTable)
1327 .remove(new RegExp(`^${clsPrefix}-scroll-position-.+$`))
1328 .add(`${clsPrefix}-scroll-position-${position}`);
1329 }
1330 if(handleScrollX){
1331 debounce(
1332 handleScrollX(e.target.scrollLeft,this.treeType),
1333 300)
1334 }
1335 }
1336 // console.log('lastScrollTop--'+this.lastScrollTop+'--eventScrollTop--'+ e.target.scrollTop);
1337 if (scroll.y && this.lastScrollTop != e.target.scrollTop && e.target !== headTable) {
1338 if (fixedColumnsBodyLeft && e.target !== fixedColumnsBodyLeft) {
1339 fixedColumnsBodyLeft.scrollTop = e.target.scrollTop;
1340 }
1341 if (fixedColumnsBodyRight && e.target !== fixedColumnsBodyRight) {
1342 fixedColumnsBodyRight.scrollTop = e.target.scrollTop;
1343 }
1344 if (this.bodyTable && e.target !== this.bodyTable) {
1345 this.bodyTable.scrollTop = e.target.scrollTop;
1346 }
1347 if(this.hoverDom){
1348 this.hoverDom.style.display = 'none'
1349 }
1350 this.lastScrollTop = e.target.scrollTop;
1351 if(handleScrollY){
1352 debounce(
1353 handleScrollY(this.lastScrollTop,this.treeType,onBodyScroll),
1354 300)
1355 }else{
1356 //滚动回调
1357 onBodyScroll(this.lastScrollTop)
1358 }
1359
1360 }
1361
1362 // Remember last scrollLeft for scroll direction detecting.
1363 this.lastScrollLeft = e.target.scrollLeft;
1364 }
1365
1366 handleRowHover(isHover, key,event,currentIndex, propsRecord) {
1367 //增加新的API,设置是否同步Hover状态,提高性能,避免无关的渲染
1368 let { syncHover,onRowHover,data } = this.props;
1369 //fix:树形表,onRowHover返回参数异常
1370 let { isTreeType } = this;
1371 const record = isTreeType ? propsRecord : data[currentIndex];
1372 // 固定列、或者含有hoverdom时情况下同步hover状态
1373 if(this.columnManager.isAnyColumnsFixed() && syncHover ){
1374 this.hoverKey = key;
1375 this.store.setState({
1376 currentHoverKey: isHover ? key : null,
1377 });
1378 }
1379 if(this.hoverDom){
1380 if(isHover){
1381 this.currentHoverKey = key;
1382 const td = closest(event.target,'td');
1383 if(td){
1384 const scrollTop = this.lastScrollTop ?this.lastScrollTop:0
1385 let top = td.offsetTop - scrollTop;
1386 if(this.headTable){
1387 top = top + this.headTable.clientHeight;
1388 }
1389 this.hoverDom.style.top = top + 'px';
1390 this.hoverDom.style.height = td.offsetHeight + 'px';
1391 this.hoverDom.style.lineHeight = td.offsetHeight + 'px';
1392 this.hoverDom.style.display = 'block';
1393 }
1394 this.setState({
1395 currentHoverIndex: currentIndex,
1396 currentHoverRecord: record
1397 })
1398 }
1399 }
1400
1401 onRowHover && onRowHover(currentIndex,record);
1402
1403 }
1404
1405 onRowHoverMouseEnter = () =>{
1406
1407 this.store.setState({
1408 currentHoverKey: this.currentHoverKey,
1409 });
1410 this.hoverDom.style.display = 'block';
1411
1412 }
1413 onRowHoverMouseLeave = () =>{
1414
1415 }
1416 onFocus=(e)=>{
1417 this.props.onKeyTab&&this.props.onKeyTab();
1418 }
1419
1420 onKeyDown=(e)=>{
1421 let event = Event.getEvent(e);
1422 // event.preventDefault?event.preventDefault():event.returnValue = false;
1423 if(event.keyCode === 38){//up
1424 event.preventDefault&&event.preventDefault();
1425 this.props.onKeyUp&&this.props.onKeyUp();
1426 }else if(event.keyCode === 40){//down
1427 event.preventDefault&&event.preventDefault();
1428 this.props.onKeyDown&&this.props.onKeyDown();
1429 }
1430 this.props.onTableKeyDown&&this.props.onTableKeyDown();
1431 }
1432
1433 render() {
1434 const { currentHoverRecord, currentHoverIndex } = this.state;
1435 const props = this.props;
1436 const clsPrefix = props.clsPrefix;
1437 const hasFixedLeft = this.columnManager.isAnyColumnsLeftFixed();
1438 let className = props.clsPrefix;
1439 if (props.className) {
1440 className += ` ${props.className}`;
1441 }
1442 if (props.useFixedHeader || (props.scroll && props.scroll.y)) {
1443 className += ` ${clsPrefix}-fixed-header`;
1444 }
1445 if (!props.showHeader) {
1446 className += ` ${clsPrefix}-hide-header`;
1447 }
1448 if (props.bordered) {
1449 className += ` ${clsPrefix}-bordered`;
1450 }
1451 className += ` ${clsPrefix}-scroll-position-${this.state.scrollPosition}`;
1452 //如果传入height说明是固定高度
1453 //内容过多折行显示时,height 属性会失效,为了避免产生错行
1454 if(props.bodyDisplayInRow && props.height){
1455 className += ' fixed-height';
1456 }
1457 if(props.bodyDisplayInRow){
1458 className += ' body-dispaly-in-row'
1459 }
1460 if(props.headerDisplayInRow){
1461 className += ' header-dispaly-in-row'
1462 }
1463 const isTableScroll = this.columnManager.isAnyColumnsFixed() ||
1464 props.scroll.x ||
1465 props.scroll.y;
1466 let loading = props.loading;
1467 if (typeof loading === 'boolean') {
1468 loading = {
1469 show: loading,
1470 };
1471 }
1472 if (props.size) {
1473 className += ` ${clsPrefix}-${props.size}`;
1474 }
1475 if(hasFixedLeft){
1476 className += ` has-fixed-left`;
1477 }
1478
1479 return (
1480 <div className={className} style={props.style} ref={el => this.contentTable = el}
1481 tabIndex={props.focusable && (props.tabIndex?props.tabIndex:'0')} >
1482 {this.getTitle()}
1483 <div className={`${clsPrefix}-content`}>
1484
1485 <div className={isTableScroll ? `${clsPrefix}-scroll` : ''} >
1486 {this.getTable({ columns: this.columnManager.groupedColumns() })}
1487 {this.getEmptyText()}
1488 {this.getFooter()}
1489 </div>
1490
1491 {hasFixedLeft &&
1492 <div className={`${clsPrefix}-fixed-left`}>
1493 {this.getLeftFixedTable()}
1494 </div>}
1495 {this.columnManager.isAnyColumnsRightFixed() &&
1496 <div className={`${clsPrefix}-fixed-right`}>
1497 {this.getRightFixedTable()}
1498 </div>}
1499 </div>
1500 <Loading
1501 container={this}
1502 {...loading} />
1503 { props.hoverContent && <div className="u-row-hover"
1504 onMouseEnter={this.onRowHoverMouseEnter} onMouseLeave={this.onRowHoverMouseLeave} ref={el=> this.hoverDom = el }>{props.hoverContent(currentHoverRecord, currentHoverIndex)}</div>}
1505 </div>
1506 );
1507 }
1508};
1509
1510Table.propTypes = propTypes;
1511Table.defaultProps = defaultProps;
1512Table.contextTypes = {
1513 beeLocale: PropTypes.object
1514};
1515
1516export default Table;