目录
- react 如何封装一个 table 组件
- react 父子组件传值
- 讲一下 umi
- umi 的状态管理的使用
- 介绍下你的项目难点
- 讲一下你们小程序是怎么做的
- tooltip 组件怎么自定义判断 2 行 3 行的时候展示
- umi 怎么动态加载 css js
- umi 怎么使用状态管理
1. react 如何封装一个 table 组件
我将为你提供一个封装 Ant Design Table 组件的方案,使其具有类似 Ant Design Pro 的高级功能。这种封装可以提高代码复用性,简化表格使用,并统一项目中的表格风格。
封装思路
我们将创建一个 ProTable
组件,支持以下核心功能:
- 自动处理分页、排序、筛选
- 支持搜索表单
- 集成请求数据的 Hooks
- 自动生成表格列配置
- 支持操作栏自定义
- 统一的样式风格
组件实现
下面是一个完整的 ProTable
组件实现:
import React, { useState, useMemo, useCallback, useEffect } from 'react';
import { Table, Form, Button, Space, Input, Select, Popconfirm } from 'antd';
import { SearchOutlined, ReloadOutlined } from '@ant-design/icons';
import { debounce } from 'lodash';
const { Item } = Form;
const { Option } = Select;
/**
* ProTable 组件 - 封装 Ant Design Table 提供高级功能
* @param {Object} props - 组件属性
* @param {Array} props.columns - 表格列配置
* @param {Function} props.request - 获取数据的请求函数
* @param {Object} props.formItems - 搜索表单配置
* @param {Object} props.initialValues - 初始表单值
* @param {Boolean} props.loading - 加载状态
* @param {Function} props.onRow - 行点击事件
* @param {Function} props.rowKey - 行键
* @param {Object} props.tableProps - 传递给 Table 的其他属性
*/
const ProTable = ({
columns = [],
request,
formItems = [],
initialValues = {},
loading = false,
onRow,
rowKey = 'id',
tableProps = {},
}) => {
const [form] = Form.useForm();
const [dataSource, setDataSource] = useState([]);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 0,
});
const [sorter, setSorter] = useState({});
const [filters, setFilters] = useState({});
// 处理搜索表单值变化
const handleFormValuesChange = useCallback((changedValues, allValues) => {
// 重置分页
setPagination(prev => ({
...prev,
current: 1,
}));
// 更新筛选条件
setFilters(allValues);
}, []);
// 处理分页变化
const handleTableChange = useCallback((pagination, filters, sorter) => {
setPagination(pagination);
setFilters(filters);
setSorter(sorter);
}, []);
// 构建搜索表单
const renderFormItems = useMemo(() => {
return formItems.map(item => {
const { key, title, type = 'input', options = [], ...rest } = item;
switch (type) {
case 'select':
return (
<Item key={key} label={title} name={key} {...rest}>
<Select placeholder={`请选择${title}`}>
{options.map(option => (
<Option key={option.value} value={option.value}>{option.label}</Option>
))}
</Select>
</Item>
);
case 'input':
default:
return (
<Item key={key} label={title} name={key} {...rest}>
<Input placeholder={`请输入${title}`} />
</Item>
);
}
});
}, [formItems]);
// 处理搜索
const handleSearch = useCallback(() => {
// 重置分页
setPagination(prev => ({
...prev,
current: 1,
}));
// 触发数据加载
fetchData();
}, [fetchData]);
// 处理重置
const handleReset = useCallback(() => {
form.resetFields();
// 重置分页和筛选
setPagination({
current: 1,
pageSize: 10,
total: 0,
});
setFilters({});
setSorter({});
// 触发数据加载
fetchData();
}, [form, fetchData]);
// 构建表格列配置
const buildColumns = useMemo(() => {
return columns.map(column => {
// 如果有操作列,添加固定位置
if (column.key === 'actions') {
return {
...column,
fixed: 'right',
width: 180,
};
}
return column;
});
}, [columns]);
// 合并表格属性
const mergedTableProps = useMemo(() => {
return {
rowKey,
dataSource,
pagination,
loading,
onChange: handleTableChange,
onRow,
columns: buildColumns,
...tableProps,
};
}, [dataSource, pagination, loading, handleTableChange, onRow, buildColumns, tableProps, rowKey]);
// 数据加载
const fetchData = useCallback(async () => {
if (!request) return;
try {
const { current, pageSize } = pagination;
const { field, order } = sorter;
const response = await request({
page: current,
pageSize,
sortField: field,
sortOrder: order === 'ascend' ? 'asc' : 'desc',
...filters,
});
setDataSource(response.data || []);
setPagination(prev => ({
...prev,
total: response.total || 0,
}));
} catch (error) {
console.error('Failed to fetch data:', error);
}
}, [request, pagination, sorter, filters]);
// 初始化数据
useEffect(() => {
form.setFieldsValue(initialValues);
fetchData();
}, [form, initialValues, fetchData]);
// 防抖搜索
const debouncedSearch = useCallback(
debounce(handleSearch, 500),
[handleSearch]
);
return (
<div className="pro-table-container">
{formItems.length > 0 && (
<Form
form={form}
layout="inline"
onValuesChange={handleFormValuesChange}
initialValues={initialValues}
>
<div className="pro-table-search-bar">
<Space>
{renderFormItems}
<Item>
<Button type="primary" onClick={handleSearch} icon={<SearchOutlined />}>
搜索
</Button>
</Item>
<Item>
<Button onClick={handleReset}>重置</Button>
</Item>
</Space>
</div>
</Form>
)}
<Table {...mergedTableProps} />
</div>
);
};
export default ProTable;
使用示例
下面是如何使用这个封装组件的示例:
import React from 'react';
import ProTable from './ProTable';
// 模拟API请求
const fetchTableData = async ({ page, pageSize, sortField, sortOrder, ...filters }) => {
// 这里应该是实际的API调用
console.log('Fetching data with params:', { page, pageSize, sortField, sortOrder, filters });
// 模拟返回数据
return new Promise(resolve => {
setTimeout(() => {
resolve({
data: [
{ id: 1, name: '张三', age: 28, address: '北京市朝阳区', status: 'active' },
{ id: 2, name: '李四', age: 32, address: '上海市浦东新区', status: 'inactive' },
{ id: 3, name: '王五', age: 45, address: '广州市天河区', status: 'active' },
],
total: 100,
});
}, 800);
});
};
// 表格列配置
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
sorter: true,
},
{
title: '姓名',
dataIndex: 'name',
key: 'name',
sorter: true,
},
{
title: '年龄',
dataIndex: 'age',
key: 'age',
sorter: true,
},
{
title: '地址',
dataIndex: 'address',
key: 'address',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
filters: [
{ text: '活跃', value: 'active' },
{ text: '非活跃', value: 'inactive' },
],
onFilter: (value, record) => record.status === value,
},
{
title: '操作',
key: 'actions',
render: (_, record) => (
<Space size="small">
<a href="#">查看</a>
<a href="#">编辑</a>
<Popconfirm title="确定要删除吗?" onConfirm={() => console.log('Delete:', record.id)}>
<a href="#">删除</a>
</Popconfirm>
</Space>
),
},
];
// 搜索表单配置
const formItems = [
{
key: 'name',
title: '姓名',
},
{
key: 'status',
title: '状态',
type: 'select',
options: [
{ label: '全部', value: '' },
{ label: '活跃', value: 'active' },
{ label: '非活跃', value: 'inactive' },
],
},
];
// 页面组件
const MyTablePage = () => {
return (
<div className="page-container">
<h1>用户列表</h1>
<ProTable
columns={columns}
request={fetchTableData}
formItems={formItems}
initialValues={{ status: 'active' }}
/>
</div>
);
};
export default MyTablePage;
样式优化(可选)
添加一些全局样式可以使表格看起来更像 Ant Design Pro:
/* pro-table.css */
.pro-table-container {
background-color: #fff;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 1px 2px -2px rgba(0, 0, 0, 0.16), 0 3px 6px 0 rgba(0, 0, 0, 0.12), 0 5px 12px 4px rgba(0, 0, 0, 0.09);
}
.pro-table-search-bar {
margin-bottom: 16px;
padding-bottom: 16px;
border-bottom: 1px solid #f0f0f0;
}
/* 表格样式优化 */
.ant-table-thead > tr > th {
background-color: #fafafa;
font-weight: 600;
}
.ant-table-tbody > tr:hover > td {
background-color: #f5f7fa !important;
}
.ant-pagination {
margin-top: 16px !important;
}
封装优势
- 统一风格:所有表格使用相同的样式和交互逻辑
- 简化使用:只需提供列配置和数据请求函数
- 自动处理分页/排序/筛选:减少重复代码
- 可扩展:可以轻松添加新功能(如导出、批量操作)
- 易于维护:一处修改,所有表格生效
你可以根据项目需求进一步扩展这个组件,比如添加批量操作、导出功能、行选择等。