forked from ddrilling/asb_cloud_front
Частична улучшена типизация Editable table
This commit is contained in:
parent
32ec3c22d2
commit
4ad6f91c8a
@ -3,7 +3,7 @@ import { Form, Button, Popconfirm, TableProps } from 'antd'
|
|||||||
import { EditOutlined, SaveOutlined, PlusOutlined, CloseCircleOutlined, DeleteOutlined } from '@ant-design/icons'
|
import { EditOutlined, SaveOutlined, PlusOutlined, CloseCircleOutlined, DeleteOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
import { hasPermission } from '@utils'
|
import { hasPermission, isDev } from '@utils'
|
||||||
|
|
||||||
import { Table, TableColumns } from '.'
|
import { Table, TableColumns } from '.'
|
||||||
import { EditableCell } from './EditableCell'
|
import { EditableCell } from './EditableCell'
|
||||||
@ -87,7 +87,7 @@ const EditableTableComponents = { body: { cell: EditableCell }}
|
|||||||
export type EditableTableProps<T extends CrudObject, S extends CrudService<T>> = TableProps<T> & {
|
export type EditableTableProps<T extends CrudObject, S extends CrudService<T>> = TableProps<T> & {
|
||||||
columns: ColumnsType<T> & TableColumns<T>
|
columns: ColumnsType<T> & TableColumns<T>
|
||||||
dataSource?: T[]
|
dataSource?: T[]
|
||||||
onChange
|
onChange?: ((data: T[]) => Promise<void>)
|
||||||
onRowAdd?: TableActionProps<T, S> | ((record: T) => Promise<void>)
|
onRowAdd?: TableActionProps<T, S> | ((record: T) => Promise<void>)
|
||||||
onRowEdit?: TableActionProps<T, S> | ((record: T) => Promise<void>)
|
onRowEdit?: TableActionProps<T, S> | ((record: T) => Promise<void>)
|
||||||
onRowDelete?: TableActionProps<T, S> | ((record: T) => Promise<void>)
|
onRowDelete?: TableActionProps<T, S> | ((record: T) => Promise<void>)
|
||||||
@ -130,14 +130,19 @@ const _EditableTable = <T extends CrudObject, S extends CrudService<T>>({
|
|||||||
}, [form])
|
}, [form])
|
||||||
|
|
||||||
const cancel = useCallback(() => {
|
const cancel = useCallback(() => {
|
||||||
if (editingKey === newRowKeyValue) {
|
setEditingKey((prev) => {
|
||||||
const newData = [...data]
|
if (prev === newRowKeyValue) {
|
||||||
const index = newData.findIndex((item) => newRowKeyValue === item.key)
|
setData((prev) => {
|
||||||
newData.splice(index, 1)
|
const newData = [...prev]
|
||||||
setData(newData)
|
const index = newData.findIndex((item) => newRowKeyValue === item.key)
|
||||||
}
|
newData.splice(index, 1)
|
||||||
setEditingKey('')
|
return newData
|
||||||
}, [data, editingKey])
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ''
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
const addNewRow = useCallback(async () => {
|
const addNewRow = useCallback(async () => {
|
||||||
const newRow: any = { key: newRowKeyValue } // TODO: Исправить тип
|
const newRow: any = { key: newRowKeyValue } // TODO: Исправить тип
|
||||||
@ -146,43 +151,38 @@ const _EditableTable = <T extends CrudObject, S extends CrudService<T>>({
|
|||||||
}, [edit])
|
}, [edit])
|
||||||
|
|
||||||
const save = useCallback(async (record: T) => {
|
const save = useCallback(async (record: T) => {
|
||||||
|
let row
|
||||||
try {
|
try {
|
||||||
const row = await form.validateFields()
|
row = await form.validateFields()
|
||||||
const newData = [...data]
|
|
||||||
const index = newData.findIndex((item) => record.key === item.key)
|
|
||||||
const item = newData[index]
|
|
||||||
const newItem = { ...item, ...row }
|
|
||||||
|
|
||||||
newData.splice(index, 1, newItem)
|
|
||||||
|
|
||||||
if (item.key === newRowKeyValue)
|
|
||||||
item.key = newRowKeyValue + newData.length
|
|
||||||
|
|
||||||
const isAdding = editingKey === newRowKeyValue
|
|
||||||
setEditingKey('')
|
|
||||||
setData(newData)
|
|
||||||
|
|
||||||
if (isAdding)
|
|
||||||
try {
|
|
||||||
onAdd?.(newItem)
|
|
||||||
} catch (err) {
|
|
||||||
console.log('callback onRowAdd fault:', err)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
try {
|
|
||||||
onEdit?.(newItem)
|
|
||||||
} catch (err) {
|
|
||||||
console.log('callback onRowEdit fault:', err)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
onChange?.(newData)
|
|
||||||
} catch (err) {
|
|
||||||
console.log('callback onChange fault:', err)
|
|
||||||
}
|
|
||||||
} catch (errInfo) {
|
} catch (errInfo) {
|
||||||
console.log('Validate Failed:', errInfo)
|
if (isDev())
|
||||||
|
console.warn('Validate Failed:', errInfo)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const newData = [...data]
|
||||||
|
const index = newData.findIndex((item) => record.key === item.key)
|
||||||
|
const item = newData[index]
|
||||||
|
const newItem = { ...item, ...row }
|
||||||
|
|
||||||
|
newData.splice(index, 1, newItem)
|
||||||
|
|
||||||
|
if (item.key === newRowKeyValue)
|
||||||
|
item.key += newData.length
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (editingKey === newRowKeyValue)
|
||||||
|
await onAdd?.(newItem)
|
||||||
|
else
|
||||||
|
await onEdit?.(newItem)
|
||||||
|
await onChange?.(newData)
|
||||||
|
} catch (err) {
|
||||||
|
if (isDev())
|
||||||
|
console.warn(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
setEditingKey('')
|
||||||
|
setData(newData)
|
||||||
}, [data, editingKey, form, onChange, onAdd, onEdit])
|
}, [data, editingKey, form, onChange, onAdd, onEdit])
|
||||||
|
|
||||||
const deleteRow = useCallback((record: T) => {
|
const deleteRow = useCallback((record: T) => {
|
||||||
|
@ -1,26 +1,58 @@
|
|||||||
import { memo, useCallback, useEffect, useState } from 'react'
|
import { memo, ReactNode, useCallback, useEffect, useState } from 'react'
|
||||||
import { ColumnGroupType, ColumnType } from 'antd/lib/table'
|
import { ColumnGroupType, ColumnType } from 'antd/lib/table'
|
||||||
import { Table as RawTable, TableProps } from 'antd'
|
import { Table as RawTable, TableProps } from 'antd'
|
||||||
|
import { NamePath } from 'antd/lib/form/interface'
|
||||||
|
|
||||||
import type { OmitExtends } from '@utils/types'
|
import type { OmitExtends } from '@utils/types'
|
||||||
import { applyTableSettings, getTableSettings, setTableSettings, TableColumnSettings, TableSettings } from '@utils'
|
import { applyTableSettings, getTableSettings, setTableSettings, TableColumnSettings, TableSettings } from '@utils'
|
||||||
|
|
||||||
import TableSettingsChanger from './TableSettingsChanger'
|
import TableSettingsChanger from './TableSettingsChanger'
|
||||||
|
import { DataType, RenderMethod } from './Columns'
|
||||||
import { tryAddKeys } from './EditableTable'
|
import { tryAddKeys } from './EditableTable'
|
||||||
|
|
||||||
import '@styles/index.css'
|
import '@styles/index.css'
|
||||||
|
|
||||||
export type BaseTableColumn<T> = ColumnGroupType<T> | ColumnType<T>
|
export type BaseTableColumn<T> = ColumnGroupType<T> | ColumnType<T>
|
||||||
export type TableColumns<T> = OmitExtends<BaseTableColumn<T>, TableColumnSettings>[]
|
export type TableColumns<T> = (OmitExtends<Omit<BaseTableColumn<T>, 'key' | 'render'>, TableColumnSettings> & {
|
||||||
|
key: NamePath
|
||||||
|
render: RenderMethod<T | null>
|
||||||
|
})[]
|
||||||
|
|
||||||
export type TableContainer<T> = Omit<TableProps<T>, 'columns'> & {
|
type MergedTableColumns<T> = (OmitExtends<Omit<BaseTableColumn<T>, 'render'>, TableColumnSettings> & {
|
||||||
|
render: RenderMethod<T | null>
|
||||||
|
})[]
|
||||||
|
|
||||||
|
export type TableContainer<T> = Omit<TableProps<T>, 'columns' | 'dataSource'> & {
|
||||||
columns: TableColumns<T>
|
columns: TableColumns<T>
|
||||||
tableName?: string
|
tableName?: string
|
||||||
showSettingsChanger?: boolean
|
showSettingsChanger?: boolean
|
||||||
|
dataSource: DataType<T>[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _Table = <T extends object>({ columns, dataSource, tableName, showSettingsChanger, ...other }: TableContainer<T>) => {
|
export const keyToString = (key: NamePath): string => Array.isArray(key) ? key.join('.') : String(key)
|
||||||
const [newColumns, setNewColumns] = useState<TableColumns<T>>([])
|
|
||||||
|
const defaultRender = <T,>(data: T): ReactNode => <>{data}</>
|
||||||
|
|
||||||
|
const getValueFromKey = <T,>(key: NamePath, record: DataType<T>): T | null => {
|
||||||
|
if (!key) return null
|
||||||
|
if (!Array.isArray(key))
|
||||||
|
return record[String(key)]
|
||||||
|
if (key.length <= 0) return null
|
||||||
|
const child = record[key[0]]
|
||||||
|
if (key.length === 1) return child
|
||||||
|
if (typeof child !== 'object') return null
|
||||||
|
return getValueFromKey(key.slice(1), child as DataType<T>)
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnRenderWrapper = <T,>(key: NamePath, render?: RenderMethod<T | null>): RenderMethod<T | null> => (_: T | null, record?: DataType<T | null>, index?: number) => {
|
||||||
|
if (!record) return '-'
|
||||||
|
if (render)
|
||||||
|
return render(getValueFromKey(key, record), record, index)
|
||||||
|
return defaultRender(getValueFromKey(key, record))
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Table = <T extends Record<string, any>>({ columns, dataSource, tableName, showSettingsChanger, ...other }: TableContainer<T>) => {
|
||||||
|
const [newColumns, setNewColumns] = useState<MergedTableColumns<T>>([])
|
||||||
const [settings, setSettings] = useState<TableSettings>({})
|
const [settings, setSettings] = useState<TableSettings>({})
|
||||||
|
|
||||||
const onSettingsChanged = useCallback((settings?: TableSettings | null) => {
|
const onSettingsChanged = useCallback((settings?: TableSettings | null) => {
|
||||||
@ -31,7 +63,12 @@ const _Table = <T extends object>({ columns, dataSource, tableName, showSettings
|
|||||||
|
|
||||||
useEffect(() => setSettings(tableName ? getTableSettings(tableName) : {}), [tableName])
|
useEffect(() => setSettings(tableName ? getTableSettings(tableName) : {}), [tableName])
|
||||||
useEffect(() => setNewColumns(() => {
|
useEffect(() => setNewColumns(() => {
|
||||||
const newColumns = applyTableSettings(columns, settings)
|
const mergedColumns = applyTableSettings(columns, settings)
|
||||||
|
const newColumns = mergedColumns.map((column) => ({
|
||||||
|
...column,
|
||||||
|
render: columnRenderWrapper(column.key, column.render),
|
||||||
|
key: keyToString(column.key),
|
||||||
|
}))
|
||||||
if (tableName && showSettingsChanger) {
|
if (tableName && showSettingsChanger) {
|
||||||
const oldTitle = newColumns[0].title
|
const oldTitle = newColumns[0].title
|
||||||
newColumns[0].title = (props) => (
|
newColumns[0].title = (props) => (
|
||||||
@ -45,9 +82,9 @@ const _Table = <T extends object>({ columns, dataSource, tableName, showSettings
|
|||||||
}), [settings, columns, onSettingsChanged, showSettingsChanger, tableName])
|
}), [settings, columns, onSettingsChanged, showSettingsChanger, tableName])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RawTable<T>
|
<RawTable<DataType<T>>
|
||||||
columns={newColumns}
|
columns={newColumns}
|
||||||
dataSource={tryAddKeys<T>(dataSource as any)}
|
dataSource={tryAddKeys<DataType<T>>(dataSource)}
|
||||||
{...other}
|
{...other}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user