/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/ban-ts-comment */ /* eslint-disable @typescript-eslint/ban-types */ /* eslint-disable prefer-spread */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import React, { useEffect, useState, useCallback } from 'react'; import { Pagination, CnCheckbox, CnRadio } from '@alife/cn-ui'; import classNames from 'classnames'; import { domType, ColumnProps, TableProps, ItemProps, FunctionObjI, } from 'src/types/op-table'; import { Code, State, Image, Name } from './render'; import './index.scss'; function getAttr(attrs: Array, obj: any) { return attrs ?.filter?.((attr) => attr && typeof attr === 'string') ?.reduce?.((tempObj, attr) => tempObj?.[attr], obj); } function transformRender(columns: ColumnProps[], renders: FunctionObjI) { columns.forEach((col) => { const { key } = col; if (renders[key]) { col.render = renders[key]; } }); } function getDomProps(props: domType, rest: any[]) { let ret = props; if (props instanceof Function) { ret = (props as Function).apply(null, rest); } return ret; } export const renderMap = new Map(); export default function OPTable(props: TableProps) { const { dataSource: tableInitData = [], columns = [], className, renders, rowProps, cellProps, primaryKey, selectedKeys, hasHeader = true, onRowClick = () => {}, onPageChange = () => {}, pagination = { enabled: false, pageSize: 10, currentPage: 1, onPageSizeChange: () => {}, onCurrentPageChange: () => {}, }, showSelect = false, // 开启选择行 rowSelection = { type: 'multiple', }, onSelectChange = () => {}, // 选择行后的回调 autoWidth = true, // 设置自动宽度后,表格列 设的宽度失效 } = props; const [currentPage, setCurrentPage] = useState( pagination.currentPage || 1, ); const [pageSize, setPageSize] = useState(pagination.pageSize || 10); const [indeterminate, setIndeterminate] = useState(false); const [headerChecked, setHeaderChecked] = useState(false); const [selectedRowKeys, setSelectedRowKeys] = useState< string | number | Array >([]); const [selectedRowRecord, setSelectedRowRecord] = useState< ItemProps | ItemProps[] >([]); const [dataSource, setDataSource] = useState( // eslint-disable-next-line @typescript-eslint/no-unsafe-argument JSON.parse(JSON.stringify(tableInitData)), ); renders && transformRender(columns, renders); useEffect(() => { setDataSource(tableInitData); }, [tableInitData]); // 分页条数改变 const onPageSizeChange = React.useCallback( (v: number) => { setPageSize(v); setCurrentPage(1); pagination.onPageSizeChange && pagination.onPageSizeChange(v); onPageChange && onPageChange({ currentPage: 1, pageSize: v, }); }, [pagination, onPageChange], ); // 翻页改变 const onCurrentPageChange = React.useCallback( (v: number) => { setCurrentPage(v); pagination.onCurrentPageChange && pagination.onCurrentPageChange(v); onPageChange && onPageChange({ currentPage: v, pageSize, }); }, [pagination, onPageChange, pageSize], ); const handleRowSelectionChangeMultiple = useCallback( (keys: (string | number)[]) => { const records: ItemProps[] = []; const tmpDataSource = dataSource; // 过滤传入的 keys 不存在于 dataSource 中 keys = keys.filter( (k) => !!dataSource.filter( (item, index) => k === (primaryKey ? item[primaryKey] : index), ).length, ); if (keys && Array.isArray(keys)) { tmpDataSource.forEach((recordItem, recordIndex) => { if ( // eslint-disable-next-line @typescript-eslint/no-unsafe-argument keys.includes(primaryKey ? recordItem[primaryKey] : recordIndex) ) { records.push(recordItem); recordItem.checked = true; recordItem.selected = true; } else { recordItem.checked = false; recordItem.selected = false; } }); setHeaderChecked(keys.length === tmpDataSource.length); setIndeterminate( !!(keys.length && keys.length !== tmpDataSource.length), ); setSelectedRowKeys(keys); setSelectedRowRecord(records); setDataSource(tmpDataSource); onSelectChange && onSelectChange(keys, records); } }, [dataSource, onSelectChange, primaryKey], ); // 行选中状态改变-多选 const onCheckedChangeFromTd = useCallback( (checked: boolean, record: ItemProps, index: number) => { let keys: (string | number)[] = selectedRowKeys as string[]; const checkedKey = (primaryKey ? record[primaryKey] : index) as string; if (checked) { keys.push(checkedKey); } else { keys = keys.filter((k) => k !== checkedKey); } handleRowSelectionChangeMultiple(keys); }, [handleRowSelectionChangeMultiple, primaryKey, selectedRowKeys], ); const handleRowSelectionChangeSingle = useCallback( (checkedKey: string | number, checked: boolean) => { const tmpDataSource = dataSource; let record: ItemProps = {}; tmpDataSource.forEach((recordItem, recordIndex) => { if ( checkedKey === (primaryKey ? recordItem[primaryKey] : recordIndex) ) { recordItem.checked = checked; recordItem.selected = true; record = recordItem; } else { recordItem.checked = false; recordItem.selected = false; } }); setSelectedRowKeys(checkedKey); setSelectedRowRecord(record); setDataSource(tmpDataSource); onSelectChange && onSelectChange(checkedKey, record); }, [dataSource, onSelectChange, primaryKey], ); // 行选中状态改变-单选 const onCheckedChangeFromTdSingle = useCallback( (checked: boolean, record: ItemProps, index: number) => { const checkedKey = (primaryKey ? record[primaryKey] : index) as string; handleRowSelectionChangeSingle(checkedKey, checked); }, [handleRowSelectionChangeSingle, primaryKey], ); useEffect(() => { // 初始默认选中行 if (showSelect && rowSelection.selectedRowKeys) { rowSelection.type === 'single' ? handleRowSelectionChangeSingle( rowSelection.selectedRowKeys as string, !!rowSelection.selectedRowKeys, ) : handleRowSelectionChangeMultiple( rowSelection.selectedRowKeys as string[], ); } }, [ handleRowSelectionChangeMultiple, handleRowSelectionChangeSingle, rowSelection.selectedRowKeys, rowSelection.type, showSelect, ]); // 表头选中状态改变 const onCheckedChangeFromTh = useCallback( (checked: boolean) => { setHeaderChecked(checked); setIndeterminate(false); if (checked) { handleRowSelectionChangeMultiple( dataSource.map( (item, index) => (primaryKey ? item[primaryKey] : index) as string, ), ); } if (!checked) { handleRowSelectionChangeMultiple([]); } }, [dataSource, handleRowSelectionChangeMultiple, primaryKey], ); return (
{showSelect && } {columns.map((item) => ( ))} {hasHeader && ( {showSelect && ( )} {columns.map((item) => { return ( ); })} )} {dataSource.map((item, index) => { const currentKey: string | number = primaryKey ? item[primaryKey] : index; return ( { if (showSelect && rowSelection.type === 'single') { handleRowSelectionChangeSingle(currentKey, true); } if (showSelect && rowSelection.type !== 'single') { let newKeys: (string | number)[] = []; if (item.checked) { if (Array.isArray(selectedRowKeys)) { newKeys = selectedRowKeys.filter( (k: string | number) => currentKey !== k, ); } } else { if (Array.isArray(selectedRowKeys)) { newKeys = selectedRowKeys.concat([currentKey]); } } handleRowSelectionChangeMultiple(newKeys); } onRowClick(item, index); }} > {showSelect && ( )} {columns.map((column, colIndex) => { return ( ); })} ); })}
{rowSelection.type === 'single' ? null : ( )} {item.title || item.name}
{rowSelection.type === 'single' ? ( { onCheckedChangeFromTdSingle(checked, item, index); }} /> ) : ( { onCheckedChangeFromTd(checked, item, index); }} /> )} {renderTD(item, column, index)}
{pagination && pagination?.enabled && (
)}
); } function getRowClassName(data: ItemProps, props: TableProps) { const { primaryKey, selectedKeys, completeKeys } = props; const keyValue: string = data[primaryKey as string]; return classNames({ selected: data.selected || Array.from(selectedKeys ?? '').includes(keyValue), complete: data.complete || data.completed || Array.from(completeKeys ?? '').includes(keyValue), }); } function getAlignTdClassName(align: string) { return ( { right: 'td-align-right', left: 'td-align-left', center: 'td-align-center', }[align] || 'td-align-left' ); } function getTdSerialClassName( index: number, total: number, showSelect: boolean, ) { return classNames('operate-table-cell', { 'operate-table-cell__first': !showSelect && index === 0, 'operate-table-cell__last': index === total, }); } // 2023.2.27 render优化 function renderTD(record: ItemProps, column: ColumnProps, index: number) { const { key, render, options } = column; const value: string = getAttr([...(key?.split?.('.') || [])], record); const renderProps = { value, index, record, options }; if (render instanceof Function) { // 自定义render return render(value, index, record, options) as FunctionObjI; } if (typeof render === 'string') { // 内置render const renderName = render.toLowerCase(); switch (renderName) { case 'code': return ; case 'pkstatus': return ; case 'image': return ; case 'name': return ; default: return value; } } return value; }