verou
2025-03-24 b2835c4388aa4abdbd41a7ab7592eb1a1804947a
feat:逻辑分区
6个文件已修改
3个文件已添加
936 ■■■■■ 已修改文件
rsf-admin/src/page/basicInfo/loc/BindModal.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/locAreaMat/BindLocModal.jsx 220 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/locAreaMat/BindMatnrModal.jsx 216 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatCreate.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatEdit.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatList.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatPanel.jsx 434 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/matnr/MatnrList.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/components/ConfirmModal.jsx 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/loc/BindModal.jsx
@@ -41,8 +41,6 @@
    IconButton,
    styled,
} from '@mui/material';
import DialogCloseButton from "../../components/DialogCloseButton";
import DictionarySelect from "../../components/DictionarySelect";
rsf-admin/src/page/basicInfo/locAreaMat/BindLocModal.jsx
New file
@@ -0,0 +1,220 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    CreateBase,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SaveButton,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    Toolbar,
    required,
    useDataProvider,
    useNotify,
    Form,
    useCreateController,
    useListContext,
    useRefresh,
    SelectArrayInput,
    useRecordContext,
} from 'react-admin';
import {
    Dialog,
    DialogActions,
    DialogContent,
    DialogTitle,
    Grid,
    TextField,
    Box,
    Button,
    Paper,
    TableContainer,
    Table,
    TableHead,
    TableBody,
    TableRow,
    TableCell,
    Tooltip,
    IconButton,
    styled,
} from '@mui/material';
import DialogCloseButton from "../../components/DialogCloseButton";
import DictionarySelect from "../../components/DictionarySelect";
import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form";
import SaveIcon from '@mui/icons-material/Save';
import request from '@/utils/request';
import { Add, Edit, Delete } from '@mui/icons-material';
import _ from 'lodash';
import { DataGrid } from '@mui/x-data-grid';
import StatusSelectInput from "../../components/StatusSelectInput";
import TreeSelectInput from "@/page/components/TreeSelectInput";
const BindMatnrModal = ({ open, setOpen, reload }) => {
    const refresh = useRefresh();
    const translate = useTranslate();
    const record = useRecordContext();
    const notify = useNotify();
    const [formData, setFormData] = useState({
        areaId: null,
        warehouseId: null,
        matnrId: null,
        typeId: null,
    });
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
            reset()
        }
    };
    const reset = () => {
        setFormData({
            areaId: null,
            matnrId: null,
            warehouseId: null,
            typeId: null,
        })
    }
    const handleChange = (value, name) => {
        setFormData((prevData) => ({
            ...prevData,
            [name]: value
        }));
        refresh()
    };
    const removeEmptyKeys = (obj) => {
        return _.pickBy(obj, (value) => {
            if (_.isObject(value)) {
                const newObj = removeEmptyKeys(value);
                return !_.isEmpty(newObj);
            }
            return !_.isNil(value) && (_.isNumber(value) ? value !== 0 : !_.isEmpty(value));
        });
    }
    const handleSubmit = async () => {
        const parmas = {
            areaMatId: record.id,
            matnrId: formData.matnrId,
            areaId: formData.areaId,
            warehouseId: formData.warehouseId,
            typeId: formData.typeId,
        }
        const res = await request.post(`/locAreaMatRela/matnr/bind`, parmas);
        if (res?.data?.code === 200) {
            handleClose()
            reload()
        } else {
            notify(res.data.msg);
        }
    }
    const [groupId, setGroupId] = useState();
    const warehouseChange = (e) => {
        setGroupId(e.target.value)
    }
    return (
        <Dialog open={open} maxWidth="md" fullWidth>
            <Form onSubmit={handleSubmit}>
                <DialogCloseButton onClose={handleClose} />
                <DialogTitle>{translate('toolbar.bindmatnr')}</DialogTitle>
                <DialogContent sx={{ mt: 2 }}>
                    <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
                        <Grid container spacing={2}>
                            <Grid item xs={4}>
                                <ReferenceInput
                                    source="warehouseId"
                                    reference="warehouse"
                                >
                                    <AutocompleteInput
                                        label="table.field.loc.warehouseId"
                                        optionText="name"
                                        onChange={(value) => handleChange(value, 'warehouseId')}
                                        value={formData.warehouseId}
                                        validate={required()}
                                        filterToQuery={(val) => ({ name: val })}
                                    />
                                </ReferenceInput>
                            </Grid>
                            <Grid item xs={4}>
                                <ReferenceInput
                                    source="areaId"
                                    reference="warehouseAreas"
                                >
                                    <AutocompleteInput
                                        label="table.field.loc.areaId"
                                        optionText="name"
                                        onChange={(value) => handleChange(value, 'areaId')}
                                        value={formData.areaId}
                                        validate={required()}
                                        filterToQuery={(val) => ({ name: val })}
                                    />
                                </ReferenceInput>
                            </Grid>
                            <Grid item xs={4}>
                                <ReferenceArrayInput source="typeId" reference="locType" >
                                    <SelectArrayInput
                                        label="table.field.locAreaMatRela.locTypeId"
                                        validate={required()}
                                        optionText={'name'}
                                        value={formData.typeId}
                                        onChange={(e) => handleChange(e.target.value, 'typeId')}
                                    />
                                </ReferenceArrayInput>
                            </Grid>
                            <Grid item xs={4}>
                                <ReferenceArrayInput source="matnrId" reference="matnr" filter={{ groupId: formData.groupId }}>
                                    <SelectArrayInput
                                        label="table.field.locAreaMatRela.matnrId"
                                        validate={required()}
                                        value={formData.matnrId}
                                        onChange={(e) => handleChange(e.target.value, 'matnrId')}
                                    />
                                </ReferenceArrayInput>
                            </Grid>
                        </Grid>
                    </Box>
                </DialogContent>
                <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                    <Box sx={{ width: '100%', display: 'flex', justifyContent: 'space-between' }}>
                        <Button type="submit" variant="contained" startIcon={<SaveIcon />}>
                            {translate('toolbar.confirm')}
                        </Button>
                    </Box>
                </DialogActions>
            </Form>
        </Dialog>
    );
}
export default BindMatnrModal;
rsf-admin/src/page/basicInfo/locAreaMat/BindMatnrModal.jsx
New file
@@ -0,0 +1,216 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    CreateBase,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SaveButton,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    Toolbar,
    required,
    useDataProvider,
    useNotify,
    Form,
    useCreateController,
    useListContext,
    useRefresh,
    SelectArrayInput,
    useRecordContext,
} from 'react-admin';
import {
    Dialog,
    DialogActions,
    DialogContent,
    DialogTitle,
    Grid,
    TextField,
    Box,
    Button,
    Paper,
    TableContainer,
    Table,
    TableHead,
    TableBody,
    TableRow,
    TableCell,
    Tooltip,
    IconButton,
    styled,
} from '@mui/material';
import DialogCloseButton from "../../components/DialogCloseButton";
import DictionarySelect from "../../components/DictionarySelect";
import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form";
import SaveIcon from '@mui/icons-material/Save';
import request from '@/utils/request';
import { Add, Edit, Delete } from '@mui/icons-material';
import _ from 'lodash';
import { DataGrid } from '@mui/x-data-grid';
import StatusSelectInput from "../../components/StatusSelectInput";
import TreeSelectInput from "@/page/components/TreeSelectInput";
const BindMatnrModal = ({ open, setOpen, reload }) => {
    const refresh = useRefresh();
    const translate = useTranslate();
    const record = useRecordContext();
    const notify = useNotify();
    const [formData, setFormData] = useState({
        areaId: null,
        warehouseId: null,
        groupId: null,
        locId: null,
    });
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
            reset()
        }
    };
    const reset = () => {
        setFormData({
            areaId: null,
            warehouseId: null,
            groupId: null,
            locId: null,
        })
    }
    const handleChange = (value, name) => {
        setFormData((prevData) => ({
            ...prevData,
            [name]: value
        }));
        refresh()
    };
    const removeEmptyKeys = (obj) => {
        return _.pickBy(obj, (value) => {
            if (_.isObject(value)) {
                const newObj = removeEmptyKeys(value);
                return !_.isEmpty(newObj);
            }
            return !_.isNil(value) && (_.isNumber(value) ? value !== 0 : !_.isEmpty(value));
        });
    }
    const handleSubmit = async () => {
        const parmas = {
            areaMatId: record.id,
            groupId: [formData.groupId],
            areaId: formData.areaId,
            warehouseId: formData.warehouseId,
            locId: formData.locId,
        }
        const res = await request.post(`/locAreaMatRela/matnr/bind`, parmas);
        if (res?.data?.code === 200) {
            handleClose()
            reload()
        } else {
            notify(res.data.msg);
        }
    }
    const [groupId, setGroupId] = useState();
    const warehouseChange = (e) => {
        setGroupId(e.target.value)
    }
    return (
        <Dialog open={open} maxWidth="md" fullWidth>
            <Form onSubmit={handleSubmit}>
                <DialogCloseButton onClose={handleClose} />
                <DialogTitle>{translate('toolbar.bindloc')}</DialogTitle>
                <DialogContent sx={{ mt: 2 }}>
                    <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
                        <Grid container spacing={2}>
                            <Grid item xs={4}>
                                <ReferenceInput
                                    source="warehouseId"
                                    reference="warehouse"
                                >
                                    <AutocompleteInput
                                        label="table.field.loc.warehouseId"
                                        optionText="name"
                                        onChange={(value) => handleChange(value, 'warehouseId')}
                                        value={formData.warehouseId}
                                        validate={required()}
                                        filterToQuery={(val) => ({ name: val })}
                                    />
                                </ReferenceInput>
                            </Grid>
                            <Grid item xs={4}>
                                <ReferenceInput
                                    source="areaId"
                                    reference="warehouseAreas"
                                >
                                    <AutocompleteInput
                                        label="table.field.loc.areaId"
                                        optionText="name"
                                        onChange={(value) => handleChange(value, 'areaId')}
                                        value={formData.areaId}
                                        validate={required()}
                                        filterToQuery={(val) => ({ name: val })}
                                    />
                                </ReferenceInput>
                            </Grid>
                            <Grid item xs={4}>
                                <TreeSelectInput
                                    label="table.field.locAreaMatRela.groupId"
                                    resource={'matnrGroup'}
                                    source="groupId"
                                    value={formData.groupId}
                                    onChange={(e) => handleChange(e.target.value, 'groupId')}
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <ReferenceArrayInput source="locId" reference="loc" >
                                    <SelectArrayInput
                                        label="table.field.locAreaMatRela.locId"
                                        validate={required()}
                                        optionText={'code'}
                                        value={formData.locId}
                                        onChange={(e) => handleChange(e.target.value, 'locId')}
                                    />
                                </ReferenceArrayInput>
                            </Grid>
                        </Grid>
                    </Box>
                </DialogContent>
                <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                    <Box sx={{ width: '100%', display: 'flex', justifyContent: 'space-between' }}>
                        <Button type="submit" variant="contained" startIcon={<SaveIcon />}>
                            {translate('toolbar.confirm')}
                        </Button>
                    </Box>
                </DialogActions>
            </Form>
        </Dialog>
    );
}
export default BindMatnrModal;
rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatCreate.jsx
@@ -92,7 +92,7 @@
                                        autoFocus
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                {/* <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.locAreaMat.warehouseId"
                                        source="warehouseId"
@@ -103,7 +103,7 @@
                                        label="table.field.locAreaMat.areaId"
                                        source="areaId"
                                    />
                                </Grid>
                                </Grid> */}
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.locAreaMat.depict"
rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatEdit.jsx
@@ -71,7 +71,7 @@
                                autoFocus
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                        {/* <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.locAreaMat.warehouseId"
                                source="warehouseId"
@@ -82,7 +82,7 @@
                                label="table.field.locAreaMat.areaId"
                                source="areaId"
                            />
                        </Stack>
                        </Stack> */}
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.locAreaMat.depict"
rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatList.jsx
@@ -119,8 +119,8 @@
                >
                    <NumberField source="id" />
                    <TextField source="code" label="table.field.locAreaMat.code" />
                    <NumberField source="warehouseId" label="table.field.locAreaMat.warehouseId" />
                    <NumberField source="areaId" label="table.field.locAreaMat.areaId" />
                    {/* <NumberField source="warehouseId" label="table.field.locAreaMat.warehouseId" />
                    <NumberField source="areaId" label="table.field.locAreaMat.areaId" /> */}
                    <TextField source="depict" label="table.field.locAreaMat.depict" />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatPanel.jsx
@@ -1,57 +1,411 @@
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';
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: 'matnrId$', headerName: translate('table.field.locAreaMatRela.matnrId'), width: 100 },
        { field: 'groupId$', headerName: translate('table.field.locAreaMatRela.groupId'), width: 100 },
        { field: 'locTypeId$', headerName: translate('table.field.locAreaMatRela.locTypeId'), width: 100 },
        { field: 'locId$', headerName: translate('table.field.locAreaMatRela.locId'), 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({
        areaMatId: record.id,
    });
    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(data || [])
        } else {
            notify(msg);
        }
    }
    const requestLocType = async () => {
        const { data: { code, data, msg } } = await request.get(`/locAreaMatRela/locType/${record.id}`);
        if (code === 200) {
            setLocTree(data || [])
        } else {
            notify(msg);
        }
    }
    return (
        <Grid container spacing={2}>
            {/* 物料分组 */}
            <Grid item xs={2}>
                1
                <MatnrTree matnrTree={matnrTree} setParmas={setParmas} reload={reload} />
            </Grid>
            <Grid item xs={3}>
                2
            {/* 库位类型 */}
            <Grid item xs={2}>
                <LocTree locTree={locTree} 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
                    columnBufferPx={100}
                />
            </Grid>
        </Grid>
    );
};
export default LocAreaMatPanel;
const MatnrTree = ({ matnrTree, 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()
    };
    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}
                selectedItems={selectedItems}
                getItemId={(item) => item.id}
                getItemLabel={(item) => item.name}
                defaultExpandedItems={['grid']}
                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, 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.preventDefault();
        console.log(nodeId)
    };
    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={locTree}
                apiRef={apiRef}
                selectedItems={selectedItems}
                getItemId={(item) => item.id}
                getItemLabel={(item) => item.name}
                defaultExpandedItems={['grid']}
                onSelectedItemsChange={handleSelectedItemsChange}
                onItemSelectionToggle={handleItemSelectionToggle}
                onItemClick={handleNodeSelect}
            />
            <BindLocModal
                open={addDialog}
                setOpen={setAddDialog}
                selectedItems={selectedItems}
                reload={reload}
            />
            <ConfirmModal
                open={delectDialog}
                setOpen={setDelectDialog}
                onConfirm={contirmDelete}
            />
        </Card>
    )
}
rsf-admin/src/page/basicInfo/matnr/MatnrList.jsx
@@ -86,7 +86,7 @@
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        display: 'block',
        width: '100px',
        width: '300px',
    },
    '& .RaDatagrid-table': {
        width: '100%'
rsf-admin/src/page/components/ConfirmModal.jsx
New file
@@ -0,0 +1,50 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material';
import {
    useTranslate,
} from 'react-admin';
const ConfirmModal = (props) => {
    const { open, onConfirm, setOpen } = props;
    const translate = useTranslate();
    const handleClose = (event) => {
        event.stopPropagation();
        setOpen(false);
    };
    const handleConfirm = (event) => {
        handleClose(event);
        onConfirm();
    };
    return (
        <>
            <Dialog
                aria-labelledby="dialog-title"
                aria-describedby="dialog-description"
                open={open}
                onClose={handleClose}
            >
                <DialogTitle>{translate('common.msg.confirm.tip')}</DialogTitle>
                <DialogContent>
                    <DialogContentText>
                        {translate('common.msg.confirm.desc')}
                    </DialogContentText>
                </DialogContent>
                <DialogActions>
                    <Button onClick={handleClose} color="primary">
                        {translate('ra.action.cancel')}
                    </Button>
                    <Button onClick={handleConfirm} color="primary">
                        {translate('ra.action.confirm')}
                    </Button>
                </DialogActions>
            </Dialog>
        </>
    )
}
export default ConfirmModal;