From b2835c4388aa4abdbd41a7ab7592eb1a1804947a Mon Sep 17 00:00:00 2001
From: verou <857149855@qq.com>
Date: 星期一, 24 三月 2025 16:12:21 +0800
Subject: [PATCH] feat:逻辑分区

---
 rsf-admin/src/page/basicInfo/loc/BindModal.jsx               |    2 
 rsf-admin/src/page/basicInfo/locAreaMat/BindLocModal.jsx     |  220 +++++++++++++
 rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatList.jsx   |    4 
 rsf-admin/src/page/components/ConfirmModal.jsx               |   50 +++
 rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatCreate.jsx |    4 
 rsf-admin/src/page/basicInfo/matnr/MatnrList.jsx             |    2 
 rsf-admin/src/page/basicInfo/locAreaMat/BindMatnrModal.jsx   |  216 +++++++++++++
 rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatEdit.jsx   |    4 
 rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatPanel.jsx  |  434 ++++++++++++++++++++++++--
 9 files changed, 887 insertions(+), 49 deletions(-)

diff --git a/rsf-admin/src/page/basicInfo/loc/BindModal.jsx b/rsf-admin/src/page/basicInfo/loc/BindModal.jsx
index 0a8145f..04cd607 100644
--- a/rsf-admin/src/page/basicInfo/loc/BindModal.jsx
+++ b/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";
diff --git a/rsf-admin/src/page/basicInfo/locAreaMat/BindLocModal.jsx b/rsf-admin/src/page/basicInfo/locAreaMat/BindLocModal.jsx
new file mode 100644
index 0000000..07f22b4
--- /dev/null
+++ b/rsf-admin/src/page/basicInfo/locAreaMat/BindLocModal.jsx
@@ -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;
\ No newline at end of file
diff --git a/rsf-admin/src/page/basicInfo/locAreaMat/BindMatnrModal.jsx b/rsf-admin/src/page/basicInfo/locAreaMat/BindMatnrModal.jsx
new file mode 100644
index 0000000..9523f61
--- /dev/null
+++ b/rsf-admin/src/page/basicInfo/locAreaMat/BindMatnrModal.jsx
@@ -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;
\ No newline at end of file
diff --git a/rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatCreate.jsx b/rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatCreate.jsx
index b0446fe..9f726dc 100644
--- a/rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatCreate.jsx
+++ b/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"
diff --git a/rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatEdit.jsx b/rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatEdit.jsx
index ca29be5..752dbfd 100644
--- a/rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatEdit.jsx
+++ b/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"
diff --git a/rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatList.jsx b/rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatList.jsx
index b5d2ac1..a27e68c 100644
--- a/rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatList.jsx
+++ b/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}>
diff --git a/rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatPanel.jsx b/rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatPanel.jsx
index 311841d..6ca9111 100644
--- a/rsf-admin/src/page/basicInfo/locAreaMat/LocAreaMatPanel.jsx
+++ b/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>
+    )
+}
\ No newline at end of file
diff --git a/rsf-admin/src/page/basicInfo/matnr/MatnrList.jsx b/rsf-admin/src/page/basicInfo/matnr/MatnrList.jsx
index 23079ee..5dc0c6c 100644
--- a/rsf-admin/src/page/basicInfo/matnr/MatnrList.jsx
+++ b/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%'
diff --git a/rsf-admin/src/page/components/ConfirmModal.jsx b/rsf-admin/src/page/components/ConfirmModal.jsx
new file mode 100644
index 0000000..118cab1
--- /dev/null
+++ b/rsf-admin/src/page/components/ConfirmModal.jsx
@@ -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;
\ No newline at end of file

--
Gitblit v1.9.1