DESKTOP-LMJ82IJ\Eno
2025-04-13 cc2984eeb289b54cfa193dde558417c46f309e8f
rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatPanel.jsx
@@ -1,57 +1,431 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material';
import React, { useState, useRef, useEffect } from "react";
import {
    useTranslate,
    useRecordContext,
    List,
    DatagridConfigurable,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    EditButton,
    FilterButton,
    CreateButton,
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    useNotify,
    useListContext,
    FunctionField,
    TextField,
    NumberField,
    DateField,
    BooleanField,
    ReferenceField,
    TextInput,
    DateTimeInput,
    DateInput,
    SelectInput,
    NumberInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
} from 'react-admin';
import PanelTypography from "../../components/PanelTypography";
import * as Common from '@/utils/common'
    Grid, Card, Typography, Button, Checkbox, Tooltip,
    IconButton,
} from '@mui/material';
import { useTranslate, useRecordContext, useNotify } from 'react-admin';
import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
import { TreeItem2 } from "@mui/x-tree-view/TreeItem2";
import { useTreeViewApiRef } from '@mui/x-tree-view/hooks';
import FiberManualRecordIcon from '@mui/icons-material/FiberManualRecord';
import AddIcon from '@mui/icons-material/Add';
import DeleteIcon from '@mui/icons-material/Delete';
import BindMatnrModal from './BindMatnrModal';
import BindLocModal from './BindLocModal';
import ConfirmModal from "@/page/components/ConfirmModal";
import { DataGrid } from '@mui/x-data-grid';
import request from '@/utils/request';
import { haveChildren } from '@/utils/common';
const LocAreaMatPanel = () => {
    const record = useRecordContext();
    if (!record) return null;
    const translate = useTranslate();
    const notify = useNotify();
    const columns = [
        { field: 'id', headerName: 'ID', width: 100 },
        { field: 'areaId$', headerName: translate('table.field.locAreaMatRela.areaId'), width: 100 },
        { field: 'locId$', headerName: translate('table.field.locAreaMatRela.locId'), width: 100 },
        { field: 'locTypeId$', headerName: translate('table.field.locAreaMatRela.locTypeId'), width: 100 },
        { field: 'matnrId$', headerName: translate('table.field.locAreaMatRela.matnrId'), width: 100 },
        { field: 'groupId$', headerName: translate('table.field.locAreaMatRela.groupId'), width: 100 },
        {
            field: 'action',
            headerName: '操作',
            minWidth: 100,
            sticky: 'left',
            flex: 1,
            renderCell: (params) => (
                <Tooltip title="Delete">
                    <IconButton onClick={(e) => handleDelete(params.row, e)}>
                        <DeleteIcon />
                    </IconButton>
                </Tooltip>
            ),
        },
    ];
    const handleDelete = async (row, e) => {
        e.stopPropagation()
        const { data: { code, data, msg } } = await request.post(`/locAreaMatRela/remove/${row.id}`);
        if (code === 200) {
            reload()
            notify(msg);
        } else {
            notify(msg);
        }
    }
    const [parmas, setParmas] = useState({
        current: 1,
        pageSize: 99,
        areaMatId: record.id,
        locTypeId: '',
        groupId: '',
    });
    const [tableData, setTableData] = useState([]);
    const [matnrTree, setMatnrTree] = useState([]);
    const [locTree, setLocTree] = useState([]);
    useEffect(() => {
        reload()
    }, [parmas])
    const reload = () => {
        requestTable()
        requestMatnr()
        requestLocType()
    }
    const requestTable = async () => {
        const { data: { code, data, msg } } = await request.post(`/locAreaMatRela/page`, parmas);
        if (code === 200) {
            setTableData(data.records || [])
        } else {
            notify(msg);
        }
    }
    const requestMatnr = async () => {
        const { data: { code, data, msg } } = await request.get(`/locAreaMatRela/groups/${record.id}`);
        if (code === 200) {
            setMatnrTree(haveChildren(data) || [])
        } else {
            notify(msg);
        }
    }
    const requestLocType = async () => {
        const { data: { code, data, msg } } = await request.get(`/locAreaMatRela/locType/${record.id}`);
        if (code === 200) {
            setLocTree(haveChildren(data) || [])
        } else {
            notify(msg);
        }
    }
    return (
        <Grid container spacing={2}>
            {/* 物料分组 */}
            <Grid item xs={2}>
                1
                <MatnrTree matnrTree={matnrTree} parmas={parmas} setParmas={setParmas} reload={reload} />
            </Grid>
            <Grid item xs={3}>
                2
            {/* 库位类型 */}
            <Grid item xs={2}>
                <LocTree locTree={locTree} parmas={parmas} setParmas={setParmas} reload={reload} />
            </Grid>
            <Grid item xs={12}>
                3
            {/* 其他内容 */}
            <Grid item xs={8}>
                <DataGrid
                    size="small"
                    rows={tableData}
                    columns={columns}
                    checkboxSelection
                    disableColumnMenu={true}
                    disableColumnSorting
                    disableMultipleColumnsSorting
                    initialState={{
                        pagination: {
                            paginationModel: {
                                pageSize: 10,
                            },
                        },
                    }}
                    pageSizeOptions={[10]}
                />
            </Grid>
        </Grid>
    );
};
export default LocAreaMatPanel;
const MatnrTree = ({ matnrTree, parmas, setParmas, reload }) => {
    const record = useRecordContext();
    const notify = useNotify();
    function getItemDescendantsIds(item) {
        const ids = [];
        item.children?.forEach((child) => {
            ids.push(child.id);
            ids.push(...getItemDescendantsIds(child));
        });
        return ids;
    }
    const [selectedItems, setSelectedItems] = useState([]);
    const toggledItemRef = useRef({});
    const apiRef = useTreeViewApiRef();
    const handleItemSelectionToggle = (event, itemId, isSelected) => {
        event.stopPropagation()
        event.preventDefault();
        toggledItemRef.current[itemId] = isSelected;
    };
    const handleSelectedItemsChange = (event, newSelectedItems) => {
        event.stopPropagation()
        event.preventDefault();
        setSelectedItems(newSelectedItems);
        const itemsToSelect = [];
        const itemsToUnSelect = {};
        Object.entries(toggledItemRef.current).forEach(([itemId, isSelected]) => {
            const item = apiRef.current.getItem(itemId);
            if (isSelected) {
                itemsToSelect.push(...getItemDescendantsIds(item));
            } else {
                getItemDescendantsIds(item).forEach((descendantId) => {
                    itemsToUnSelect[descendantId] = true;
                });
            }
        });
        const newSelectedItemsWithChildren = Array.from(
            new Set(
                [...newSelectedItems, ...itemsToSelect].filter(
                    (itemId) => !itemsToUnSelect[itemId],
                ),
            ),
        );
        setSelectedItems(newSelectedItemsWithChildren);
        toggledItemRef.current = {};
    };
    const [addDialog, setAddDialog] = useState(false);
    const [delectDialog, setDelectDialog] = useState(false);
    const handleDelete = () => {
        if (selectedItems.length > 0) {
            setDelectDialog(true)
        } else {
            notify('请选择物料分组');
        }
    };
    const contirmDelete = async () => {
        const parmas = {
            aeaMatId: record.id,
            groupId: selectedItems
        }
        const res = await request.post(`/locAreaMatRela/group/remove`, parmas);
        if (res?.data?.code === 200) {
            reload()
            notify(res.data.msg);
        } else {
            notify(res.data.msg);
        }
    };
    const handleAdd = () => {
        setAddDialog(true)
    };
    const handleNodeSelect = (event, nodeId) => {
        event.stopPropagation();
        parmas.groupId = nodeId;
        parmas.locTypeId = '';
        setParmas(parmas)
        reload()
    };
    return (
        <Card sx={{ p: 1 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', paddingBottom: '3px', marginBottom: '3px', borderBottom: '1px dashed #d4d4d4' }}>
                <div style={{ fontSize: '17px' }}>物料分组</div>
                {/* <div style={{ display: 'flex', alignItems: 'center', gap: '3px' }}>
                    <AddIcon color={'info'} sx={{ cursor: 'pointer' }} onClick={() => handleAdd()} />
                    <DeleteIcon color={'warning'} sx={{ cursor: 'pointer' }} onClick={() => handleDelete()} />
                </div> */}
            </div>
            <RichTreeView
                expansionTrigger="iconContainer"
                // checkboxSelection
                // multiSelect
                items={matnrTree}
                apiRef={apiRef}
                getItemId={(item) => item.id}
                getItemLabel={(item) => item.name}
                defaultExpandedItems={['grid']}
                selectedItems={selectedItems}
                onSelectedItemsChange={handleSelectedItemsChange}
                onItemSelectionToggle={handleItemSelectionToggle}
                onItemClick={handleNodeSelect}
            />
            <BindMatnrModal
                open={addDialog}
                setOpen={setAddDialog}
                selectedItems={selectedItems}
                reload={reload}
            />
            <ConfirmModal
                open={delectDialog}
                setOpen={setDelectDialog}
                onConfirm={contirmDelete}
            />
        </Card>
    )
}
const LocTree = ({ locTree, setParmas, parmas, reload }) => {
    const record = useRecordContext();
    const notify = useNotify();
    function getItemDescendantsIds(item) {
        const ids = [];
        item.children?.forEach((child) => {
            ids.push(child.id);
            ids.push(...getItemDescendantsIds(child));
        });
        return ids;
    }
    const [selectedItems, setSelectedItems] = useState([]);
    const toggledItemRef = useRef({});
    const apiRef = useTreeViewApiRef();
    const handleItemSelectionToggle = (event, itemId, isSelected) => {
        toggledItemRef.current[itemId] = isSelected;
    };
    const handleSelectedItemsChange = (event, newSelectedItems) => {
        setSelectedItems(newSelectedItems);
        const itemsToSelect = [];
        const itemsToUnSelect = {};
        Object.entries(toggledItemRef.current).forEach(([itemId, isSelected]) => {
            const item = apiRef.current.getItem(itemId);
            if (isSelected) {
                itemsToSelect.push(...getItemDescendantsIds(item));
            } else {
                getItemDescendantsIds(item).forEach((descendantId) => {
                    itemsToUnSelect[descendantId] = true;
                });
            }
        });
        const newSelectedItemsWithChildren = Array.from(
            new Set(
                [...newSelectedItems, ...itemsToSelect].filter(
                    (itemId) => !itemsToUnSelect[itemId],
                ),
            ),
        );
        setSelectedItems(newSelectedItemsWithChildren);
        toggledItemRef.current = {};
    };
    const [addDialog, setAddDialog] = useState(false);
    const [delectDialog, setDelectDialog] = useState(false);
    const handleDelete = () => {
        if (selectedItems.length > 0) {
            setDelectDialog(true)
        } else {
            notify('请选择库位类型');
        }
    };
    const contirmDelete = async () => {
        const parmas = {
            areaMatId: record.id,
            typeId: selectedItems
        }
        const res = await request.post(`/locAreaMatRela/locType/remove`, parmas);
        if (res?.data?.code === 200) {
            reload()
            notify(res.data.msg);
        } else {
            notify(res.data.msg);
        }
    };
    const handleAdd = () => {
        setAddDialog(true)
    };
    const handleNodeSelect = (event, nodeId) => {
        event.stopPropagation();
        parmas.locTypeId = nodeId;
        parmas.groupId = '';
        setParmas(parmas)
        reload()
    };
    return (
        <Card sx={{ p: 1 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', paddingBottom: '3px', marginBottom: '3px', borderBottom: '1px dashed #d4d4d4' }}>
                <div style={{ fontSize: '17px' }}>库位类型</div>
                {/* <div style={{ display: 'flex', alignItems: 'center', gap: '3px' }}>
                    <AddIcon color={'info'} sx={{ cursor: 'pointer' }} onClick={() => handleAdd()} />
                    <DeleteIcon color={'warning'} sx={{ cursor: 'pointer' }} onClick={() => handleDelete()} />
                </div> */}
            </div>
            <RichTreeView
                expansionTrigger="iconContainer"
                items={locTree}
                apiRef={apiRef}
                getItemId={(item) => item.id}
                getItemLabel={(item) => item.name}
                defaultExpandedItems={['grid']}
                onItemClick={handleNodeSelect}
                onItemSelectionToggle={handleItemSelectionToggle}
                // checkboxSelection
                // multiSelect
                selectedItems={selectedItems}
                onSelectedItemsChange={handleSelectedItemsChange}
            />
            <BindLocModal
                open={addDialog}
                setOpen={setAddDialog}
                selectedItems={selectedItems}
                reload={reload}
            />
            <ConfirmModal
                open={delectDialog}
                setOpen={setDelectDialog}
                onConfirm={contirmDelete}
            />
        </Card>
    )
}