import { forwardRef, useContext, useEffect, useImperativeHandle, useState } from "react" //import { getEditCellRender } from './RenderCell' import { DataGrid, GridRowId, GridRowModel, useGridApiRef } from "@mui/x-data-grid" import { Button, Stack } from "@mui/material" import { EditorProperties, getDefaultValue } from "@iplusplus/y-model" import { checkCanEdit } from "../convention" import { EditorContext } from "../editor-context" import SwapVertIcon from '@mui/icons-material/SwapVert'; import AddIcon from '@mui/icons-material/Add'; import RemoveIcon from '@mui/icons-material/Remove'; import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; import { CustomIcon } from "../ui-utils" function translateNumberValue(properties: EditorProperties, newRow: GridRowModel, oldRow: GridRowModel) { const result = { ...newRow } properties.forEach(p => { if (p.valueType.dataTypeName === "number" && typeof result[p.name] !== "number") { let newNumberValue = parseFloat(result[p.name]) if (isNaN(newNumberValue)) newNumberValue = oldRow[p.name] result[p.name] = newNumberValue } }) return result } function createProcessRowUpdate( properties: EditorProperties, onRowUpdate: (newData: Record) => void, ) { return (newRow: GridRowModel, oldRow: GridRowModel) => new Promise(resolve => { const newRowCopy = translateNumberValue(properties, newRow, oldRow) resolve(newRowCopy) onRowUpdate(newRowCopy) }) } export type ObjectArrayEditorHandler = { getData(): Record[] } let gTmpId = 0 function uid() { return gTmpId++ } function assignId(data: Record[]): Record[] { return data.map(d => ({ id: uid(), ...d, })) } function swapArrayItem(arr: unknown[], pos1: number, pos2: number) { const tmp = arr[pos1] arr[pos1] = arr[pos2] arr[pos2] = tmp } export type ObjectArrayEditorDetail = { disableAdd?: boolean, disableDelete?: boolean } export type ObjectArrayEditorProps = { properties: EditorProperties data: Record[] newRowProvider?: (currentRows: Record[]) => Promise[]> onlyEdit?: boolean, refreshByData?: boolean, details?: ObjectArrayEditorDetail } function reverseArrayByIndexes(arr:T[],indexes:number[]){ //这个函数用于逆序一个数组中的指定项, arr表示目标数组, indexes表示需要逆序的项的下标 // 示例输出: reverseArrayByIndexes([0,1,2,3,4,5],[1,3,4])=>[0,4,2,3,1,5] const values = indexes.map(i => arr[i]); const reversed = values.reverse(); const result = [...arr]; indexes.forEach((idx, i) => { result[idx] = reversed[i]; }); return result; } export const ObjectArrayEditor = forwardRef< ObjectArrayEditorHandler, ObjectArrayEditorProps >(function ({ properties, data, newRowProvider, onlyEdit, refreshByData, details }, ref) { const [rows, setRows] = useState(() => assignId(data)) // const [currentData, setCurrentData] = useState(data); const apiRef = useGridApiRef() const [lastEditField, setLastEditField] = useState("") const disableAdd = details && details.disableAdd; const disableDelete = details && details.disableDelete; useEffect(() => { if (refreshByData) { setRows(() => assignId(data)) } }, [refreshByData, data]) const getData: () => Record[] = () => { // const rowModels = apiRef.current.getRowModels(); const result: Record[] = [] rows.forEach(v => { //make eslint happy const { id: _id, ...remains } = v result.push(remains) }) return result } useImperativeHandle(ref, () => ({ getData, })) const processRowUpdate = createProcessRowUpdate(properties, newRow => { if (!lastEditField) return console.log(lastEditField) const newValue = newRow[lastEditField] const selectRows = apiRef.current.getSelectedRows() const newRows = rows.map(r => { const id = r.id as GridRowId if (id === newRow.id || selectRows.get(id)) { return { ...r, [lastEditField]: newValue } } return r }) console.log(newRows) setRows(newRows) }) const addRowDefault = () => { const newRow = getDefaultValue(properties) newRow.id = uid() const newRows = [...rows, newRow] console.log(newRows) setRows(newRows) } const addRow = newRowProvider ? () => { newRowProvider(rows).then(newRows => { const newRows3 = assignId(newRows) const newRows2 = [...rows, ...newRows3] console.log(newRows2) setRows(newRows2) }) } : addRowDefault const deleteRow = () => { const selectedRows = apiRef.current.getSelectedRows() const newRows = rows.filter(r => !selectedRows.has(r.id as GridRowId)) setRows(newRows) } const getSelectedIndexes = () => { const reuslt: number[] = [] const selectedRows = apiRef.current.getSelectedRows() selectedRows.forEach((_v, k) => { const idx = rows.findIndex(r => r.id === k) console.log(idx) if (idx >= 0) { reuslt.push(idx) } }) return reuslt.sort() } const rowsUp = () => { const mock = {} const newRows = [mock, ...rows] const indexes = getSelectedIndexes() indexes.forEach(idx => { swapArrayItem(newRows, idx, idx + 1) }) setRows(newRows.filter(r => r !== mock)) } const rowsDown = () => { const mock = {} const newRows = [...rows, mock] const indexes = getSelectedIndexes().reverse() indexes.forEach(idx => { swapArrayItem(newRows, idx + 1, idx) }) setRows(newRows.filter(r => r !== mock)) } const rowsReverse = () => { const indexes = getSelectedIndexes() if(indexes.length > 1){ const newRows = reverseArrayByIndexes(rows,indexes); setRows(newRows); } } const context = useContext(EditorContext) const columns = properties.map(p => ({ field: p.name, headerName: p.title, editable: checkCanEdit(p), // type: 'string', sortable: false, renderEditCell: context.editCellRenderBuilder(p), renderCell: context.cellRenderBuilder(p), disableColumnMenu: true, flex: 1, })) const menu = !onlyEdit ? ( {!disableAdd ? } tooltip="添加新项" handler={addRow} /> : null} {!disableDelete ? } tooltip="删除选中项" handler={deleteRow} /> : null} } tooltip="上移选中项" handler={rowsUp} /> } tooltip="下移选中项" handler={rowsDown} /> } tooltip="倒序选中项" handler={rowsReverse} /> ) : null return ( <> {menu} {/* */} { console.log(p.field) setLastEditField(p.field) }} disableRowSelectionOnClick /> {/* */} ) })