diff --git a/src/components/Table/EditableTable.jsx b/src/components/Table/EditableTable.jsx index e93a36d..538bbd2 100755 --- a/src/components/Table/EditableTable.jsx +++ b/src/components/Table/EditableTable.jsx @@ -1,223 +1,267 @@ -import { memo, useCallback, useState, useEffect } from 'react' +import { memo, useCallback, useState, useEffect, useMemo } from 'react' import { Form, Button, Popconfirm } from 'antd' import { EditOutlined, SaveOutlined, PlusOutlined, CloseCircleOutlined, DeleteOutlined } from '@ant-design/icons' import { invokeWebApiWrapperAsync } from '@components/factory' +import { hasPermission } from '@utils' import { Table } from '.' import { EditableCell } from './EditableCell' const newRowKeyValue = 'newRow' -const actions = [ - [['insert'], (data) => [data]], - [['insertRange'], (data) => [[data].flat(1)]], - [['edit', 'update', 'put'], (data) => data.id && [data.id, data]], - [['delete'], (data) => data.id && [data.id]], -] +const actions = { + insert: (data, idWell) => [idWell, data], + insertRange: (data, idWell) => [idWell, [data].flat(1)], + update: (data, idWell, idRecord) => [idWell, idRecord && data.id, data], + delete: (data, idWell) => [idWell, data.id], +} -export const makeActionHandler = (action, { idWell, service, setLoader, errorMsg, onComplete }, recordParser, actionName) => service && action && ( - (record) => invokeWebApiWrapperAsync( - async () => { - const addIdWell = (...params) => idWell ? [idWell, ...params] : params - if (typeof recordParser === 'function') - record = recordParser(record) - - const actionId = actions.findIndex((elm) => elm[0].includes(action)) - const params = actions[actionId]?.[1](record) - - if (params) await service[action](...addIdWell(...params)) - await onComplete?.() - }, +export const makeTableAction = ({ + service, + permission, + action, + actionName, + recordParser, + idWell, + idRecord = false, setLoader, - errorMsg, - actionName - ) + errorMsg = 'Не удалось выполнить операцию', + onComplete, +}) => hasPermission(permission) && service && action && ( + (record) => invokeWebApiWrapperAsync( + async () => { + const data = recordParser?.(record) ?? record + const params = actions[action]?.(data, idWell, idRecord).filter(Boolean) + if (params?.length > 0) + await service[action](...params) + await onComplete?.() + }, + setLoader, + errorMsg, + actionName + ) +) + +// TODO: Remove makeActionHandler +const actions2 = { + insert: (data) => [data], + insertRange: (data) => [[data].flat(1)], + update: (data) => data.id && [data.id, data], + delete: (data) => data.id && [data.id], +} + +/** + * @deprecated используйте {@link makeTableAction} + */ +export const makeActionHandler = (action, { idWell, service, setLoader, errorMsg, onComplete }, recordParser, actionName) => service && action && ( + (record) => invokeWebApiWrapperAsync( + async () => { + const addIdWell = (...params) => idWell ? [idWell, ...params] : params + if (typeof recordParser === 'function') + record = recordParser(record) + + const params = actions2[action]?.(record) + + if (params) await service[action](...addIdWell(...params)) + await onComplete?.() + }, + setLoader, + errorMsg, + actionName + ) ) export const tryAddKeys = (items) => { - if (!items?.length || !items[0]) - return [] - if (items[0].key) - return items - return items.map((item, index) => ({...item, key: item.key ?? item.id ?? index })) + if (!items?.length || !items[0]) + return [] + if (items[0].key) + return items + return items.map((item, index) => ({...item, key: item.key ?? item.id ?? index })) } +const EditableTableComponents = { body: { cell: EditableCell }} + +/** + * @param onChange - Метод вызывается со всем dataSource с измененными элементами после любого действия + * @param onRowAdd - Метод вызывается с новой добавленной записью. Если метод не определен, то кнопка добавления строки не показывается + * @param onRowEdit - Метод вызывается с новой отредактированной записью. Если метод не поределен, то кнопка редактирования строки не показывается + * @param onRowDelete - Метод вызывается с удаленной записью. Если метод не поределен, то кнопка удаления строки не показывается + */ export const EditableTable = memo(({ - columns, - dataSource, - onChange, // Метод вызывается со всем dataSource с измененными элементами после любого действия - onRowAdd, // Метод вызывается с новой добавленной записью. Если метод не определен, то кнопка добавления строки не показывается - onRowEdit, // Метод вызывается с новой отредактированной записью. Если метод не поределен, то кнопка редактирования строки не показывается - onRowDelete, // Метод вызывается с удаленной записью. Если метод не поределен, то кнопка удаления строки не показывается - additionalButtons, - buttonsWidth, - ...otherTableProps + columns, + dataSource, + onChange, + onRowAdd, + onRowEdit, + onRowDelete, + additionalButtons, + buttonsWidth, + ...otherTableProps }) => { - const [form] = Form.useForm() - const [data, setData] = useState(tryAddKeys(dataSource)) - const [editingKey, setEditingKey] = useState('') + const [form] = Form.useForm() + const [data, setData] = useState(tryAddKeys(dataSource)) + const [editingKey, setEditingKey] = useState('') - useEffect(() => { - setData(tryAddKeys(dataSource)) - }, [dataSource]) + const onAdd = useMemo(() => typeof onRowAdd === 'function' ? onRowAdd : makeTableAction(onRowAdd), [onRowAdd]) + const onEdit = useMemo(() => typeof onRowEdit === 'function' ? onRowEdit : makeTableAction(onRowEdit), [onRowEdit]) + const onDelete = useMemo(() => typeof onRowDelete === 'function' ? onRowDelete : makeTableAction(onRowDelete), [onRowDelete]) - const isEditing = useCallback((record) => record?.key === editingKey, [editingKey]) + const isEditing = useCallback((record) => record?.key === editingKey, [editingKey]) - const edit = useCallback((record) => { - form.setFieldsValue({...record}) - setEditingKey(record.key) - }, [form]) + const edit = useCallback((record) => { + form.setFieldsValue({...record}) + setEditingKey(record.key) + }, [form]) - const cancel = useCallback(() => { - if (editingKey === newRowKeyValue) { - const newData = [...data] - const index = newData.findIndex((item) => newRowKeyValue === item.key) - newData.splice(index, 1) - setData(newData) - } - setEditingKey('') - }, [data, editingKey]) - - const addNewRow = useCallback(async () => { - let newRow = { - ...form.initialValues, - key:newRowKeyValue - } - - const newData = [newRow, ...data] - setData(newData) - edit(newRow) - }, [data, edit, form.initialValues]) - - const save = useCallback(async (record) => { - try { - const 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 { - onRowAdd(newItem) - } catch (err) { - console.log('callback onRowAdd fault:', err) + const cancel = useCallback(() => { + if (editingKey === newRowKeyValue) { + const newData = [...data] + const index = newData.findIndex((item) => newRowKeyValue === item.key) + newData.splice(index, 1) + setData(newData) } - else - try { - onRowEdit(newItem) - } catch (err) { - console.log('callback onRowEdit fault:', err) + setEditingKey('') + }, [data, editingKey]) + + const addNewRow = useCallback(async () => { + let newRow = { + ...form.initialValues, + key:newRowKeyValue } - try { + const newData = [newRow, ...data] + setData(newData) + edit(newRow) + }, [data, edit, form.initialValues]) + + const save = useCallback(async (record) => { + try { + const 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) { + console.log('Validate Failed:', errInfo) + } + }, [data, editingKey, form, onChange, onAdd, onEdit]) + + const deleteRow = useCallback((record) => { + const newData = [...data] + const index = newData.findIndex((item) => record.key === item.key) + + newData.splice(index, 1) + setData(newData) + + onDelete(record) onChange?.(newData) - } catch (err) { - console.log('callback onChange fault:', err) - } + }, [data, onChange, onDelete]) - } catch (errInfo) { - console.log('Validate Failed:', errInfo) - } - }, [data, editingKey, form, onChange, onRowAdd, onRowEdit]) + const handleColumn = useCallback((col) => { + if (col.children) + col.children = col.children.map(handleColumn) - const deleteRow = useCallback((record) => { - const newData = [...data] - const index = newData.findIndex((item) => record.key === item.key) + if (!col.editable) + return col - newData.splice(index, 1) - setData(newData) + return { + ...col, + onCell: (record) => ({ + ...col.onCell?.(record), + editing: isEditing(record), + record, + dataIndex: col.dataIndex ?? col.key, + key: col.key ?? col.dataIndex, + input: col.input, + isRequired: col.isRequired, + title: col.title, + datatype: col.datatype, + formItemClass: col.formItemClass, + formItemRules: col.formItemRules, + initialValue: col.initialValue, + }), + } + }, [isEditing]) - onRowDelete(record) - onChange?.(newData) - }, [data, onChange, onRowDelete]) + const operationColumn = useMemo(() => ({ + width: buttonsWidth ?? 82, + title: !!onAdd && ( +