日常开发过程中,会遇到对 React Antd Table
列表的数据进行一键编辑修改的功能,最近刚好做到这个功能,记录一下,方便后续使用参考。
主页面组件
import { useEffect, useState } from "react";
import { Form, Button, Input, message } from "antd";
import styles from "./index.less";
import ResizeTable from "@/components/ResizeTable";
import api from "../../api";
const ProcessTable = () => {
const [edit, setEdit] = useState(false);
const [basicData, setBasicData] = useState([]);
const [craftOrderData, setCraftOrderData] = useState<any>({});
const [form] = Form.useForm();
// 查询按钮 进行提交搜索
const handleSearchCraftOrderSumbit = (craftOrderId: string) => {
api.searchCraftOrder(craftOrderId).then((res) => {
setCraftOrderData(res.data);
setBasicData(res?.data?.detail);
});
};
const onFinish = () => {
const newdata = form.getFieldsValue().packageProduct;
setBasicData(newdata);
// 根据需要获取并处理对应的接口入参
const { detail, ...extraValue } = craftOrderData;
const params = { ...extraValue, detail: newdata };
// 编辑列表项内容后,触发【保存】按钮调用接口更新列表数据
api.updateCraftOrder(params).then((res) => {
if (res.code !== 200) {
message.error(res.message);
return;
}
message.success("更新成功");
setEdit(false);
});
};
// 需要的列表栏
const columns = [
{
title: "控温方式",
dataIndex: "temperatureControllerMethod",
name: "temperatureControllerMethod",
key: "temperatureControllerMethod",
align: "center",
renderType: edit ? "input" : "text",
},
{
title: "设定温度",
dataIndex: "settingFurnaceTemperature",
name: "settingFurnaceTemperature",
key: "settingFurnaceTemperature",
align: "center",
renderType: edit ? "input" : "text",
},
{
title: "设定时间",
dataIndex: "settingDuration",
name: "settingDuration",
key: "settingDuration",
align: "center",
renderType: edit ? "input" : "text",
},
{
title: "运行时间",
dataIndex: "runningDuration",
name: "runningDuration",
key: "runningDuration",
align: "center",
renderType: edit ? "input" : "text",
},
{
title: "重量",
dataIndex: "weight",
name: "weight",
key: "weight",
align: "center",
renderType: edit ? "input" : "text",
},
];
const ListTableForm = (props: any) => {
const { edit, basicData, form } = props;
const rules = [{ required: true, message: "请填充内容!" }];
const [cls, setCls] = useState<any[]>([]);
function renderType(_: any, record: any, index: any, other: any) {
const { renderType } = other; //renderType是让编辑态的表格,继承查看态的表格
switch (renderType) {
case "input":
return (
<Form.Item
style={{ marginBottom: 0 }}
rules={rules}
name={[record.name, other.name]}
fieldKey={[record.fieldKey, other.name]}
>
<Input
defaultValue={record.Temperature}
size={"middle"}
allowClear
disabled={other.key === "num"}
/>
</Form.Item>
);
case "input-value":
return (
<Form.Item
style={{ marginBottom: 0 }}
rules={rules}
name={[record.name, other.name]}
fieldKey={[record.fieldKey, other.name]}
>
<Input defaultValue={record.value} />
</Form.Item>
);
case "input-description":
return (
<Form.Item
style={{ marginBottom: 0 }}
rules={rules}
name={[record.name, other.name]}
fieldKey={[record.fieldKey, other.name]}
>
<Input defaultValue={record.description} />
</Form.Item>
);
case "text":
return (
<Form.Item
style={{ marginBottom: 0 }}
rules={rules}
name={[record.name, other.name]}
fieldKey={[record.fieldKey, other.name]}
>
<span>{record.name}</span>
</Form.Item>
);
case "text-value":
return (
<Form.Item
style={{ marginBottom: 0 }}
rules={rules}
name={[record.name, other.name]}
fieldKey={[record.fieldKey, other.name]}
>
<span>{record.value}</span>
</Form.Item>
);
case "text-description":
return (
<Form.Item
style={{ marginBottom: 0 }}
rules={rules}
name={[record.name, other.name]}
fieldKey={[record.fieldKey, other.name]}
>
<span>{record.description}</span>
</Form.Item>
);
default:
return (
<Form.Item shouldUpdate={true} style={{ marginBottom: 0 }}>
{({ getFieldValue }) => {
return (getFieldValue(props.formName) || [])?.[index]?.[
other?.name
];
}}
</Form.Item>
);
}
}
useEffect(() => {
const _newProps = props.cls.map(
(item: { [x: string]: any; render: any }) => {
const { render, ...resetProps } = item;
return {
...resetProps,
render: (text: any, record: any, index: any) =>
renderType(text, record, index, item),
};
}
);
setCls(_newProps);
}, [props.cls]);
return (
<>
{edit ? (
// 编辑态模式下,搭配 Form.List 内嵌Form表单就可以通过form拿到所有列表项修改的值
<Form.List name={props.formName || "tableForm"}>
{(fields) => {
return (
<>
<Form.Item style={{ marginBottom: 0 }}>
<ResizeTable
rowClassName={() => styles.childtable}
tableHeadheight={90}
idName="tables" //必传,用来获取表格高度做数据滚动检测
title={() => "表单设置"}
footer={() => {
return (
<div className={styles.footers}>
<Button
onClick={() => {
setEdit(false);
}}
>
取消
</Button>
<Button
type="primary"
className={styles.primary}
onClick={onFinish}
>
保存
</Button>
</div>
);
}}
pagination={false}
rowKey={"name"}
dataSource={fields}
columns={cls}
/>
</Form.Item>
</>
);
}}
</Form.List>
) : (
<Form.Item style={{ marginBottom: 0 }}>
<ResizeTable
rowClassName={() => styles.childtable}
tableHeadheight={90}
idName="table"
title={() => "表单设置"}
footer={() => {
return (
<Button
onClick={() => {
setEdit(true);
form.setFieldsValue({ packageProduct: basicData });
}}
>
编辑
</Button>
);
}}
pagination={false}
rowKey={"name"}
dataSource={basicData}
columns={columns}
/>
</Form.Item>
)}
</>
);
};
useEffect(() => {
// 根据参数,查询table列表数据
handleSearchCraftOrderSumbit("123");
}, []);
return (
<div className={styles.root}>
<Form form={form} name="basic" className={styles.main}>
<ListTableForm
hideAction={!edit}
edit={edit}
formName="packageProduct"
fields={basicData}
basicData={basicData}
cls={columns}
form={form}
/>
</Form>
</div>
);
};
export default ProcessTable;
无非就是编辑态模式下,通过Form.List内嵌Form.Item表单项的方式在form中拿到所有列表修改项的值。这里页面用到了额外封装的ResizeTable组件,其实就是对Antd的Table做了层封装 使其支持resize动态平铺以及高度自控制的滚动。
ResizeTable组件
import React from "react";
import Table from "@/components/Table";
import { useGetResizeHeight } from "./hook/useGetResizeTable";
import styles from "./index.less";
/*** 自适应高度表格*
* @功能*
* 1、自动占满页面剩余高度*
* 2、改变浏览器窗口时自动适应剩余高度不出现外部滚动条*
* @前提*
* 页面自身设置的有高度,而非根据子元素撑开高度*
* @使用方法*
* 1、安装lodash--yarn add lodash,仅使用里面的防抖方法*
* 2、在需要使用的地方导入--import ResizeTable from '@/components/ResizeTable/ResizeTable'*
* 3、使用--<ResizeTable api和antd的Table一样/>* @demo* import ResizeTable from '@/components/ResizeTable/ResizeTable';*
* 4、tableHeadheight(表头高度)paginationHeight(分页组件高度可选) middleHeight(底部预留高度可选)
**/
const ResizeTable: React.FC<any> = (props) => {
//表头高度<tableHeadheight> 分页组件高度(paginationHeight) 底部预留高度(middleHeight)
const { tableHeadheight, paginationHeight, middleHeight, idName } = props;
const [tableHeight] = useGetResizeHeight(
`${idName}`,
tableHeadheight,
paginationHeight ? paginationHeight : 0,
middleHeight ? middleHeight : 0
);
let tableProps = { ...props, scroll: { y: tableHeight } };
if (props && props.scroll && props.scroll.x) {
tableProps.scroll.x = props.scroll.x;
}
return (
<div className={styles.tableWrap}>
<div id={idName} className={styles.table}>
<Table {...tableProps} />
</div>
</div>
);
};
export default ResizeTable;
useGetResizeHeight动态缩放组件
import { useEffect, useState } from "react";
import debounce from "lodash/debounce";
import { useSize } from "ahooks";
export function useGetResizeHeight(
name: string,
tableHeadheight: number,
paginationHeight: number,
middleHeight: number
) {
//设置撑高表格外部包裹元素的高度
const [tableHeight, setTableHeight] = useState(500);
const size = useSize(document.getElementById(name));
const handleGetTableHeight = () => {
setTimeout(() => {
let height = document.getElementById(name)!.clientHeight;
//高度-表头高度<tableHeadheight>-分页组件高度(paginationHeight)-底部预留高度(middleHeight)
height = height - tableHeadheight - paginationHeight - middleHeight - 6;
setTableHeight(height);
});
};
useEffect(() => {
handleGetTableHeight();
const debounced = debounce(handleGetTableHeight, 200);
window.addEventListener("resize", debounced);
return () => window.removeEventListener("resize", debounced);
}, [size]);
return [tableHeight];
}
Tips:
这里有个小点要注意下,因为这里的table表头只有一层,使用这种方式是完全没问题的。但如果是合并表头的header,columns中内嵌了children后,使用这个ListTableForm
编辑的时候就会导致columns只能显示第一层的部分,导致表头header丢失了。
这个时候可以使用geek的方式进行处理
多表头合并态下,如何编辑的时候固定表格头不变,以下是部分实例代码 参考
{
edit ? (
// 编辑态
<Form.List name={props.formName || "tableForm"}>
{(fields) => {
return (
<>
<Form.Item style={{ marginBottom: 0 }}>
{/* 只展示固定表头做占位用 不展示数据 同时根据style清除原有的table-body样式 */}
<Table
className={`${styles.emptyTableBody}`}
pagination={false}
dataSource={[]}
columns={colums as any}
/>
{/* 固定上面的表头后,把现在的表头通过样式藏掉,因为表单的数据需要通过column里的name做绑定 */}
<Table
className={`${styles.emptyTableColumn}`}
pagination={false}
rowKey={"name"}
footer={() => {
return (
<div className={styles.footers}>
<Button
onClick={() => {
setEdit(false);
}}
style={{ marginRight: 15 }}
>
取消
</Button>
<Button
type="primary"
className={styles.primary}
onClick={onFinish}
>
保存
</Button>
</div>
);
}}
dataSource={fields}
columns={cls}
/>
</Form.Item>
</>
);
}}
</Form.List>
) : (
// 仅展示
<Form.Item style={{ marginBottom: 0 }}>
<Table />
</Form.Item>
)
}
// 上面对应的Table隐藏表头head和表格body列表内容的样式
.emptyTableBody {
:global(.ant-table-tbody) {
display: none;
}
}
.emptyTableColumn {
:global(.ant-table-thead) {
display: none;
}
}