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