# antd Table组件的通用可编辑单元格editable-cell

组件来源: 办公系统v3 - 通用组件

## 1.功能说明

可通过该组件替换antd Table的components，从而达到table组件可编辑的效果

## 2.组件依赖

-- react

-- antd

## 3.组件接口

```tsx
interface EditableCellProps<T> {
    editable: boolean;    //是否可编辑（默认为false）
    cellKey: string,      //单元格的key值
    selectedKey: string,  //当前选择的k值
    children: React.ReactNode;
    dataIndex: string;    //当前列的数据来源，与对应Table.Column保持一致
    record: T;            //列模板
    align?: "left" | "center" | "right",    //文字样式（左对齐、居中、右对齐）
    type?: 'Input' | 'Select' | 'InputNumberPrice' | 'InputNumber' | 'InputNumberPercent'; //可编辑形式
    options?: any[];             //当type = Select时的选项
    allowClear?: boolean;        //是否允许全部清空
    showSelectSearch?: boolean;
    directEdit?: boolean;
    handleSave?: (record: T) => void;    //修改后的保存回调函数
    onTap?: (record: T, cellKey: string) => void;   //点击时的回调
    onRightTap?: (e, record: T, cellKey: string) => void;   //右键点击时的回调
}
```

## 4.案例

1)引入依赖

```tsx
import ContextMenuFC from '@/common/context-menu/context-menu-fc';
import { Table } from 'antd';
import { useEffect, useState } from 'react';
import classNames from 'classnames';
import { EditableCell, EditableRow } from '@/common/editable-cell';
```

2)定义好初始数据、右键菜单相关功能、数据修改后的储存功能

```tsx
const [data, setData] = useState<PersonData[]>([{name: "小明", id: 1, age: 13}, {name: "小红", id: 2, age: 14}, {name: "小峰", id: 3, age: 17}]);
const [selectedRow, setSelectedRow] = useState<PersonData>();

const [contextMenuVisible, setContextMenuVisible] = useState<boolean>(false);
const [contextMenuTop, setContextMenuTop] = useState<number>(null);
const [contextMenuLeft, setContextMenuLeft] = useState<number>(null);
const [contextMenuWidth, setContextMenuWidth] = useState<number>(140);

useEffect(() => {
}, [data])

const updateData = (personData: PersonData) => {
    let newData = [];
    data.forEach(item => {
        if(item.id !== personData.id){
            newData.push(item);
        }else{
            newData.push(personData)
        }
    })
    setData(newData);

}

const onContextMenu = (e, record) => {
    setSelectedRow(record);
    e.preventDefault();
    let clientX = e.clientX;
    let clientY = e.clientY;
    setContextMenuLeft(clientX);
    setContextMenuTop(clientY);
    setContextMenuVisible(true)
}

const handleSave = (row: PersonData, key: string) => {
    updateData(row);
}
```

3)在函数式组件的return中加入：

```tsx
<Table
    components={{
        body: {
            row: EditableRow,   //再次添加可编辑组件
            cell: EditableCell, //再次添加可编辑组件
        }
    }}
    dataSource={data}
    rowKey={(record) => record.id + record.name + record.age}
    pagination={false}
    bordered
    onRow={row => {
        return {
            onContextMenu: e => onContextMenu(e, row)
        }
    }}
>
    <Table.Column
        title="序号"
        dataIndex="id"
        width="90px"
        align="right"
    ></Table.Column>
    {/**通过对onCell属性赋予interface中的值，让名称列变得可编辑 */}
    <Table.Column
    title="名称" dataIndex="name" width="90px" align="right"
    onCell={(record:PersonData, idx) => ({
        //此处的属性全是interface中所定义的属性
        record,
        editable:true,
        dataIndex:'name',
        allowClear:true,
        handleSave: (e) => handleSave(e, 'name'),
        }) as any}
    ></Table.Column>
    <Table.Column title="年龄" dataIndex="age" width="90px" align="right"></Table.Column>
</Table>

{/**右键菜单 */}
<ContextMenuFC
    visible={contextMenuVisible}
    left={contextMenuLeft}
    top={contextMenuTop}
    width={contextMenuWidth}
    wrapperClassname='task-flow-context-menu-wrapper'
    onCancel={() => setContextMenuVisible(false)}
>
    <div className="ope-menu">
        <div className={classNames({
            "menu-item": true,
        })} onClick={() => {
            let tempData = [];
            data.forEach(item => {
                if(item.id === selectedRow.id){
                    item.age += 1;
                    tempData.push(item);
                }else{
                    tempData.push(item);
                }
            })
            setData(tempData);
            setContextMenuVisible(false);
            setSelectedRow(null);
        }
            }>年龄+1</div>
        <div className={classNames({
            "menu-item": true,
        })} onClick={() => {
            let tempData = [];
            data.forEach(item => {
                if(item.id === selectedRow.id){
                    item.age -= 1;
                    tempData.push(item);
                }else{
                    tempData.push(item);
                }
            })
            setData(tempData);
            setContextMenuVisible(false);
            setSelectedRow(null);
        }
            }>年龄-1</div>
    </div>
</ContextMenuFC>
```

## 5.备注

editable-cell本身不单独使用，而是作为一个值传递到antd Table组件中的components属性中，再在需要修改为可编辑的列中设置onCell
