From 44d11d964d630df9226b332bc0aa6b5c071841dc Mon Sep 17 00:00:00 2001 From: goodmice Date: Thu, 6 Oct 2022 20:27:58 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=B0=D1=87=D0=B0=D1=82=D0=B0=20=D1=82?= =?UTF-8?q?=D0=B8=D0=BF=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20EditableTabl?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{EditableTable.jsx => EditableTable.tsx} | 124 ++++++++++++------ 1 file changed, 82 insertions(+), 42 deletions(-) rename src/components/Table/{EditableTable.jsx => EditableTable.tsx} (64%) mode change 100755 => 100644 diff --git a/src/components/Table/EditableTable.jsx b/src/components/Table/EditableTable.tsx old mode 100755 new mode 100644 similarity index 64% rename from src/components/Table/EditableTable.jsx rename to src/components/Table/EditableTable.tsx index a94955e..019c588 --- a/src/components/Table/EditableTable.jsx +++ b/src/components/Table/EditableTable.tsx @@ -1,39 +1,72 @@ -import { memo, useCallback, useState, useEffect, useMemo } from 'react' -import { Form, Button, Popconfirm } from 'antd' +import { memo, useCallback, useState, useEffect, useMemo, Dispatch, SetStateAction, ReactNode } from 'react' +import { Form, Button, Popconfirm, TableProps } 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 { Table, TableColumns } from '.' import { EditableCell } from './EditableCell' +import { ColumnsType } from 'antd/lib/table' +import { ArrayElement } from '@asb/utils/types' const newRowKeyValue = 'newRow' -const actions = { +type CrudObject = { + id?: string | number + key?: string | number +} + +interface CrudService { + insert: (((data: T) => Promise) | ((idWell: number, data: T) => Promise)) + insertRange: (((data: T[]) => Promise) | ((idWell: number, data: T[]) => Promise)) + update: (((data: T) => Promise | ((idWell: number, data: T) => Promise) | ((idWell: number, id: number, data: T) => Promise))) + delete: (((id: number) => Promise) | ((idWell: number, id: number) => Promise)) + new(...args: any[]): any +} + +type TableAction = Record, (data: T, idWell?: number, idRecord?: number) => any[]> + +const makeActions = (): TableAction => ({ 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], +}) + +type TableActionProps> = { + service?: S + permission?: string | string[] + action?: keyof CrudService + actionName?: string + recordParser?: (record: T) => T + idWell?: number + idRecord?: number + setLoader?: Dispatch> + errorMsg?: string + onComplete?: () => Promise } -export const makeTableAction = ({ +export const makeTableAction = >({ service, permission, action, actionName, recordParser, idWell, - idRecord = false, + idRecord, setLoader, errorMsg = 'Не удалось выполнить операцию', onComplete, -}) => hasPermission(permission) && service && action && ( - (record) => invokeWebApiWrapperAsync( +}: TableActionProps) => { + if (!hasPermission(permission) || !service || !action) return async (record: T) => {} + const actions = makeActions() + return async (record: T) => await invokeWebApiWrapperAsync( async () => { const data = recordParser?.(record) ?? record const params = actions[action]?.(data, idWell, idRecord).filter(Boolean) if (params?.length > 0) + //@ts-ignore await service[action](...params) await onComplete?.() }, @@ -41,25 +74,34 @@ export const makeTableAction = ({ errorMsg, { actionName } ) -) +} -export const tryAddKeys = (items) => { - if (!items?.length || !items[0]) - return [] - if (items[0].key) - return items +export const tryAddKeys = >(items?: T[]): T[] => { + if (!items?.length || !items[0]) return items ?? [] + if (items[0].key) return items return items.map((item, index) => ({...item, key: item.key ?? item.id ?? index })) } const EditableTableComponents = { body: { cell: EditableCell }} +export type EditableTableProps> = TableProps & { + columns: ColumnsType & TableColumns + dataSource?: T[] + onChange + onRowAdd?: TableActionProps | ((record: T) => Promise) + onRowEdit?: TableActionProps | ((record: T) => Promise) + onRowDelete?: TableActionProps | ((record: T) => Promise) + additionalButtons?: (record: T, editingKey: string | number) => ReactNode + buttonsWidth +} + /** * @param onChange - Метод вызывается со всем dataSource с измененными элементами после любого действия * @param onRowAdd - Метод вызывается с новой добавленной записью. Если метод не определен, то кнопка добавления строки не показывается * @param onRowEdit - Метод вызывается с новой отредактированной записью. Если метод не поределен, то кнопка редактирования строки не показывается * @param onRowDelete - Метод вызывается с удаленной записью. Если метод не поределен, то кнопка удаления строки не показывается */ -export const EditableTable = memo(({ +const _EditableTable = >({ columns, dataSource, onChange, @@ -69,19 +111,20 @@ export const EditableTable = memo(({ additionalButtons, buttonsWidth, ...otherTableProps -}) => { +}: EditableTableProps) => { const [form] = Form.useForm() - const [data, setData] = useState(tryAddKeys(dataSource)) - const [editingKey, setEditingKey] = useState('') + const [data, setData] = useState(tryAddKeys(dataSource)) + const [editingKey, setEditingKey] = useState('') const onAdd = useMemo(() => onRowAdd && typeof onRowAdd !== 'function' ? makeTableAction(onRowAdd) : onRowAdd, [onRowAdd]) const onEdit = useMemo(() => onRowEdit && typeof onRowEdit !== 'function' ? makeTableAction(onRowEdit) : onRowEdit, [onRowEdit]) const onDelete = useMemo(() => onRowDelete && typeof onRowDelete !== 'function' ? makeTableAction(onRowDelete) : onRowDelete, [onRowDelete]) - const isEditing = useCallback((record) => record?.key === editingKey, [editingKey]) + const isEditing = useCallback((record: T) => record?.key === editingKey, [editingKey]) - const edit = useCallback((record) => { + const edit = useCallback((record: T) => { + if (!record.key) return form.setFieldsValue({...record}) setEditingKey(record.key) }, [form]) @@ -97,17 +140,12 @@ export const EditableTable = memo(({ }, [data, editingKey]) const addNewRow = useCallback(async () => { - let newRow = { - ...form.initialValues, - key:newRowKeyValue - } - - const newData = [newRow, ...data] - setData(newData) + const newRow: any = { key: newRowKeyValue } // TODO: Исправить тип + setData((prevData) => [newRow, ...prevData]) edit(newRow) - }, [data, edit, form.initialValues]) + }, [edit]) - const save = useCallback(async (record) => { + const save = useCallback(async (record: T) => { try { const row = await form.validateFields() const newData = [...data] @@ -126,13 +164,13 @@ export const EditableTable = memo(({ if (isAdding) try { - onAdd(newItem) + onAdd?.(newItem) } catch (err) { console.log('callback onRowAdd fault:', err) } else try { - onEdit(newItem) + onEdit?.(newItem) } catch (err) { console.log('callback onRowEdit fault:', err) } @@ -147,18 +185,18 @@ export const EditableTable = memo(({ } }, [data, editingKey, form, onChange, onAdd, onEdit]) - const deleteRow = useCallback((record) => { + const deleteRow = useCallback((record: T) => { const newData = [...data] const index = newData.findIndex((item) => record.key === item.key) newData.splice(index, 1) setData(newData) - onDelete(record) + onDelete?.(record) onChange?.(newData) }, [data, onChange, onDelete]) - const handleColumn = useCallback((col) => { + const handleColumn = useCallback((col: any) => { // TODO: Исправить тип if (col.children) col.children = col.children.map(handleColumn) @@ -167,7 +205,7 @@ export const EditableTable = memo(({ return { ...col, - onCell: (record) => ({ + onCell: (record: T) => ({ ...col.onCell?.(record), editing: isEditing(record), record, @@ -194,7 +232,7 @@ export const EditableTable = memo(({ /> ), dataIndex: 'operation', - render: (_, record) => isEditing(record) ? ( + render: (_: any, record: T) => isEditing(record) ? (