You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
305 lines
8.5 KiB
305 lines
8.5 KiB
import React, {
|
|
useState,
|
|
forwardRef,
|
|
useImperativeHandle,
|
|
useRef,
|
|
ReactNode,
|
|
FC
|
|
} from 'react'
|
|
import { Table } from 'antd'
|
|
import useService from '@/utils/tableHook'
|
|
import SearchView from '@/components/SearchForm'
|
|
|
|
/**
|
|
* 封装列表、分页、多选、搜索组件
|
|
* @param {RefType} ref 表格的实例,用于调用内部方法
|
|
* @param {object[]} columns 表格列的配置
|
|
* @param {function} apiFun 表格数据的请求方法
|
|
* @param {object[]} searchConfigList 搜索栏配置
|
|
* @param {function} beforeSearch 搜索前的操作(如处理一些特殊数据)
|
|
* @param {function} onFieldsChange 处理搜索栏表单联动事件
|
|
* @param {object} extraProps 额外的搜索参数(不在搜索配置内的)
|
|
* @param {function} onSelectRow 复选框操作回调
|
|
* @param {string} rowKey 表格行的key
|
|
* @param {function} sortConfig 自定义表格排序字段
|
|
* @param {function} expandedRowRender 额外的展开行
|
|
* @param {function} onExpand 点击展开图标时触发
|
|
* @param {string} rowClassName 表格行的样式名
|
|
* @param {boolean} small 表格和分页的展示大小
|
|
* @param {string[]} extraPagation 额外的分页大小
|
|
*/
|
|
|
|
interface TableProps {
|
|
columns: object[];
|
|
apiFun: (arg0?: unknown[]) => Promise<{}>;
|
|
ref?: RefType;
|
|
searchConfigList?: object[];
|
|
extraProps?: object;
|
|
rowKey?: string;
|
|
rowClassName?: string;
|
|
small?: boolean;
|
|
showHeader?: boolean;
|
|
extraPagation?: string[];
|
|
beforeSearch?: (arg0?: unknown) => void;
|
|
onSelectRow?: (arg0?: string[], arg1?: string[]) => void;
|
|
onFieldsChange?: (arg0?: unknown, arg1?: unknown) => void;
|
|
sortConfig?: (arg0?: object) => any;
|
|
expandedRowRender?: () => ReactNode;
|
|
onExpand?: () => void;
|
|
header?: JSX.Element
|
|
}
|
|
|
|
const MyTable: FC<TableProps> = forwardRef(
|
|
(props: TableProps, ref: RefType) => {
|
|
/**
|
|
* @forwardRef
|
|
* 引用父组件的ref实例,成为子组件的一个参数
|
|
* 可以引用父组件的ref绑定到子组件自身的节点上.
|
|
*/
|
|
const searchForm: RefType = useRef(null)
|
|
const {
|
|
columns,
|
|
apiFun,
|
|
searchConfigList,
|
|
extraProps,
|
|
rowKey,
|
|
rowClassName,
|
|
small,
|
|
showHeader,
|
|
extraPagation,
|
|
beforeSearch,
|
|
onSelectRow,
|
|
onFieldsChange,
|
|
sortConfig,
|
|
expandedRowRender,
|
|
onExpand,
|
|
header
|
|
} = props
|
|
|
|
// 搜索参数,如果有特殊需要处理的参数,就处理
|
|
const searchObj = searchConfigList.reduce(
|
|
(prev: CommonObjectType, next: CommonObjectType) => {
|
|
return Object.assign(prev, {
|
|
[next.key]: next.fn ? next.fn(next.initialValue) : next.initialValue
|
|
})
|
|
},
|
|
{}
|
|
)
|
|
|
|
// 初始参数
|
|
const initParams = {
|
|
...searchObj,
|
|
...extraProps,
|
|
page: 1,
|
|
page_size: 20
|
|
}
|
|
|
|
// 多选框的选择值
|
|
const [selectedKeys, setSelectedKeys] = useState([])
|
|
// 列表所有的筛选参数(包括搜索、分页、排序等)
|
|
const [tableParams, setTableParams] = useState(initParams)
|
|
// 列表搜索参数
|
|
const [searchParams, setSearchParams] = useState(searchObj)
|
|
// 列表排序参数
|
|
const [sortParams, setSortParams] = useState({})
|
|
// 列表分页参数
|
|
const [curPageNo, setCurPageNo] = useState(initParams.page)
|
|
const [curPageSize, setCurPageSize] = useState(initParams.page_size)
|
|
|
|
const { loading = false, response = {} }: CommonObjectType = useService(
|
|
apiFun,
|
|
tableParams
|
|
);
|
|
|
|
let tableData = [];
|
|
let total = 0;
|
|
const validData = response?.data ? response.data : [];
|
|
if (Array.isArray(validData) && validData.length >= 0) {
|
|
tableData = validData;
|
|
total = validData.length;
|
|
}
|
|
|
|
if (validData.rows) {
|
|
tableData = validData.rows;
|
|
total = validData.total;
|
|
}
|
|
|
|
// const validData = response?.data ? response.data : {}
|
|
// const { rows: tableData = [], total } = validData
|
|
|
|
// 执行搜索操作
|
|
const handleSearch = (val: CommonObjectType): void => {
|
|
if (val.login) {
|
|
val.login = Number(val.login)
|
|
}
|
|
|
|
const obj = {
|
|
...val
|
|
}
|
|
if (Array.isArray(obj.chain_id)) {
|
|
obj.chain_id = Number(obj.chain_id[0])
|
|
}
|
|
|
|
setSearchParams(obj)
|
|
|
|
setTableParams({ ...tableParams, ...obj, page: 1 })
|
|
}
|
|
|
|
// 重置列表部分状态
|
|
const resetAction = (page?: number): void => {
|
|
setSelectedKeys([])
|
|
const nextPage = page || curPageNo
|
|
const nextParmas = page === 1 ? {} : { ...searchParams, ...sortParams }
|
|
setCurPageNo(nextPage)
|
|
setTableParams({
|
|
...initParams,
|
|
...nextParmas,
|
|
page: nextPage,
|
|
page_size: curPageSize
|
|
})
|
|
}
|
|
|
|
// 列表复选框选中变化
|
|
const onSelectChange = (
|
|
selectedRowKeys: any[],
|
|
selectedRows: any[]
|
|
): void => {
|
|
setSelectedKeys(selectedRowKeys)
|
|
onSelectRow(selectedRowKeys, selectedRows)
|
|
}
|
|
// 复选框配置
|
|
const rowSelection = {
|
|
selectedRowKeys: selectedKeys,
|
|
onChange: onSelectChange
|
|
}
|
|
// 判断是否有复选框显示
|
|
const showCheckbox = onSelectRow ? { rowSelection } : {}
|
|
|
|
// 展开配置
|
|
const expendConfig = {
|
|
expandedRowRender,
|
|
onExpand,
|
|
rowClassName
|
|
}
|
|
// 判断是否有展开行
|
|
const showExpend = expandedRowRender ? expendConfig : {}
|
|
|
|
// 表格和分页的大小
|
|
const tableSize = small ? 'small' : 'middle'
|
|
const pagationSize = small ? 'small' : 'default'
|
|
|
|
// 分页、筛选、排序变化时触发
|
|
const onTableChange = (
|
|
pagination: CommonObjectType,
|
|
filters: CommonObjectType,
|
|
sorter: object
|
|
): void => {
|
|
// 如果有sort排序并且sort参数改变时,优先排序
|
|
const sortObj = sortConfig ? sortConfig(sorter) : {}
|
|
setSortParams(sortObj)
|
|
|
|
const { current: page, pageSize } = pagination
|
|
|
|
setCurPageNo(page)
|
|
setCurPageSize(pageSize)
|
|
setTableParams({
|
|
...initParams,
|
|
...searchParams,
|
|
...sortObj,
|
|
page,
|
|
page_size: pageSize
|
|
})
|
|
}
|
|
|
|
/**
|
|
* @useImperativeHandle
|
|
* 第一个参数,接收一个通过forwardRef引用父组件的ref实例
|
|
* 第二个参数一个回调函数,返回一个对象,对象里面存储需要暴露给父组件的属性或方法
|
|
*/
|
|
useImperativeHandle(ref, () => ({
|
|
// 更新列表
|
|
update(page?: number): void {
|
|
resetAction(page)
|
|
},
|
|
// 更新列表,并重置搜索字段
|
|
resetForm(page?: number): void {
|
|
if (searchForm.current) searchForm.current.resetFields()
|
|
setSearchParams({})
|
|
resetAction(page)
|
|
},
|
|
// 仅重置搜索字段
|
|
resetField(field?: string[]): void {
|
|
return field
|
|
? searchForm.current.resetFields([...field])
|
|
: searchForm.current.resetFields()
|
|
},
|
|
// 获取当前列表数据
|
|
getTableData(): CommonObjectType[] {
|
|
return tableData
|
|
}
|
|
}))
|
|
|
|
return (
|
|
<div>
|
|
{/* 搜索栏 */}
|
|
{searchConfigList.length > 0 && (
|
|
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
<SearchView
|
|
ref={searchForm}
|
|
config={searchConfigList}
|
|
beforeSearch={beforeSearch}
|
|
handleSearch={handleSearch}
|
|
onFieldsChange={onFieldsChange}
|
|
/>
|
|
{header}
|
|
</div>
|
|
)}
|
|
{
|
|
header && searchConfigList.length <= 0 && <div>{header}</div>
|
|
}
|
|
{/* 列表 */}
|
|
<Table
|
|
{...showCheckbox}
|
|
{...showExpend}
|
|
rowKey={rowKey}
|
|
loading={loading}
|
|
dataSource={tableData}
|
|
columns={columns}
|
|
onChange={onTableChange}
|
|
size={tableSize}
|
|
showHeader={showHeader}
|
|
scroll={{ x: 1800 }}
|
|
pagination={{
|
|
size: pagationSize,
|
|
total,
|
|
pageSize: tableParams.page_size,
|
|
current: tableParams.page,
|
|
showQuickJumper: true,
|
|
showSizeChanger: true,
|
|
pageSizeOptions: ['20', '50', '100', '200', ...extraPagation],
|
|
showTotal: (all) => `共 ${all} 条`
|
|
}}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
)
|
|
|
|
MyTable.defaultProps = {
|
|
searchConfigList: [],
|
|
ref: null,
|
|
extraProps: {},
|
|
rowKey: 'id',
|
|
rowClassName: '',
|
|
small: false,
|
|
showHeader: true,
|
|
extraPagation: [],
|
|
beforeSearch: () => { },
|
|
onSelectRow: () => { },
|
|
onFieldsChange: () => { },
|
|
sortConfig: () => { },
|
|
expandedRowRender: null,
|
|
onExpand: () => { }
|
|
}
|
|
|
|
export default MyTable
|