React 实现table内单元格可编辑 (hooks)
程序员文章站
2022-06-08 14:53:56
...
双击可编辑 失去焦点或者键盘回车事件保存,自己封装组件 并调用 拿走即用
自己封装的组件:
import React, { useContext, useState, useEffect, useRef } from "react";
import { Table, Input, Form } from "antd";
import { FormInstance } from "antd/lib/form";
import styled from "@emotion/styled";
import CommonTable from "../CommonTable";
// 这是ant design中的一个简单Table组件 自己封装一个即可
const EditableContext = React.createContext<FormInstance<any> | null>(null);
interface Item {
key: string;
name: string;
age: string;
address: string;
}
interface EditableRowProps {
index: number;
}
const EditTableContainer = styled(CommonTable)`
.editable-cell {
position: relative;
}
.editable-cell-value-wrap {
padding: 5px 12px;
cursor: pointer;
}
.editable-row:hover .editable-cell-value-wrap {
padding: 4px 11px;
border: 1px solid #5499c9;
border-radius: 2px;
}
`;
const EditableRow: React.FC<EditableRowProps> = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
interface EditableCellProps {
title: React.ReactNode;
editable: boolean;
children: React.ReactNode;
dataIndex: keyof Item;
record: Item;
handleSave: (record: Item) => void;
}
const EditableCell: React.FC<EditableCellProps> = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const [editing, setEditing] = useState(false);
const inputRef = useRef<Input>(null);
const form = useContext(EditableContext)!;
useEffect(() => {
if (editing) {
inputRef.current!.focus();
}
}, [editing]);
const toggleEdit = () => {
setEditing(!editing);
form.setFieldsValue({ [dataIndex]: record[dataIndex] });
};
const save = async () => {
try {
const values = await form.validateFields();
toggleEdit();
handleSave({ ...record, ...values });
} catch (errInfo) {
console.log("Save failed:", errInfo);
}
};
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{ margin: 0 }}
name={dataIndex}
rules={[
{
required: true,
message: `${title}为必填项`,
},
]}
>
<Input ref={inputRef} onPressEnter={save} onBlur={save} />
</Form.Item>
) : (
<div
className="editable-cell-value-wrap"
style={{ paddingRight: 24 }}
onDoubleClick={toggleEdit}
>
{children}
</div>
);
}
return <td {...restProps}>{childNode}</td>;
};
export type EditableTableProps = Parameters<typeof Table>[0];
export interface DataType {
key: React.Key;
name: string;
age: string;
address: string;
}
export type ColumnTypes = Exclude<EditableTableProps["columns"], undefined>;
export default function RightTable(props: EditableTableProps) {
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
return (
<EditTableContainer
components={components}
rowClassName={() => "editable-row"}
{...props}
/>
);
}
自己在调用该组件:
import EditableTable, {
ColumnTypes,
DataType,
} from "@/components/EditableTable";
import { useState } from "react";
import styled from "@emotion/styled";
//css样式
const Tableincident = styled(EditableTable)`
input {
background-color: transparent !important;
color: #fff !important;
}
.ant-table-body::-webkit-scrollbar {
display: none;
}
.ant-table {
background: transparent !important;
.ant-table-container::after {
::-webkit-scrollbar-thumb {
background: #000000;
}
}
}
.ant-table-thead > tr > th {
background: #004c87 !important;
color: #fff;
font-size: 14px;
border: 1px solid #004c87;
cursor: default;
.ant-table-cell-scrollbar {
box-shadow: 0;
}
}
.ant-table-tbody > tr > td {
border-color: #004c87;
background: transparent !important;
color: #fff;
font-size: 14px;
}
`;
//函数组件
function Test(props: any) {
const [dataSource, setDataSource] = useState<DataType[]>([]);
const columns = [
{
title: "时间",
dataIndex: "Time",
width: "50%",
editable: true,
},
{
title: "地点",
dataIndex: "Site",
width: "30%",
editable: true,
},
{
title: "触发词",
dataIndex: "Trigger",
width: "30%",
editable: true,
},
{
title: "论元",
dataIndex: "argument",
width: "50%",
editable: true,
},
].map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record: DataType) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave: handleSave,
}),
};
});
const handleSave = (row: DataType) => {
console.log(row);
const newData = [...props.data];
const index = newData.findIndex((item) => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
console.log(newData);
setDataSource(newData);
//这里有一个数据保存的接口
};
return (
<div>
<Tableincident
columns={columns as ColumnTypes}
dataSource={dataSource}
scroll={{ y: 200 }}
pagination={false}
></Tableincident>
</div>
);
}
export default Test;