@gmfe/table-x 2.14.30-beta.4 → 2.14.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +226 -0
- package/package.json +4 -4
- package/src/base/td.js +1 -1
- package/src/base/tr.js +2 -2
- package/src/index.js +0 -4
- package/src/index.less +3 -155
- package/src/util/index.js +6 -8
- package/src/hoc/two_level_header_table_x/flatten_helper.js +0 -29
- package/src/hoc/two_level_header_table_x/rebuild_helper.js +0 -95
- package/src/hoc/two_level_header_table_x/table_x.js +0 -169
- package/src/hoc/two_level_header_table_x/td.js +0 -126
- package/src/hoc/two_level_header_table_x/thead.js +0 -293
- package/src/hoc/two_level_header_table_x/tr.js +0 -53
- package/src/hoc/two_level_header_table_x/transform_helper.js +0 -165
- package/src/hoc/two_level_header_table_x.js +0 -36
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import React, { useMemo } from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import { useTable } from 'react-table'
|
|
4
|
-
import { Empty, Loading, afterScroll, __DEFAULT_COLUMN } from '../../util'
|
|
5
|
-
import classNames from 'classnames'
|
|
6
|
-
import _ from 'lodash'
|
|
7
|
-
import TwoLevelTHead from './thead'
|
|
8
|
-
import Tr from './tr'
|
|
9
|
-
import { rebuildNestedColumnsFromFlat } from './rebuild_helper'
|
|
10
|
-
import { transformColumnsForTwoLevel } from './transform_helper'
|
|
11
|
-
|
|
12
|
-
const defaultColumn = __DEFAULT_COLUMN
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* 支持两级表头的 TableX 组件
|
|
16
|
-
*
|
|
17
|
-
* 接收:
|
|
18
|
-
* - columns: 处理后的扁平 columns(经过 select、diy HOC 处理,每个 subColumn 都带有 parentKey、parentHeader、parentFixed 属性)
|
|
19
|
-
*
|
|
20
|
-
* 功能:
|
|
21
|
-
* 1. 根据 parentKey 重建嵌套结构
|
|
22
|
-
* 2. 转换为 react-table 格式
|
|
23
|
-
* 3. 使用 TwoLevelTHead 渲染两级表头
|
|
24
|
-
*/
|
|
25
|
-
const TwoLevelTableX = ({
|
|
26
|
-
columns,
|
|
27
|
-
data,
|
|
28
|
-
loading,
|
|
29
|
-
SubComponent,
|
|
30
|
-
keyField = 'value',
|
|
31
|
-
className,
|
|
32
|
-
tiled = false,
|
|
33
|
-
onScroll,
|
|
34
|
-
isTrDisable = () => false,
|
|
35
|
-
isTrHighlight = () => false,
|
|
36
|
-
...rest
|
|
37
|
-
}) => {
|
|
38
|
-
const rebuiltColumns = useMemo(() => {
|
|
39
|
-
return rebuildNestedColumnsFromFlat(columns)
|
|
40
|
-
}, [columns])
|
|
41
|
-
|
|
42
|
-
const { transformedColumns, firstLevelHeaders } = useMemo(() => {
|
|
43
|
-
return transformColumnsForTwoLevel(rebuiltColumns)
|
|
44
|
-
}, [rebuiltColumns])
|
|
45
|
-
|
|
46
|
-
const visibleColumns = useMemo(() => {
|
|
47
|
-
return transformedColumns.filter(c => c.show !== false)
|
|
48
|
-
}, [transformedColumns])
|
|
49
|
-
|
|
50
|
-
const {
|
|
51
|
-
getTableProps,
|
|
52
|
-
headerGroups,
|
|
53
|
-
getTableBodyProps,
|
|
54
|
-
rows,
|
|
55
|
-
prepareRow
|
|
56
|
-
} = useTable({
|
|
57
|
-
columns: visibleColumns,
|
|
58
|
-
data,
|
|
59
|
-
defaultColumn
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
let totalWidth = 0
|
|
63
|
-
if (rows[0] && rows[0].cells.length > 0) {
|
|
64
|
-
prepareRow(rows[0])
|
|
65
|
-
const last = rows[0].cells[rows[0].cells.length - 1].column
|
|
66
|
-
totalWidth = last.totalLeft + last.totalWidth
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const gtp = getTableProps()
|
|
70
|
-
const tableStyle =
|
|
71
|
-
totalWidth > 0
|
|
72
|
-
? { width: '100%', minWidth: totalWidth + 'px' }
|
|
73
|
-
: { width: '100%' }
|
|
74
|
-
|
|
75
|
-
const tableProps = {
|
|
76
|
-
...gtp,
|
|
77
|
-
style: tableStyle,
|
|
78
|
-
className: classNames(
|
|
79
|
-
'gm-table-x-table',
|
|
80
|
-
'gm-table-x-table-two-level',
|
|
81
|
-
gtp.className
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const gtbp = getTableBodyProps()
|
|
86
|
-
const tableBodyProps = {
|
|
87
|
-
...gtbp,
|
|
88
|
-
className: 'gm-table-x-tbody'
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const handleScroll = e => {
|
|
92
|
-
onScroll && onScroll(e)
|
|
93
|
-
afterScroll()
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const RenderRow = ({ index, style }) => {
|
|
97
|
-
const row = rows[index]
|
|
98
|
-
prepareRow(row)
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
<Tr
|
|
102
|
-
key={row.index}
|
|
103
|
-
row={row}
|
|
104
|
-
SubComponent={SubComponent}
|
|
105
|
-
keyField={keyField}
|
|
106
|
-
style={style}
|
|
107
|
-
totalWidth={totalWidth}
|
|
108
|
-
isTrDisable={isTrDisable}
|
|
109
|
-
isTrHighlight={isTrHighlight}
|
|
110
|
-
/>
|
|
111
|
-
)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return (
|
|
115
|
-
<div
|
|
116
|
-
{...rest}
|
|
117
|
-
className={classNames(
|
|
118
|
-
'gm-table-x',
|
|
119
|
-
{
|
|
120
|
-
'gm-table-x-empty': data.length === 0,
|
|
121
|
-
'gm-table-x-tiled': tiled
|
|
122
|
-
},
|
|
123
|
-
className
|
|
124
|
-
)}
|
|
125
|
-
onScroll={handleScroll}
|
|
126
|
-
>
|
|
127
|
-
<table {...tableProps}>
|
|
128
|
-
<TwoLevelTHead
|
|
129
|
-
headerGroups={headerGroups}
|
|
130
|
-
firstLevelHeaders={firstLevelHeaders}
|
|
131
|
-
totalWidth={totalWidth}
|
|
132
|
-
/>
|
|
133
|
-
<tbody {...tableBodyProps}>
|
|
134
|
-
{_.map(rows, row =>
|
|
135
|
-
RenderRow({
|
|
136
|
-
index: row.index,
|
|
137
|
-
style: {}
|
|
138
|
-
})
|
|
139
|
-
)}
|
|
140
|
-
</tbody>
|
|
141
|
-
</table>
|
|
142
|
-
{loading && <Loading style={{ marginTop: '92px' }} />}
|
|
143
|
-
{!loading && data.length === 0 && <Empty />}
|
|
144
|
-
</div>
|
|
145
|
-
)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
TwoLevelTableX.propTypes = {
|
|
149
|
-
columns: PropTypes.array.isRequired,
|
|
150
|
-
data: PropTypes.array.isRequired,
|
|
151
|
-
loading: PropTypes.bool,
|
|
152
|
-
SubComponent: PropTypes.func,
|
|
153
|
-
keyField: PropTypes.string,
|
|
154
|
-
tiled: PropTypes.bool,
|
|
155
|
-
isTrDisable: PropTypes.func,
|
|
156
|
-
isTrHighlight: PropTypes.func,
|
|
157
|
-
onScroll: PropTypes.func,
|
|
158
|
-
className: PropTypes.string,
|
|
159
|
-
style: PropTypes.object
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
TwoLevelTableX.defaultProps = {
|
|
163
|
-
keyField: 'value',
|
|
164
|
-
tiled: false,
|
|
165
|
-
isTrDisable: () => false,
|
|
166
|
-
isTrHighlight: () => false
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export default TwoLevelTableX
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import classNames from 'classnames'
|
|
2
|
-
import { getColumnStyle } from '../../util'
|
|
3
|
-
import PropTypes from 'prop-types'
|
|
4
|
-
import React from 'react'
|
|
5
|
-
|
|
6
|
-
// cell.render('Cell') 是一个react组件,如果这个组件return undefined,那就就会报错
|
|
7
|
-
// 这里是为了兼容 cell.render('Cell') 返回undefined的情况
|
|
8
|
-
class TdCatchErr extends React.Component {
|
|
9
|
-
state = {
|
|
10
|
-
hasError: false
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
static getDerivedStateFromError(error) {
|
|
14
|
-
// 捕获子组件树中的错误,返回新的 state
|
|
15
|
-
return { hasError: true }
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
componentDidCatch(error, errorInfo) {
|
|
19
|
-
// 静默处理错误,不打印日志
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
render() {
|
|
23
|
-
if (this.state.hasError) {
|
|
24
|
-
return null
|
|
25
|
-
}
|
|
26
|
-
const children = this.props.children
|
|
27
|
-
// 如果 children 是 undefined 或 null,返回 null(React 允许返回 null)
|
|
28
|
-
if (children === undefined || children === null) {
|
|
29
|
-
return null
|
|
30
|
-
}
|
|
31
|
-
return children
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// 创建一个包装组件,用于包装 Cell 组件的 type,确保它总是返回有效值
|
|
36
|
-
// 这个组件会包装原始的 Cell 组件,如果原始组件返回 undefined,就返回 null
|
|
37
|
-
const SafeCellWrapper = OriginalComponent => {
|
|
38
|
-
return function WrappedCell(props) {
|
|
39
|
-
try {
|
|
40
|
-
const result = OriginalComponent(props)
|
|
41
|
-
// 如果原始组件返回 undefined,返回 null
|
|
42
|
-
return result === undefined ? null : result
|
|
43
|
-
} catch (error) {
|
|
44
|
-
return null
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// 错误边界组件,专门用于捕获 Cell 组件返回 undefined 的错误
|
|
50
|
-
class SafeCellErrorBoundary extends React.Component {
|
|
51
|
-
state = {
|
|
52
|
-
hasError: false
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
static getDerivedStateFromError(error) {
|
|
56
|
-
return { hasError: true }
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
componentDidCatch(error, errorInfo) {
|
|
60
|
-
// 静默处理错误
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
render() {
|
|
64
|
-
if (this.state.hasError) {
|
|
65
|
-
return null
|
|
66
|
-
}
|
|
67
|
-
return this.props.children
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const Td = ({ cell, totalWidth }) => {
|
|
72
|
-
const cp = cell.getCellProps()
|
|
73
|
-
const { tdClassName } = cell.column
|
|
74
|
-
const tdProps = {
|
|
75
|
-
...cp,
|
|
76
|
-
className: classNames('gm-table-x-td', tdClassName, {
|
|
77
|
-
'gm-table-x-fixed-left': cell.column.fixed === 'left',
|
|
78
|
-
'gm-table-x-fixed-right': cell.column.fixed === 'right'
|
|
79
|
-
}),
|
|
80
|
-
style: {
|
|
81
|
-
...cp.style,
|
|
82
|
-
...getColumnStyle(cell.column)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (cell.column.fixed === 'left') {
|
|
87
|
-
// 用到 fixed,可以利用 totalLeft
|
|
88
|
-
tdProps.style.left = cell.column.totalLeft
|
|
89
|
-
} else if (cell.column.fixed === 'right') {
|
|
90
|
-
tdProps.style.right =
|
|
91
|
-
totalWidth - cell.column.totalLeft - cell.column.totalWidth
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 如果 Cell 返回 undefined,转换为 null(null 不会报错,undefined 会)
|
|
95
|
-
let cellContent = cell.render('Cell')
|
|
96
|
-
|
|
97
|
-
// cellContent 是一个 React 元素,但它的 type(组件)在渲染时可能返回 undefined
|
|
98
|
-
// 我们需要用一个包装组件来替换原始的 type,确保总是返回有效值
|
|
99
|
-
if (cellContent && React.isValidElement(cellContent)) {
|
|
100
|
-
// 创建一个新的 React 元素,用包装组件替换原始的 type
|
|
101
|
-
const OriginalComponent = cellContent.type
|
|
102
|
-
const WrappedComponent = SafeCellWrapper(OriginalComponent)
|
|
103
|
-
cellContent = React.createElement(
|
|
104
|
-
SafeCellErrorBoundary,
|
|
105
|
-
null,
|
|
106
|
-
React.createElement(WrappedComponent, cellContent.props)
|
|
107
|
-
)
|
|
108
|
-
} else if (cellContent === undefined || cellContent === null) {
|
|
109
|
-
cellContent = null
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return (
|
|
113
|
-
<td {...tdProps}>
|
|
114
|
-
<TdCatchErr>{cellContent}</TdCatchErr>
|
|
115
|
-
</td>
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
Td.whyDidYouRender = true
|
|
120
|
-
|
|
121
|
-
Td.propTypes = {
|
|
122
|
-
cell: PropTypes.object.isRequired,
|
|
123
|
-
totalWidth: PropTypes.number.isRequired
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export default React.memo(Td)
|
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
import PropTypes from 'prop-types'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import classNames from 'classnames'
|
|
4
|
-
import Th from '../../base/th'
|
|
5
|
-
import { getColumnStyle, SortHeader } from '../../util'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* 二级表头组件
|
|
9
|
-
* 渲染两级表头:
|
|
10
|
-
* - 第一行:一级表头(有子列的显示一级表头,没有子列的占两行)
|
|
11
|
-
* - 第二行:二级表头(只有有子列的一级表头才显示)
|
|
12
|
-
*/
|
|
13
|
-
const TwoLevelTHead = ({ headerGroups, firstLevelHeaders, totalWidth }) => {
|
|
14
|
-
const hasNestedHeaders = headerGroups.length > 1
|
|
15
|
-
const firstLevelGroup = hasNestedHeaders ? headerGroups[0] : null
|
|
16
|
-
const secondLevelGroup = hasNestedHeaders ? headerGroups[1] : headerGroups[0]
|
|
17
|
-
|
|
18
|
-
// 构建第一级表头的渲染信息
|
|
19
|
-
const firstLevelCells = []
|
|
20
|
-
let secondLevelIndex = 0 // 在第二级表头中的索引
|
|
21
|
-
|
|
22
|
-
firstLevelHeaders.forEach((firstLevelHeader, idx) => {
|
|
23
|
-
if (firstLevelHeader.hasSubColumns) {
|
|
24
|
-
// 有子列:从第二级表头中获取对应的列
|
|
25
|
-
const subColumnCount = firstLevelHeader.subColumnCount
|
|
26
|
-
const subColumns = secondLevelGroup.headers.slice(
|
|
27
|
-
secondLevelIndex,
|
|
28
|
-
secondLevelIndex + subColumnCount
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
// 获取对应的第一级分组列对象
|
|
32
|
-
const groupColumn = firstLevelGroup
|
|
33
|
-
? firstLevelGroup.headers.find(col => {
|
|
34
|
-
// 通过 id 或 Header 匹配
|
|
35
|
-
return (
|
|
36
|
-
col.id === firstLevelHeader.id ||
|
|
37
|
-
(col.columns && col.columns.length === subColumnCount)
|
|
38
|
-
)
|
|
39
|
-
}) || firstLevelGroup.headers[idx]
|
|
40
|
-
: null
|
|
41
|
-
|
|
42
|
-
// 计算分组列的总宽度(所有子列的宽度之和)
|
|
43
|
-
let totalSubWidth = 0
|
|
44
|
-
subColumns.forEach(subCol => {
|
|
45
|
-
const style = getColumnStyle(subCol)
|
|
46
|
-
const width = parseFloat(style.width) || subCol.totalWidth || 0
|
|
47
|
-
totalSubWidth += width
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
firstLevelCells.push({
|
|
51
|
-
type: 'group',
|
|
52
|
-
header: firstLevelHeader,
|
|
53
|
-
colSpan: subColumnCount,
|
|
54
|
-
subColumns: subColumns,
|
|
55
|
-
totalWidth: totalSubWidth, // 添加总宽度
|
|
56
|
-
column: groupColumn,
|
|
57
|
-
secondLevelStartIndex: secondLevelIndex
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
secondLevelIndex += subColumnCount
|
|
61
|
-
} else {
|
|
62
|
-
let singleColumn = null
|
|
63
|
-
|
|
64
|
-
// 优先从 firstLevelGroup 中查找单列(用于 getHeaderProps 等)
|
|
65
|
-
if (firstLevelGroup) {
|
|
66
|
-
singleColumn = firstLevelGroup.headers.find(col => {
|
|
67
|
-
// 通过 id 或 accessor 匹配
|
|
68
|
-
const matchId = col.id === firstLevelHeader.id
|
|
69
|
-
const matchAccessor =
|
|
70
|
-
col.accessor === firstLevelHeader.accessor ||
|
|
71
|
-
(typeof col.accessor === 'function' &&
|
|
72
|
-
typeof firstLevelHeader.accessor === 'function' &&
|
|
73
|
-
col.accessor.toString() === firstLevelHeader.accessor.toString())
|
|
74
|
-
return matchId || matchAccessor
|
|
75
|
-
})
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// 如果 firstLevelGroup 中没找到,从 secondLevelGroup 获取
|
|
79
|
-
if (!singleColumn) {
|
|
80
|
-
singleColumn = secondLevelGroup.headers[secondLevelIndex]
|
|
81
|
-
}
|
|
82
|
-
firstLevelCells.push({
|
|
83
|
-
type: 'single',
|
|
84
|
-
header: firstLevelHeader,
|
|
85
|
-
colSpan: 1,
|
|
86
|
-
rowSpan: 2, // 明确设置为 2,占两行
|
|
87
|
-
column: singleColumn,
|
|
88
|
-
secondLevelIndex: secondLevelIndex
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
secondLevelIndex += 1
|
|
92
|
-
}
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<thead className='gm-table-x-thead gm-table-x-thead-two-level'>
|
|
97
|
-
{/* 第一级表头行 */}
|
|
98
|
-
<tr className='gm-table-x-tr gm-table-x-tr-first-level'>
|
|
99
|
-
{firstLevelCells.map((cell, idx) => {
|
|
100
|
-
const { column, header, colSpan, rowSpan, type } = cell
|
|
101
|
-
|
|
102
|
-
// 对于分组列,使用分组列对象;对于单列,使用单列对象
|
|
103
|
-
const headerColumn = column
|
|
104
|
-
const hp = headerColumn ? headerColumn.getHeaderProps() : {}
|
|
105
|
-
const { thClassName, style } = headerColumn || {}
|
|
106
|
-
|
|
107
|
-
// 计算样式
|
|
108
|
-
let cellStyle = {
|
|
109
|
-
...hp.style,
|
|
110
|
-
...style
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const fixed =
|
|
114
|
-
type === 'group'
|
|
115
|
-
? header.fixed // 分组列直接使用 header.fixed,不 fallback 到 headerColumn
|
|
116
|
-
: headerColumn?.fixed !== undefined
|
|
117
|
-
? headerColumn.fixed
|
|
118
|
-
: header.fixed
|
|
119
|
-
|
|
120
|
-
// 判断是否是最后一列(通过检查是否是 firstLevelCells 的最后一个元素)
|
|
121
|
-
const isLastCell = idx === firstLevelCells.length - 1
|
|
122
|
-
|
|
123
|
-
if (type === 'group') {
|
|
124
|
-
const { flex, width, minWidth, maxWidth, ...restStyle } = cellStyle
|
|
125
|
-
cellStyle = restStyle
|
|
126
|
-
} else if (headerColumn) {
|
|
127
|
-
const columnStyle = getColumnStyle(headerColumn)
|
|
128
|
-
const { flex, ...restColumnStyle } = columnStyle
|
|
129
|
-
cellStyle = {
|
|
130
|
-
...cellStyle,
|
|
131
|
-
...restColumnStyle // 只使用 width 和 maxWidth,不使用 flex
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// 如果有固定列,确保宽度不被压缩
|
|
135
|
-
if (fixed === 'left' || fixed === 'right') {
|
|
136
|
-
const width = restColumnStyle.width
|
|
137
|
-
if (width) {
|
|
138
|
-
cellStyle.minWidth = width
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (fixed === 'left') {
|
|
143
|
-
if (
|
|
144
|
-
type === 'group' &&
|
|
145
|
-
cell.subColumns &&
|
|
146
|
-
cell.subColumns.length > 0
|
|
147
|
-
) {
|
|
148
|
-
cellStyle.left = cell.subColumns[0].totalLeft
|
|
149
|
-
} else if (headerColumn) {
|
|
150
|
-
cellStyle.left = headerColumn.totalLeft
|
|
151
|
-
}
|
|
152
|
-
} else if (fixed === 'right') {
|
|
153
|
-
// 对于分组列,使用最后一个子列的位置
|
|
154
|
-
if (
|
|
155
|
-
type === 'group' &&
|
|
156
|
-
cell.subColumns &&
|
|
157
|
-
cell.subColumns.length > 0
|
|
158
|
-
) {
|
|
159
|
-
const lastSubCol = cell.subColumns[cell.subColumns.length - 1]
|
|
160
|
-
// 如果是最后一列,直接设置 right: 0
|
|
161
|
-
if (isLastCell) {
|
|
162
|
-
cellStyle.right = 0
|
|
163
|
-
} else {
|
|
164
|
-
cellStyle.right =
|
|
165
|
-
totalWidth - lastSubCol.totalLeft - lastSubCol.totalWidth
|
|
166
|
-
}
|
|
167
|
-
} else if (headerColumn) {
|
|
168
|
-
// 如果是最后一列,直接设置 right: 0
|
|
169
|
-
if (isLastCell) {
|
|
170
|
-
cellStyle.right = 0
|
|
171
|
-
} else {
|
|
172
|
-
cellStyle.right =
|
|
173
|
-
totalWidth - headerColumn.totalLeft - headerColumn.totalWidth
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
const {
|
|
178
|
-
colSpan: hpColSpan,
|
|
179
|
-
rowSpan: hpRowSpan,
|
|
180
|
-
key: hpKey,
|
|
181
|
-
...restHp
|
|
182
|
-
} = hp || {}
|
|
183
|
-
const finalRowSpan =
|
|
184
|
-
rowSpan !== undefined ? Number(rowSpan) : undefined
|
|
185
|
-
const finalColSpan =
|
|
186
|
-
colSpan !== undefined
|
|
187
|
-
? Number(colSpan)
|
|
188
|
-
: hpColSpan !== undefined
|
|
189
|
-
? Number(hpColSpan)
|
|
190
|
-
: undefined
|
|
191
|
-
|
|
192
|
-
const thProps = {
|
|
193
|
-
...restHp,
|
|
194
|
-
...(finalColSpan !== undefined && { colSpan: finalColSpan }),
|
|
195
|
-
...(finalRowSpan !== undefined && { rowSpan: finalRowSpan }), // 只有定义了才添加
|
|
196
|
-
className: classNames(
|
|
197
|
-
'gm-table-x-th',
|
|
198
|
-
'gm-table-x-th-first-level',
|
|
199
|
-
hp?.className,
|
|
200
|
-
thClassName,
|
|
201
|
-
{
|
|
202
|
-
'gm-table-x-fixed-left': fixed === 'left',
|
|
203
|
-
'gm-table-x-fixed-right': fixed === 'right'
|
|
204
|
-
}
|
|
205
|
-
),
|
|
206
|
-
style: cellStyle
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// 生成唯一的 key:结合 id/accessor 和索引,确保即使 id/accessor 相同,key 也是唯一的
|
|
210
|
-
// 如果有 hpKey,也加上索引以确保唯一性(因为 hpKey 可能也会重复)
|
|
211
|
-
const baseKey = hpKey || header.id || header.accessor
|
|
212
|
-
const headerKey = baseKey ? `${baseKey}-${idx}` : `header-${idx}`
|
|
213
|
-
return (
|
|
214
|
-
<th key={headerKey} {...thProps}>
|
|
215
|
-
{typeof header.Header === 'function' ? (
|
|
216
|
-
<header.Header />
|
|
217
|
-
) : (
|
|
218
|
-
header.Header
|
|
219
|
-
)}
|
|
220
|
-
{headerColumn?.canSort && (
|
|
221
|
-
<SortHeader
|
|
222
|
-
{...headerColumn.getSortByToggleProps()}
|
|
223
|
-
type={
|
|
224
|
-
headerColumn.isSorted
|
|
225
|
-
? headerColumn.isSortedDesc
|
|
226
|
-
? 'desc'
|
|
227
|
-
: 'asc'
|
|
228
|
-
: null
|
|
229
|
-
}
|
|
230
|
-
/>
|
|
231
|
-
)}
|
|
232
|
-
</th>
|
|
233
|
-
)
|
|
234
|
-
})}
|
|
235
|
-
</tr>
|
|
236
|
-
|
|
237
|
-
{/* 第二级表头行(只有有子列的情况才显示) */}
|
|
238
|
-
{hasNestedHeaders && secondLevelGroup && (
|
|
239
|
-
<tr className='gm-table-x-tr gm-table-x-tr-second-level'>
|
|
240
|
-
{secondLevelGroup.headers.map((column, idx) => {
|
|
241
|
-
// 找到这个列对应的第一级表头
|
|
242
|
-
const correspondingFirstLevel = firstLevelCells.find(cell => {
|
|
243
|
-
if (cell.type === 'single') {
|
|
244
|
-
return cell.secondLevelIndex === idx
|
|
245
|
-
} else if (cell.type === 'group') {
|
|
246
|
-
return (
|
|
247
|
-
idx >= cell.secondLevelStartIndex &&
|
|
248
|
-
idx < cell.secondLevelStartIndex + cell.colSpan
|
|
249
|
-
)
|
|
250
|
-
}
|
|
251
|
-
return false
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
if (
|
|
255
|
-
correspondingFirstLevel &&
|
|
256
|
-
correspondingFirstLevel.type === 'single'
|
|
257
|
-
) {
|
|
258
|
-
return null
|
|
259
|
-
}
|
|
260
|
-
// 生成唯一的 key:结合 id/accessor 和索引,确保即使 id/accessor 相同,key 也是唯一的
|
|
261
|
-
const columnKey = column.id
|
|
262
|
-
? `${column.id}-${idx}`
|
|
263
|
-
: column.accessor
|
|
264
|
-
? `${column.accessor}-${idx}`
|
|
265
|
-
: `col-${idx}`
|
|
266
|
-
return (
|
|
267
|
-
<Th
|
|
268
|
-
key={columnKey}
|
|
269
|
-
column={{
|
|
270
|
-
...column,
|
|
271
|
-
// column.fixed 已经在 transformColumnsForTwoLevel 中正确设置
|
|
272
|
-
thClassName: classNames(
|
|
273
|
-
column.thClassName,
|
|
274
|
-
'gm-table-x-th-second-level'
|
|
275
|
-
)
|
|
276
|
-
}}
|
|
277
|
-
totalWidth={totalWidth}
|
|
278
|
-
/>
|
|
279
|
-
)
|
|
280
|
-
})}
|
|
281
|
-
</tr>
|
|
282
|
-
)}
|
|
283
|
-
</thead>
|
|
284
|
-
)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
TwoLevelTHead.propTypes = {
|
|
288
|
-
headerGroups: PropTypes.array.isRequired,
|
|
289
|
-
firstLevelHeaders: PropTypes.array.isRequired,
|
|
290
|
-
totalWidth: PropTypes.number.isRequired
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
export default React.memo(TwoLevelTHead)
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import classNames from 'classnames'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import Td from './td'
|
|
5
|
-
|
|
6
|
-
const Tr = ({
|
|
7
|
-
row,
|
|
8
|
-
SubComponent,
|
|
9
|
-
keyField,
|
|
10
|
-
style,
|
|
11
|
-
totalWidth,
|
|
12
|
-
isTrDisable,
|
|
13
|
-
isTrHighlight
|
|
14
|
-
}) => {
|
|
15
|
-
const props = {
|
|
16
|
-
...row.getRowProps(),
|
|
17
|
-
style,
|
|
18
|
-
className: classNames('gm-table-x-tr', {
|
|
19
|
-
'gm-table-x-tr-disable': isTrDisable(row.original, row.index),
|
|
20
|
-
'gm-table-x-tr-highlight': isTrHighlight(row.original, row.index),
|
|
21
|
-
'gm-table-x-tr-odd': row.index % 2 === 0,
|
|
22
|
-
'gm-table-x-tr-even': row.index % 2 !== 0
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// 目前视为了 sortable 用。值可能是 undefined,keyField 没作用的情况
|
|
27
|
-
const dataId = row.original[keyField]
|
|
28
|
-
|
|
29
|
-
return (
|
|
30
|
-
<>
|
|
31
|
-
<tr data-id={dataId} {...props}>
|
|
32
|
-
{row.cells.map((cell, cellIndex) => (
|
|
33
|
-
<Td key={cellIndex} cell={cell} totalWidth={totalWidth} />
|
|
34
|
-
))}
|
|
35
|
-
</tr>
|
|
36
|
-
{SubComponent && SubComponent(row)}
|
|
37
|
-
</>
|
|
38
|
-
)
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
Tr.whyDidYouRender = true
|
|
42
|
-
|
|
43
|
-
Tr.propTypes = {
|
|
44
|
-
row: PropTypes.object.isRequired,
|
|
45
|
-
SubComponent: PropTypes.func,
|
|
46
|
-
keyField: PropTypes.string.isRequired,
|
|
47
|
-
style: PropTypes.object.isRequired,
|
|
48
|
-
totalWidth: PropTypes.number.isRequired,
|
|
49
|
-
isTrDisable: PropTypes.func,
|
|
50
|
-
isTrHighlight: PropTypes.func
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export default React.memo(Tr)
|