feat: 高级列表
This commit is contained in:
294
src/components/EnhancedProTable/index.tsx
Normal file
294
src/components/EnhancedProTable/index.tsx
Normal file
@@ -0,0 +1,294 @@
|
||||
// components/EnhancedProTable/EnhancedProTable.tsx
|
||||
import React, { useRef, useState, useCallback, useMemo } from "react";
|
||||
import {
|
||||
ProTable,
|
||||
ProColumns,
|
||||
ActionType,
|
||||
TableDropdown,
|
||||
ParamsType,
|
||||
} from "@ant-design/pro-components";
|
||||
import { Button, Space } from "antd";
|
||||
import {
|
||||
EnhancedProTableProps,
|
||||
BaseRecord,
|
||||
TableAction,
|
||||
ToolbarAction,
|
||||
} from "./types";
|
||||
import {
|
||||
buildTableDropdownMenuItems,
|
||||
handleTableDropdownSelect,
|
||||
formatPaginationTotal,
|
||||
} from "@/utils/antd/tableHelpers";
|
||||
|
||||
function EnhancedProTable<T extends BaseRecord, U extends ParamsType = any>(
|
||||
props: EnhancedProTableProps<T, U>
|
||||
) {
|
||||
const {
|
||||
columns: originalColumns,
|
||||
request,
|
||||
actions = [],
|
||||
toolbarActions = [],
|
||||
permissions = [],
|
||||
checkPermission = () => true,
|
||||
showIndex = true,
|
||||
showSelection = true,
|
||||
showActions = true,
|
||||
maxActionCount = 2,
|
||||
onAdd,
|
||||
onEdit,
|
||||
onDelete,
|
||||
onView,
|
||||
onExport,
|
||||
customToolbarRender,
|
||||
customActionRender,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const actionRef = useRef<ActionType>(null);
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
const [selectedRows, setSelectedRows] = useState<T[]>([]);
|
||||
|
||||
// 权限检查
|
||||
const hasPermission = useCallback(
|
||||
(permission?: string) => {
|
||||
if (!permission) return true;
|
||||
return checkPermission(permission);
|
||||
},
|
||||
[checkPermission]
|
||||
);
|
||||
|
||||
// 过滤有权限的操作
|
||||
const filteredActions = useMemo(() => {
|
||||
return actions.filter((action) => hasPermission(action.permission));
|
||||
}, [actions, hasPermission]);
|
||||
|
||||
const filteredToolbarActions = useMemo(() => {
|
||||
return toolbarActions.filter((action) => hasPermission(action.permission));
|
||||
}, [toolbarActions, hasPermission]);
|
||||
|
||||
// 构建操作列
|
||||
const buildActionColumn = useCallback((): ProColumns<T> => {
|
||||
if (!showActions || filteredActions.length === 0) {
|
||||
return {} as ProColumns<T>;
|
||||
}
|
||||
|
||||
return {
|
||||
title: "操作",
|
||||
valueType: "option",
|
||||
key: "actions",
|
||||
fixed: "right",
|
||||
width: Math.min(filteredActions.length * 60 + 50, 200),
|
||||
render: (_, record, index, action) => {
|
||||
// 过滤可见的操作
|
||||
const visibleActions = filteredActions.filter(
|
||||
(actionItem) => !actionItem.visible || actionItem.visible(record)
|
||||
);
|
||||
|
||||
if (visibleActions.length === 0) return null;
|
||||
|
||||
// 显示的操作按钮
|
||||
const displayActions = visibleActions.slice(0, maxActionCount);
|
||||
// 更多操作
|
||||
const moreActions = visibleActions.slice(maxActionCount);
|
||||
|
||||
const actionElements: React.ReactNode[] = displayActions.map(
|
||||
(actionItem) => (
|
||||
<a
|
||||
key={actionItem.key}
|
||||
onClick={() => actionItem.onClick(record, action)}
|
||||
style={{
|
||||
color: actionItem.danger ? "#ff4d4f" : undefined,
|
||||
}}
|
||||
>
|
||||
{actionItem.icon} {actionItem.label}
|
||||
</a>
|
||||
)
|
||||
);
|
||||
|
||||
// 如果有更多操作,添加下拉菜单
|
||||
if (moreActions.length > 0) {
|
||||
const menuItems = buildTableDropdownMenuItems(
|
||||
moreActions.map((actionItem) => ({
|
||||
key: actionItem.key,
|
||||
label: actionItem.label,
|
||||
icon: actionItem.icon,
|
||||
danger: actionItem.danger,
|
||||
disabled: actionItem.disabled?.(record) || false,
|
||||
}))
|
||||
);
|
||||
|
||||
actionElements.push(
|
||||
<TableDropdown
|
||||
key="more"
|
||||
onSelect={(key) => {
|
||||
handleTableDropdownSelect(key, moreActions, record, action);
|
||||
action?.reload();
|
||||
}}
|
||||
menus={menuItems}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 自定义操作渲染
|
||||
if (customActionRender) {
|
||||
return customActionRender(record, actionElements);
|
||||
}
|
||||
|
||||
return actionElements;
|
||||
},
|
||||
};
|
||||
}, [showActions, filteredActions, maxActionCount, customActionRender]);
|
||||
|
||||
// 构建列配置
|
||||
const enhancedColumns = useMemo(() => {
|
||||
const columns: ProColumns<T>[] = [];
|
||||
|
||||
// 添加序号列
|
||||
if (showIndex) {
|
||||
columns.push({
|
||||
title: "序号",
|
||||
dataIndex: "index",
|
||||
valueType: "indexBorder",
|
||||
width: 48,
|
||||
fixed: "left",
|
||||
});
|
||||
}
|
||||
|
||||
// 添加原始列
|
||||
columns.push(...originalColumns);
|
||||
|
||||
// 添加操作列
|
||||
const actionColumn = buildActionColumn();
|
||||
if (actionColumn.title) {
|
||||
columns.push(actionColumn);
|
||||
}
|
||||
|
||||
return columns;
|
||||
}, [originalColumns, showIndex, buildActionColumn]);
|
||||
|
||||
// 工具栏渲染
|
||||
const toolBarRender = useCallback(() => {
|
||||
const defaultActions = filteredToolbarActions.map((action) => (
|
||||
<Button
|
||||
key={action.key}
|
||||
type={action.type}
|
||||
danger={action.danger}
|
||||
icon={action.icon}
|
||||
disabled={
|
||||
action.disabled ||
|
||||
(action.needSelection && selectedRowKeys.length === 0)
|
||||
}
|
||||
onClick={() =>
|
||||
action.onClick(selectedRowKeys as number[], selectedRows)
|
||||
}
|
||||
>
|
||||
{action.label}
|
||||
</Button>
|
||||
));
|
||||
|
||||
if (customToolbarRender) {
|
||||
return customToolbarRender(defaultActions);
|
||||
}
|
||||
|
||||
return defaultActions;
|
||||
}, [
|
||||
filteredToolbarActions,
|
||||
selectedRowKeys,
|
||||
selectedRows,
|
||||
customToolbarRender,
|
||||
]);
|
||||
|
||||
// 行选择配置
|
||||
const rowSelection = useMemo(() => {
|
||||
if (!showSelection) return undefined;
|
||||
|
||||
return {
|
||||
selectedRowKeys,
|
||||
onChange: (keys: React.Key[], rows: T[]) => {
|
||||
setSelectedRowKeys(keys);
|
||||
setSelectedRows(rows);
|
||||
},
|
||||
getCheckboxProps: (record: T) => ({
|
||||
name: record.id?.toString(),
|
||||
}),
|
||||
};
|
||||
}, [showSelection, selectedRowKeys]);
|
||||
|
||||
// 表格提醒渲染
|
||||
const tableAlertRender = useCallback(
|
||||
({ selectedRowKeys, onCleanSelected }: any) => {
|
||||
if (!showSelection || selectedRowKeys.length === 0) return false;
|
||||
|
||||
return (
|
||||
<Space size={24}>
|
||||
<span>
|
||||
已选 {selectedRowKeys.length} 项
|
||||
<a style={{ marginLeft: 8 }} onClick={onCleanSelected}>
|
||||
取消选择
|
||||
</a>
|
||||
</span>
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
[showSelection]
|
||||
);
|
||||
|
||||
// 表格提醒操作渲染
|
||||
const tableAlertOptionRender = useCallback(() => {
|
||||
if (!showSelection || selectedRowKeys.length === 0) return false;
|
||||
|
||||
const batchActions = filteredToolbarActions.filter(
|
||||
(action) => action.needSelection
|
||||
);
|
||||
|
||||
if (batchActions.length === 0) return false;
|
||||
|
||||
return (
|
||||
<Space size={16}>
|
||||
{batchActions.map((action) => (
|
||||
<a
|
||||
key={action.key}
|
||||
onClick={() =>
|
||||
action.onClick(selectedRowKeys as number[], selectedRows)
|
||||
}
|
||||
>
|
||||
{action.label}
|
||||
</a>
|
||||
))}
|
||||
</Space>
|
||||
);
|
||||
}, [showSelection, selectedRowKeys, selectedRows, filteredToolbarActions]);
|
||||
|
||||
return (
|
||||
<ProTable<T, U>
|
||||
{...restProps}
|
||||
columns={enhancedColumns}
|
||||
actionRef={actionRef}
|
||||
request={request}
|
||||
rowKey="id"
|
||||
rowSelection={rowSelection}
|
||||
toolBarRender={toolBarRender}
|
||||
tableAlertRender={tableAlertRender}
|
||||
tableAlertOptionRender={tableAlertOptionRender}
|
||||
search={{
|
||||
labelWidth: "auto",
|
||||
...restProps.search,
|
||||
}}
|
||||
options={{
|
||||
fullScreen: true,
|
||||
reload: true,
|
||||
setting: true,
|
||||
density: true,
|
||||
...restProps.options,
|
||||
}}
|
||||
pagination={{
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: formatPaginationTotal,
|
||||
...restProps.pagination,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default EnhancedProTable;
|
||||
Reference in New Issue
Block a user