未验证 提交 ccc6e7e9 编写于 作者: Y yangjinjun3 提交者: GitHub

feat: add Table (#185)

上级 be443915
......@@ -719,6 +719,16 @@
"show": true,
"author": "hx"
},
{
"version": "1.0.0",
"name": "Table",
"type": "component",
"cName": "表格",
"desc": "表格",
"sort": 21,
"show": true,
"author": "yangjinjun3"
},
{
"version": "1.0.0",
"name": "Video",
......
import * as React from 'react'
import { useState } from 'react'
import { render, fireEvent } from '@testing-library/react'
import '@testing-library/jest-dom'
import { Table } from '../table'
import Button from '../../button'
import Icon from '../../icon'
const columns = [
{
title: '姓名',
key: 'name',
align: 'center',
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
]
const data = [
{
sex: '',
name: 'Tom',
record: '小学',
},
{
name: 'Lucy',
sex: '',
record: '本科',
},
{
name: 'Jack',
sex: '',
record: '高中',
},
]
const columns2 = [
{
title: '姓名',
key: 'name',
align: 'center',
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
{
title: '操作',
key: 'render',
},
]
const data2 = [
{
name: 'Tom',
sex: '',
record: '小学',
render: () => {
return (
<Button onClick={() => {}} size="small" type="primary">
<div>Hello</div>
</Button>
)
},
},
{
name: 'Lucy',
sex: '',
record: '本科',
render: () => {
return <Icon name="dongdong" size="14px" />
},
},
{
name: 'Jack',
sex: '',
record: '高中',
render: () => {
return (
<Button
type="success"
size="small"
onClick={() => window.open('https://www.jd.com')}
>
<div>跳转到京东</div>
</Button>
)
},
},
]
function sleep(delay = 0): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, delay)
})
}
test('render bordered props', () => {
const { container } = render(<Table columns={columns} data={data} />)
const headBorder = container.querySelector(
'.nut-table__main__head__tr--border'
)
expect(headBorder).toBeInTheDocument()
})
test('render align props', () => {
const { container } = render(<Table columns={columns} data={data} />)
const headTh = container.querySelectorAll('.nut-table__main__head__tr__th')
const bodyTd = container.querySelectorAll('.nut-table__main__body__tr__td')
expect(headTh[0]).toHaveClass('nut-table__main__head__tr--aligncenter')
expect(bodyTd[0]).toHaveClass('nut-table__main__head__tr--aligncenter')
expect(bodyTd[3]).toHaveClass('nut-table__main__head__tr--aligncenter')
expect(bodyTd[6]).toHaveClass('nut-table__main__head__tr--aligncenter')
})
test('show summary', () => {
const { container } = render(
<Table columns={columns} data={data} summary="这是总结栏" />
)
const tableSummary = container.querySelector('.nut-table__summary')
expect(tableSummary).toBeInTheDocument()
})
test('render striped props', () => {
const { container } = render(<Table columns={columns} data={data} striped />)
const tableMain = container.querySelector('.nut-table__main')
expect(tableMain).toHaveClass('nut-table__main--striped')
})
test('render no data', () => {
const { container } = render(<Table columns={columns} data={[]} />)
const tableNoData = container.querySelector('.nut-table__nodata')
expect(tableNoData).toBeInTheDocument()
})
test('render no data of user defined', () => {
const { container } = render(
<Table
columns={columns}
data={[]}
noData={<div className="no-data">这里是自定义展示</div>}
/>
)
const tableNoData = container.querySelector('.no-data')
expect(tableNoData).toHaveTextContent('这里是自定义展示')
})
test('user defined td content', () => {
const { container } = render(<Table columns={columns2} data={data2} />)
const bodyTd = container.querySelectorAll('.nut-table__main__body__tr__td')
const bodyTdButton = bodyTd[3].querySelector('.nut-button')
expect(bodyTdButton).toBeInTheDocument()
})
test('render async', async () => {
const TableDemo = () => {
const [demoData, setDemoData] = useState<Array<any>>([])
return (
<>
<Button data-testid="test" onClick={() => setDemoData(data2)}>
change
</Button>
<Table columns={columns} data={demoData} />
</>
)
}
const { container, getByTestId } = render(<TableDemo />)
expect(container.querySelectorAll('.nut-table__main__body__tr').length).toBe(
0
)
await sleep(2000)
fireEvent.click(getByTestId('test'))
expect(container.querySelectorAll('.nut-table__main__body__tr').length).toBe(
3
)
})
import React, { useState } from 'react'
import Button from '@/packages/button'
import Toast from '@/packages/toast'
import Icon from '@/packages/icon'
import Table from '@/packages/table'
import { TableColumnProps } from './types'
import { useTranslate } from '../../sites/assets/locale'
interface T {
basic: string
borderedAndAlign: string
summaryTitle: string
summary: string
striped: string
noDataTitle: string
customNoData: string
customCell: string
asynchronousRendering: string
sorting: string
}
const TableDemo = () => {
const [translated] = useTranslate<T>({
'zh-CN': {
basic: '基础用法',
borderedAndAlign: '是否显示边框,文字对齐',
summaryTitle: '显示总结栏',
summary: '这是总结栏',
striped: '条纹、明暗交替',
noDataTitle: '无数据默认展示,支持自定义',
customNoData: '这里是自定义展示',
customCell: '自定义单元格',
asynchronousRendering: '支持异步渲染(5s之后看效果)',
sorting: '支持排序',
},
'en-US': {
basic: 'Basic usage',
borderedAndAlign: 'Whether to display border and align text',
summaryTitle: 'Show summary bar',
summary: 'This is the summary column',
striped: 'Stripes, alternating light and shade',
noDataTitle:
'No data is displayed by default, and customization is supported',
customNoData: 'Here is the custom display',
customCell: 'Custom cell',
asynchronousRendering:
'Support asynchronous rendering(See the effect after 5S)',
sorting: 'Support sorting',
},
})
const [columns1, setColumns1] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
},
{
title: '性别',
key: 'sex',
render: (record: any) => {
return (
<span style={{ color: record.sex === '' ? 'blue' : 'green' }}>
{record.sex}
</span>
)
},
},
{
title: '学历',
key: 'record',
},
])
const [columns2, setColumns2] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
align: 'center',
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
])
const [columns4, setColumns4] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
align: 'center',
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
{
title: '操作',
key: 'render',
},
])
const [columns5, setColumns5] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
align: 'center',
sorter: true,
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
{
title: '年龄',
key: 'age',
sorter: (row1: any, row2: any) => {
return row1.age - row2.age
},
},
])
const [data1, setData1] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
},
{
name: 'Lucy',
sex: '',
record: '本科',
},
{
name: 'Jack',
sex: '',
record: '高中',
},
])
const [data2, setData2] = useState([])
const [data3, setData3] = useState([] as any)
const [data4, setData4] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
render: () => {
return (
<Button
onClick={() => Toast.text('hello')}
size="small"
type="primary"
>
<div>Hello</div>
</Button>
)
},
},
{
name: 'Lucy',
sex: '',
record: '本科',
render: () => {
return <Icon name="dongdong" size="14px" />
},
},
{
name: 'Jack',
sex: '',
record: '高中',
render: () => {
return (
<Button
type="success"
size="small"
onClick={() => window.open('https://www.jd.com')}
>
<div>跳转到京东</div>
</Button>
)
},
},
])
const [data5, setData5] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
age: 10,
},
{
name: 'Lucy',
sex: '',
record: '本科',
age: 30,
},
{
name: 'Jack',
sex: '',
record: '高中',
age: 4,
},
])
setTimeout(() => {
setData3(data1)
}, 5000)
const handleSorter = (item: TableColumnProps, data: Array<any>) => {
Toast.text(`${JSON.stringify(item)}`)
setData5([...data])
}
return (
<>
<div className="demo">
<h2>{translated.basic}</h2>
<Table columns={columns1} data={data1} style={{ background: '#fff' }} />
<h2>{translated.borderedAndAlign}</h2>
<Table
columns={columns2}
data={data1}
bordered={false}
style={{ background: '#fff' }}
/>
<h2>{translated.summaryTitle}</h2>
<Table
columns={columns1}
data={data1}
summary={translated.summary}
style={{ background: '#fff' }}
/>
<h2>{translated.striped}</h2>
<Table
columns={columns1}
data={data1}
style={{ background: '#fff' }}
striped
/>
<h2>{translated.noDataTitle}</h2>
<Table columns={columns1} data={data2} style={{ background: '#fff' }} />
<Table
columns={columns1}
data={data2}
style={{ background: '#fff' }}
noData={translated.customNoData}
/>
<h2>{translated.customCell}</h2>
<Table columns={columns4} data={data4} style={{ background: '#fff' }} />
<h2>{translated.asynchronousRendering}</h2>
<Table columns={columns1} data={data3} style={{ background: '#fff' }} />
<h2>{translated.sorting}</h2>
<Table
columns={columns5}
data={data5}
onSorter={handleSorter}
style={{ background: '#fff' }}
/>
</div>
</>
)
}
export default TableDemo
# Table组件
### Intro
Used to display the basic table
### Install
```ts
import { Table } from '@nutui/nutui-react';
```
### Basic Usage
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns1, setColumns1] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
},
{
title: '性别',
key: 'sex',
render: (record: any) => {
return (
<span style={{ color: record.sex === '' ? 'blue' : 'green' }}>
{record.sex}
</span>
)
},
},
{
title: '学历',
key: 'record',
},
])
const [data1, setData1] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
},
{
name: 'Lucy',
sex: '',
record: '本科',
},
{
name: 'Jack',
sex: '',
record: '高中',
},
])
return <Table columns={columns1} data={data1} />;
};
export default App;
```
:::
### Whether to display border and align text
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns2, setColumns2] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
align: 'center',
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
])
const [data1, setData1] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
},
{
name: 'Lucy',
sex: '',
record: '本科',
},
{
name: 'Jack',
sex: '',
record: '高中',
},
])
return <Table
columns={columns2}
data={data1}
bordered={false}
/>;
};
export default App;
```
:::
### Show summary bar
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns1, setColumns1] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
},
{
title: '性别',
key: 'sex',
render: (record: any) => {
return (
<span style={{ color: record.sex === '' ? 'blue' : 'green' }}>
{record.sex}
</span>
)
},
},
{
title: '学历',
key: 'record',
},
])
const [data1, setData1] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
},
{
name: 'Lucy',
sex: '',
record: '本科',
},
{
name: 'Jack',
sex: '',
record: '高中',
},
])
return <Table
columns={columns1}
data={data1}
summary="这是总结栏"
/>;
};
export default App;
```
:::
### Stripes, alternating light and shade
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns1, setColumns1] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
},
{
title: '性别',
key: 'sex',
render: (record: any) => {
return (
<span style={{ color: record.sex === '' ? 'blue' : 'green' }}>
{record.sex}
</span>
)
},
},
{
title: '学历',
key: 'record',
},
])
const [data1, setData1] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
},
{
name: 'Lucy',
sex: '',
record: '本科',
},
{
name: 'Jack',
sex: '',
record: '高中',
},
])
return <Table
columns={columns1}
data={data1}
striped
/>;
};
export default App;
```
:::
### No data is displayed by default, and customization is supported
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns1, setColumns1] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
},
{
title: '性别',
key: 'sex',
render: (record: any) => {
return (
<span style={{ color: record.sex === '' ? 'blue' : 'green' }}>
{record.sex}
</span>
)
},
},
{
title: '学历',
key: 'record',
},
])
const [data2, setData2] = useState([])
return (
<>
<Table columns={columns1} data={data2} />
<Table
columns={columns1}
data={data2}
noData="这里是自定义展示"
/>
</>
);
};
export default App;
```
:::
### Custom cell
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns4, setColumns4] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
align: 'center',
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
{
title: '操作',
key: 'render',
},
])
const [data4, setData4] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
render: () => {
return (
<Button
onClick={() => Toast.text('hello')}
size="small"
type="primary"
>
<div>Hello</div>
</Button>
)
},
},
{
name: 'Lucy',
sex: '',
record: '本科',
render: () => {
return <Icon name="dongdong" size="14px" />
},
},
{
name: 'Jack',
sex: '',
record: '高中',
render: () => {
return (
<Button
type="success"
size="small"
onClick={() => window.open('https://www.jd.com')}
>
<div>跳转到京东</div>
</Button>
)
},
},
])
return <Table columns={columns4} data={data4} />;
};
export default App;
```
:::
### Support asynchronous rendering
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns4, setColumns4] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
align: 'center',
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
{
title: '操作',
key: 'render',
},
])
const [data4, setData4] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
render: () => {
return (
<Button
onClick={() => Toast.text('hello')}
size="small"
type="primary"
>
<div>Hello</div>
</Button>
)
},
},
{
name: 'Lucy',
sex: '',
record: '本科',
render: () => {
return <Icon name="dongdong" size="14px" />
},
},
{
name: 'Jack',
sex: '',
record: '高中',
render: () => {
return (
<Button
type="success"
size="small"
onClick={() => window.open('https://www.jd.com')}
>
<div>跳转到京东</div>
</Button>
)
},
},
])
return <Table columns={columns4} data={data4} />;
};
export default App;
```
:::
### Support sorting
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns4, setColumns4] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
align: 'center',
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
{
title: '操作',
key: 'render',
},
])
const [data4, setData4] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
render: () => {
return (
<Button
onClick={() => Toast.text('hello')}
size="small"
type="primary"
>
<div>Hello</div>
</Button>
)
},
},
{
name: 'Lucy',
sex: '',
record: '本科',
render: () => {
return <Icon name="dongdong" size="14px" />
},
},
{
name: 'Jack',
sex: '',
record: '高中',
render: () => {
return (
<Button
type="success"
size="small"
onClick={() => window.open('https://www.jd.com')}
>
<div>跳转到京东</div>
</Button>
)
},
},
])
return <Table columns={columns4} data={data4} />;
};
export default App;
```
:::
## API
### Props
| Attribute | Description | Type | Default |
|--------------|----------------------------------|--------|------------------|
| bordered | Show border | Boolean | true |
| columns | Header data | TableColumnProps[] | [] |
| data | Table data | Object[] | [] |
| summary | Show profile | React.ReactNode | - |
| striped | Whether the stripes alternate light and dark | Boolean | false |
| noData | Custom noData | React.ReactNode | - |
### TableColumnProps
| Attribute | Description | Type | Default |
|--------------|----------------------------------|--------|------------------|
| key | Unique identification of the column | String | '' |
| title | Header title | String | '' |
| align |Alignment of columns, optional values`left`,`center`,`right` | String | left |
| sorter | sort,optional values `true`,`function`, `default`, Where `default` means that you may depend on the interface after clicking, `function` you can return a specific sorting function, `default` indicates that the default sorting algorithm is adopted | Boolean、Function、String | - |
| render | Custom render column data, high priority | Function(record) | - |
### Events
| Event | Description | Arguments |
|--------|----------------|--------------|
| onSorter | Click the sort button to trigger | item: TableColumnProps, data: Array<any> |
# Table组件
### 介绍
用于展示基础表格
### 安装
```ts
import { Table } from '@nutui/nutui-react';
```
## 代码演示
### 基本用法
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns1, setColumns1] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
},
{
title: '性别',
key: 'sex',
render: (record: any) => {
return (
<span style={{ color: record.sex === '' ? 'blue' : 'green' }}>
{record.sex}
</span>
)
},
},
{
title: '学历',
key: 'record',
},
])
const [data1, setData1] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
},
{
name: 'Lucy',
sex: '',
record: '本科',
},
{
name: 'Jack',
sex: '',
record: '高中',
},
])
return <Table columns={columns1} data={data1} />;
};
export default App;
```
:::
### 是否显示边框,文字对齐
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns2, setColumns2] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
align: 'center',
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
])
const [data1, setData1] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
},
{
name: 'Lucy',
sex: '',
record: '本科',
},
{
name: 'Jack',
sex: '',
record: '高中',
},
])
return <Table
columns={columns2}
data={data1}
bordered={false}
/>;
};
export default App;
```
:::
### 显示总结栏
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns1, setColumns1] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
},
{
title: '性别',
key: 'sex',
render: (record: any) => {
return (
<span style={{ color: record.sex === '' ? 'blue' : 'green' }}>
{record.sex}
</span>
)
},
},
{
title: '学历',
key: 'record',
},
])
const [data1, setData1] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
},
{
name: 'Lucy',
sex: '',
record: '本科',
},
{
name: 'Jack',
sex: '',
record: '高中',
},
])
return <Table
columns={columns1}
data={data1}
summary="这是总结栏"
/>;
};
export default App;
```
:::
### 条纹、明暗交替
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns1, setColumns1] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
},
{
title: '性别',
key: 'sex',
render: (record: any) => {
return (
<span style={{ color: record.sex === '' ? 'blue' : 'green' }}>
{record.sex}
</span>
)
},
},
{
title: '学历',
key: 'record',
},
])
const [data1, setData1] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
},
{
name: 'Lucy',
sex: '',
record: '本科',
},
{
name: 'Jack',
sex: '',
record: '高中',
},
])
return <Table
columns={columns1}
data={data1}
striped
/>;
};
export default App;
```
:::
### 无数据默认展示,支持自定义
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns1, setColumns1] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
},
{
title: '性别',
key: 'sex',
render: (record: any) => {
return (
<span style={{ color: record.sex === '' ? 'blue' : 'green' }}>
{record.sex}
</span>
)
},
},
{
title: '学历',
key: 'record',
},
])
const [data2, setData2] = useState([])
return (
<>
<Table columns={columns1} data={data2} />
<Table
columns={columns1}
data={data2}
noData="这里是自定义展示"
/>
</>
);
};
export default App;
```
:::
### 自定义单元格
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns4, setColumns4] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
align: 'center',
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
{
title: '操作',
key: 'render',
},
])
const [data4, setData4] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
render: () => {
return (
<Button
onClick={() => Toast.text('hello')}
size="small"
type="primary"
>
<div>Hello</div>
</Button>
)
},
},
{
name: 'Lucy',
sex: '',
record: '本科',
render: () => {
return <Icon name="dongdong" size="14px" />
},
},
{
name: 'Jack',
sex: '',
record: '高中',
render: () => {
return (
<Button
type="success"
size="small"
onClick={() => window.open('https://www.jd.com')}
>
<div>跳转到京东</div>
</Button>
)
},
},
])
return <Table columns={columns4} data={data4} />;
};
export default App;
```
:::
### 支持异步渲染(5s之后看效果)
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns4, setColumns4] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
align: 'center',
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
{
title: '操作',
key: 'render',
},
])
const [data4, setData4] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
render: () => {
return (
<Button
onClick={() => Toast.text('hello')}
size="small"
type="primary"
>
<div>Hello</div>
</Button>
)
},
},
{
name: 'Lucy',
sex: '',
record: '本科',
render: () => {
return <Icon name="dongdong" size="14px" />
},
},
{
name: 'Jack',
sex: '',
record: '高中',
render: () => {
return (
<Button
type="success"
size="small"
onClick={() => window.open('https://www.jd.com')}
>
<div>跳转到京东</div>
</Button>
)
},
},
])
return <Table columns={columns4} data={data4} />;
};
export default App;
```
:::
### 支持排序
:::demo
```tsx
import React, { useState } from "react";
import { Table } from '@nutui/nutui-react';
interface TableColumnProps {
key?: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
const App = () => {
const [columns4, setColumns4] = useState<Array<TableColumnProps>>([
{
title: '姓名',
key: 'name',
align: 'center',
},
{
title: '性别',
key: 'sex',
},
{
title: '学历',
key: 'record',
},
{
title: '操作',
key: 'render',
},
])
const [data4, setData4] = useState([
{
name: 'Tom',
sex: '',
record: '小学',
render: () => {
return (
<Button
onClick={() => Toast.text('hello')}
size="small"
type="primary"
>
<div>Hello</div>
</Button>
)
},
},
{
name: 'Lucy',
sex: '',
record: '本科',
render: () => {
return <Icon name="dongdong" size="14px" />
},
},
{
name: 'Jack',
sex: '',
record: '高中',
render: () => {
return (
<Button
type="success"
size="small"
onClick={() => window.open('https://www.jd.com')}
>
<div>跳转到京东</div>
</Button>
)
},
},
])
return <Table columns={columns4} data={data4} />;
};
export default App;
```
:::
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| bordered | 是否显示边框 | Boolean | true |
| columns | 表头数据 | TableColumnProps[] | [] |
| data | 表格数据 | Object[] | [] |
| summary | 是否显示简介 | React.ReactNode | - |
| striped | 条纹是否明暗交替 | Boolean | false |
| noData | 自定义无数据 | React.ReactNode | - |
### TableColumnProps
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| key | 列的唯一标识 | String | '' |
| title | 表头标题 | String | '' |
| align | 列的对齐方式,可选值left,center,right | String | left |
| sorter | 排序,可选值有 true,function, default, 其中 default表示点击之后可能会依赖接口, function可以返回具体的排序函数, default表示采用默认的排序算法 | Boolean、Function、String | - |
| render | 自定义渲染列数据,优先级高 | Function(record) | - |
### Events
| 事件名 | 说明 | 回调参数 |
|--------|----------------|--------------|
| onSorter | 点击排序按钮触发 | item: TableColumnProps, data: Array<any> |
import { Table } from './table'
export default Table
.nut-table {
display: flex;
width: 100%;
flex-direction: column;
font-size: $font-size-2;
&__main {
display: table;
width: 100%;
border-collapse: collapse;
overflow-x: hidden;
&--striped {
.nut-table__main__head {
&__tr {
background-color: $table-tr-even-bg-color;
}
}
.nut-table__main__body {
&__tr:nth-child(odd) {
background-color: $table-tr-odd-bg-color;
}
}
.nut-table__main__body {
&__tr:nth-child(even) {
background-color: $table-tr-even-bg-color;
}
}
}
&__head,
&__body {
&__tr {
display: table-row;
&__th {
display: table-cell;
padding: $table-cols-padding;
}
&__td {
display: table-cell;
padding: $table-cols-padding;
&__nodata {
display: flex;
height: 50px;
align-items: center;
justify-content: center;
}
}
&--border {
border: 1px solid $table-border-color;
}
&--alignleft,
&--align {
text-align: left;
}
&--aligncenter {
text-align: center;
}
&--alignright {
text-align: right;
}
}
}
&__head {
display: table-header-group;
}
&__body {
display: table-row-group;
}
}
&__summary {
display: flex;
align-items: center;
height: 30px;
padding: $table-cols-padding;
}
&__nodata {
display: flex;
align-items: center;
justify-content: center;
height: 30px;
padding: $table-cols-padding;
}
}
import React, { FunctionComponent, useEffect, useState } from 'react'
import classNames from 'classnames'
import bem from '@/utils/bem'
import Icon from '@/packages/icon'
import { TableProps, TableColumnProps } from './types'
import { useConfig } from '@/packages/configprovider'
const defaultProps = {
className: '',
style: {},
columns: [],
data: [],
bordered: true,
striped: false,
noData: '无数据',
} as TableProps
export const Table: FunctionComponent<
Partial<TableProps> & React.HTMLAttributes<HTMLDivElement>
> = (props) => {
const { locale } = useConfig()
defaultProps.noData = locale.noData
const {
children,
className,
style,
columns,
data,
bordered,
summary,
striped,
noData,
onSorter,
...rest
} = {
...defaultProps,
...props,
}
const [curData, setCurData] = useState(data)
useEffect(() => {
if (data && String(data) !== String(curData)) {
setCurData(data)
}
}, [data])
const b = bem('table')
const classes = classNames({})
const cls = classNames(b(), classes, className)
const handleSorterClick = (item: TableColumnProps) => {
if (item.sorter) {
onSorter && onSorter(item, curData)
if (typeof item.sorter === 'function') {
setCurData(curData.sort(item.sorter as (a: any, b: any) => number))
} else {
setCurData(item.sorter === 'default' ? curData.sort() : curData)
}
}
}
const cellClasses = (item: TableColumnProps) => {
return {
'nut-table__main__head__tr--border': props.bordered,
[`nut-table__main__head__tr--align${item.align ? item.align : ''}`]: true,
}
}
const getColumnItem = (value: string): TableColumnProps => {
return columns.filter((item: TableColumnProps) => item.key === value)[0]
}
const renderHeadCells = () => {
return columns.map((item, index) => {
return (
<span
className={classNames(
'nut-table__main__head__tr__th',
cellClasses(item)
)}
key={item.key}
onClick={() => handleSorterClick(item)}
>
{item.title}
{item.sorter && <Icon name="down-arrow" size="12px" />}
</span>
)
})
}
const sortDataItem = () => {
return columns.map((columns: any) => {
return [columns.key, columns.render]
})
}
const renderBodyTds = (item: any) => {
return sortDataItem().map(([value, render], index) => {
return (
<span
className={classNames(
'nut-table__main__body__tr__td',
cellClasses(getColumnItem(value))
)}
key={value}
>
{typeof item[value] === 'function' || typeof render === 'function' ? (
<div>{render ? render(item) : item[value](item)}</div>
) : (
item[value]
)}
</span>
)
})
}
const renderBoyTrs = () => {
return curData.map((item, index) => {
return (
<div className="nut-table__main__body__tr" key={index}>
{renderBodyTds(item)}
</div>
)
})
}
return (
<div className={cls} style={style} {...rest}>
<div
className={classNames('nut-table__main', {
'nut-table__main--striped': striped,
})}
>
<div className="nut-table__main__head">
<div className="nut-table__main__head__tr">{renderHeadCells()}</div>
</div>
<div className="nut-table__main__body">{renderBoyTrs()}</div>
</div>
{summary && (
<div className="nut-table__summary">
<span className="nut-table__summary__text">{summary}</span>
</div>
)}
{curData.length === 0 && (
<div className="nut-table__nodata">
<div className="nut-table__nodata">
<div className="nut-table__nodata__text">{noData}</div>
</div>
</div>
)}
</div>
)
}
Table.defaultProps = defaultProps
Table.displayName = 'NutTable'
import React from 'react'
export interface TableProps {
className: string
style: React.CSSProperties
columns: Array<TableColumnProps>
data: Array<any>
bordered: boolean
summary?: React.ReactNode
striped?: boolean
noData?: React.ReactNode
onSorter?: (item: TableColumnProps, data: Array<any>) => void
}
export interface TableColumnProps {
key: string
title?: string
align?: string
sorter?: ((a: any, b: any) => number) | boolean | string
render?: (rowData?: any, rowIndex?: number) => string | React.ReactNode
}
......@@ -495,6 +495,12 @@ $divider-vertical-top: 2px !default;
$divider-vertical-border-left: rgba(0, 0, 0, 0.06) !default;
$divider-vertical-margin: 0 8px !default;
// Table
$table-border-color: #ececec !default;
$table-cols-padding: 10px !default;
$table-tr-even-bg-color: #f3f3f3 !default;
$table-tr-odd-bg-color: $white !default;
//numberkeyboard
$numberkeyboard-width: 100% !default;
$numberkeyboard-padding: 0 0 22px 0 !default;
......@@ -622,4 +628,3 @@ $countup-lr-margin: 1px !default;
$countup-bgcolor: #031f63 !default;
$countup-color: #ffffff !default;
$countup-width: 24px !default;
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册