Antd 可编辑表格(字段依赖更新)

145 阅读1分钟
/**
 * @name TableData
 * @description
 * @author darcrand
 */

import { Button, Divider, Form, InputNumber, Space, Table, TableProps, Typography } from 'antd'
import { useState } from 'react'
import { uid } from './utils'

type RowData = {
  id: string
  a?: number
  b?: number
  c?: number
  d?: number
}

export default function TableData() {
  const [form] = Form.useForm<RowData>()
  const [data, setData] = useState<RowData[]>([])
  const [editingId, setEditingId] = useState<string | null>(null)

  const add = () => {
    const id = uid()
    setData((prev) => prev.concat({ id }))
    setEditingId(id)
  }

  const remove = (id: string) => {
    setData((prev) => prev.filter((v) => v.id !== id))
    setEditingId(null)
  }

  const onEdit = (row: RowData) => {
    setEditingId(row.id)
    form.setFieldsValue(row)
  }

  const onCancel = () => {
    setEditingId(null)
    form.resetFields()
  }

  const onUpdate = () => {
    // 当依赖字段更新时,同步更新其他计算字段
    const values = form.getFieldsValue()
    const c = (values.a || 0) + (values.b || 0)
    const d = (values.a || 0) * (values.b || 0)

    form.setFieldValue('c', c)
    form.setFieldValue('d', d)
  }

  const onSave = async () => {
    await form.validateFields()
    const values = form.getFieldsValue()

    setData((prev) => {
      return prev.map((v) => {
        return v.id === editingId ? { ...v, ...values } : v
      })
    })

    setEditingId(null)
    form.resetFields()
  }

  const columns: TableProps<RowData>['columns'] = [
    { title: 'ID', dataIndex: 'id' },
    {
      title: 'A',
      dataIndex: 'a',
      render: (value, row) =>
        row.id === editingId ? (
          <Form.Item name={['a']} noStyle>
            <InputNumber onChange={onUpdate} />
          </Form.Item>
        ) : (
          <InputNumber readOnly variant='borderless' value={value} />
        ),
    },
    {
      title: 'B',
      dataIndex: 'b',
      render: (value, row) =>
        row.id === editingId ? (
          <Form.Item name={['b']} noStyle>
            <InputNumber onChange={onUpdate} />
          </Form.Item>
        ) : (
          <InputNumber readOnly variant='borderless' value={value} />
        ),
    },
    {
      title: 'C',
      dataIndex: 'c',
      render: (value, row) =>
        row.id === editingId ? (
          <Form.Item name={['c']} noStyle>
            <InputNumber readOnly variant='borderless' />
          </Form.Item>
        ) : (
          <InputNumber readOnly variant='borderless' value={value} />
        ),
    },
    {
      title: 'D',
      dataIndex: 'd',
      render: (value, row) =>
        row.id === editingId ? (
          <Form.Item name={['d']} noStyle>
            <InputNumber readOnly variant='borderless' />
          </Form.Item>
        ) : (
          <InputNumber readOnly variant='borderless' value={value} />
        ),
    },
    {
      title: 'Operation',
      fixed: 'right',
      render(_, row) {
        return (
          <Space>
            {row.id === editingId ? (
              <>
                <Button onClick={onCancel}>cancel</Button>
                <Button onClick={onSave}>save</Button>
                <Button onClick={() => remove(row.id)}>delete</Button>
              </>
            ) : (
              <Button onClick={() => onEdit(row)}>edit</Button>
            )}
          </Space>
        )
      },
    },
  ]

  return (
    <>
      <Form form={form}>
        <Space>
          <Button type='primary' disabled={!!editingId} onClick={add}>
            Add
          </Button>
        </Space>

        <Table
          dataSource={data}
          columns={columns}
          rowKey='id'
          pagination={{ defaultPageSize: Infinity, hideOnSinglePage: true }}
        />
      </Form>

      <Divider />

      <section>
        <Typography.Paragraph>data source</Typography.Paragraph>
        <pre>{JSON.stringify(data, null, 2)}</pre>
      </section>
    </>
  )
}