273 lines
7.3 KiB
TypeScript
273 lines
7.3 KiB
TypeScript
import React, { useMemo, useCallback, useState, useRef } from 'react';
|
||
import {
|
||
Button,
|
||
message,
|
||
Modal,
|
||
Space,
|
||
Divider,
|
||
AlertProps,
|
||
Alert,
|
||
Typography,
|
||
Tooltip,
|
||
} from 'antd';
|
||
import { PlusOutlined } from '@ant-design/icons';
|
||
import ProTable, { ProColumns, ActionType, ProTableProps } from '@ant-design/pro-table';
|
||
import { Access, useAccess } from 'umi';
|
||
|
||
export interface toolBarActionsItem {
|
||
type: 'add' | 'batchDelete';
|
||
permission?: string;
|
||
text?: string;
|
||
onConfirm: (val?: string[]) => void;
|
||
}
|
||
export interface PropsType extends ProTableProps<any, any> {
|
||
toolBarActions?: toolBarActionsItem[];
|
||
alertProps?: AlertProps;
|
||
indexColumn?: boolean | ProColumns<any, 'text'>;
|
||
}
|
||
|
||
type Record<K extends keyof any, T> = {
|
||
[P in K]: T;
|
||
};
|
||
|
||
const Table = <T extends Record<string, any>>(props: PropsType) => {
|
||
const access = useAccess();
|
||
|
||
const {
|
||
columns: columnsProps = [],
|
||
search: searchProps = {},
|
||
pagination: paginationProps,
|
||
options = false,
|
||
toolBarRender: toolBarRenderProps,
|
||
toolBarActions,
|
||
actionRef: actionRefProps,
|
||
rowSelection: rowSelectionProps,
|
||
toolbar: toolbarProps,
|
||
alertProps,
|
||
indexColumn,
|
||
} = props;
|
||
const defaultRef = useRef<ActionType>();
|
||
const actionRef: any = actionRefProps || defaultRef;
|
||
const [selectedRows, setSelectedRows] = useState<React.Key[]>([]);
|
||
|
||
// 暂时有批量删除的话,替换rowSelection,后续有发现问题再改
|
||
const rowSelection = useMemo(() => {
|
||
if (toolBarActions && toolBarActions.findIndex((item) => item.type === 'batchDelete') > -1) {
|
||
return {
|
||
selectedRowKeys: selectedRows,
|
||
onChange: setSelectedRows,
|
||
preserveSelectedRowKeys: true,
|
||
};
|
||
}
|
||
return rowSelectionProps
|
||
? {
|
||
preserveSelectedRowKeys: true,
|
||
...rowSelectionProps,
|
||
}
|
||
: false;
|
||
}, [rowSelectionProps, toolBarActions, setSelectedRows, selectedRows]);
|
||
|
||
const indexColumnValue = useMemo(() => {
|
||
if (!indexColumn) return [];
|
||
const rankItem = [
|
||
{
|
||
title: '序号',
|
||
dataIndex: 'index',
|
||
width: 80,
|
||
align: 'center',
|
||
search: false,
|
||
render: (_: any, item: any, index: number) => {
|
||
// @ts-ignore
|
||
const { current, pageSize } = actionRef.current?.pageInfo;
|
||
return index + 1 + (current - 1) * pageSize;
|
||
},
|
||
...(typeof indexColumn === 'object' ? indexColumn : {}),
|
||
},
|
||
];
|
||
return rankItem;
|
||
}, [indexColumn, actionRef]);
|
||
|
||
const columns = useMemo(() => {
|
||
const newColumns = columnsProps.map((item) => {
|
||
const optionRender = (_: any, val: any, ...rest: any) => {
|
||
return (
|
||
<Space size={0} split={<Divider type="vertical" />}>
|
||
{(item.render && (item.render(_, val, ...rest) as []))?.map((item2) => {
|
||
return item2;
|
||
})}
|
||
</Space>
|
||
);
|
||
};
|
||
let { render } = item;
|
||
if (item.valueType === 'option') {
|
||
render = optionRender;
|
||
}
|
||
return {
|
||
...item,
|
||
fieldProps:
|
||
item.valueType === 'select'
|
||
? {
|
||
...item.fieldProps,
|
||
showSearch: (item.fieldProps as any)?.options?.length > 8,
|
||
}
|
||
: item.fieldProps,
|
||
render,
|
||
};
|
||
});
|
||
|
||
return indexColumnValue.concat(newColumns);
|
||
}, [columnsProps, indexColumnValue]);
|
||
|
||
// search属性
|
||
const search = useMemo(() => {
|
||
if (searchProps === false) {
|
||
return searchProps;
|
||
}
|
||
return {
|
||
...searchProps,
|
||
optionRender: ({ searchText, resetText }, { form }) => [
|
||
<Button
|
||
key="search"
|
||
type="primary"
|
||
onClick={() => {
|
||
form?.submit();
|
||
}}
|
||
>
|
||
{searchText}
|
||
</Button>,
|
||
<Button
|
||
key="rest"
|
||
onClick={() => {
|
||
form?.resetFields();
|
||
form?.submit();
|
||
props.onReset && props.onReset();
|
||
}}
|
||
>
|
||
{resetText}
|
||
</Button>,
|
||
],
|
||
};
|
||
}, [searchProps, columnsProps]);
|
||
|
||
const handleBatchDelete = (action: any) => {
|
||
if (selectedRows.length === 0) {
|
||
message.warn('请选择要操作的项');
|
||
return;
|
||
}
|
||
Modal.confirm({
|
||
title: '提示',
|
||
content: '是否确认删除?',
|
||
okText: '确认',
|
||
cancelText: '取消',
|
||
onOk: () => {
|
||
action(selectedRows);
|
||
},
|
||
});
|
||
};
|
||
// 渲染toolbarActions自定义按钮
|
||
const toolBarActionsRender = useCallback(() => {
|
||
const buttonList: any = [];
|
||
toolBarActions &&
|
||
toolBarActions.forEach((item) => {
|
||
if (item.type === 'add') {
|
||
buttonList.push(
|
||
<Access accessible={access.canShowButton(item.permission)}>
|
||
<Button
|
||
key="add"
|
||
type="primary"
|
||
onClick={() => {
|
||
item.onConfirm();
|
||
}}
|
||
>
|
||
<PlusOutlined />
|
||
{item.text || '添加'}
|
||
</Button>
|
||
</Access>,
|
||
);
|
||
} else if (item.type === 'batchDelete') {
|
||
buttonList.push(
|
||
<Access accessible={access.canShowButton(item.permission)}>
|
||
<Button
|
||
key="del"
|
||
danger
|
||
onClick={() => {
|
||
handleBatchDelete(item.onConfirm);
|
||
}}
|
||
>
|
||
{item.text || '批量删除'}
|
||
</Button>
|
||
</Access>,
|
||
);
|
||
}
|
||
});
|
||
return buttonList;
|
||
}, [toolBarActions, selectedRows]);
|
||
|
||
// 渲染toolbar
|
||
const toolBarRender = useCallback(
|
||
(
|
||
action,
|
||
rows: {
|
||
selectedRowKeys?: (string | number)[];
|
||
selectedRows?: T[];
|
||
},
|
||
) => {
|
||
let toolRender = toolBarRenderProps ? toolBarRenderProps(action, rows) : toolBarRenderProps;
|
||
if (toolBarActions) {
|
||
const buttonList: any = toolBarActionsRender();
|
||
toolRender = toolRender ? toolRender.concat(buttonList) : buttonList;
|
||
}
|
||
return toolRender;
|
||
},
|
||
[toolBarRenderProps, toolBarActions, toolBarActionsRender],
|
||
);
|
||
// 分页统一为10条
|
||
const pagination = useMemo(() => {
|
||
if (paginationProps === undefined) {
|
||
return { defaultCurrent: 1, defaultPageSize: 10, pageSize: 10, current: 1 };
|
||
}
|
||
return paginationProps;
|
||
}, [paginationProps]);
|
||
|
||
// 渲染tip,用toolbar的filter
|
||
const toolbar = useMemo(() => {
|
||
if (alertProps) {
|
||
return {
|
||
...toolbarProps,
|
||
multipleLine: true,
|
||
filter: <Alert {...alertProps} />,
|
||
};
|
||
}
|
||
return toolbarProps;
|
||
}, [alertProps, toolbarProps]);
|
||
|
||
return (
|
||
<div className="ls-table">
|
||
<ProTable<T>
|
||
{...{
|
||
...props,
|
||
search,
|
||
pagination,
|
||
columns,
|
||
rowSelection,
|
||
options,
|
||
toolbar,
|
||
toolBarRender: toolBarRenderProps || toolBarActions ? toolBarRender : toolBarRenderProps,
|
||
actionRef,
|
||
onLoad: (res) => {
|
||
if (res.length === 0 && actionRef?.current.pageInfo.current !== 1) {
|
||
actionRef?.current.setPageInfo({
|
||
...actionRef?.current.pageInfo,
|
||
current: actionRef?.current.pageInfo.current - 1,
|
||
});
|
||
}
|
||
},
|
||
}}
|
||
/>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export type { ProColumns, ActionType, ProTableProps };
|
||
export default Table;
|