From c46d1d8c3b9875f051a6ec3c4a1d3fa7bd32e5db Mon Sep 17 00:00:00 2001
From: skyouc
Date: 星期四, 15 五月 2025 17:15:21 +0800
Subject: [PATCH] 新增库存管理 新增库存明细

---
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WarehouseStockServiceImpl.java |   27 
 rsf-admin/src/page/statistics/stockManage/MatnrListAside.jsx                                        |  121 ++++
 rsf-admin/src/page/statistics/stockManage/WarehouseStockPanel.jsx                                   |  165 +++++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Stock.java                           |   14 
 rsf-server/src/main/resources/mapper/manager/LocItemMapper.xml                                      |   51 +
 rsf-admin/src/page/statistics/stockManage/WarehouseStockCreate.jsx                                  |  207 +++++++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/LocItemMapper.java                   |    7 
 rsf-admin/src/page/statistics/stockManage/index.jsx                                                 |   18 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WarehouseStockService.java          |   13 
 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseStockController.java    |   52 +
 rsf-admin/src/page/statistics/stockManage/WarehouseStockEdit.jsx                                    |  181 ++++++
 rsf-admin/src/page/statistics/stockManage/WarehouseHistories.jsx                                    |  211 +++++++
 rsf-admin/src/page/statistics/stockManage/WarehouseStockInfo.jsx                                    |  383 +++++++++++++
 rsf-admin/src/i18n/zh.js                                                                            |   29 +
 rsf-admin/src/i18n/en.js                                                                            |   27 
 rsf-admin/src/page/ResourceContent.js                                                               |    3 
 rsf-admin/src/page/statistics/stockManage/WarehouseStockList.jsx                                    |  183 ++++++
 rsf-server/src/main/resources/application-dev.yml                                                   |    2 
 18 files changed, 1,679 insertions(+), 15 deletions(-)

diff --git a/rsf-admin/src/i18n/en.js b/rsf-admin/src/i18n/en.js
index 7811a63..1891dfd 100644
--- a/rsf-admin/src/i18n/en.js
+++ b/rsf-admin/src/i18n/en.js
@@ -11,6 +11,8 @@
         },
         button: {
             edit: "Edit",
+            detail: "Details",
+            histories: "Histories",
         },
         field: {
             id: 'ID',
@@ -30,6 +32,9 @@
             partners: 'Strategic Partners',
         },
         list: {
+            titles: {
+                stockInfo: 'Stock Details',
+            },
             empty: {
                 tip: 'No data to display',
             }
@@ -185,6 +190,7 @@
         outStockItem: 'Out Stock Item',
         inStockPoces: 'In Stock Pocess',
         outStockPoces: 'Out Stock Pocess',
+        warehouseStock: 'Instant Inventory',
         deviceBind: 'Device Bind',
         tasks: 'Tasks',
         wave: 'Wave Manage',
@@ -924,6 +930,27 @@
                 model: "model",
                 fieldsIndex: "fieldsIndex",
             },
+            warehouseStock: {
+                locId: "Loc ID",
+                locCode: "Code",
+                warehouse: 'Warehouse',
+                orderId: "Order Id",
+                type: "Type",
+                orderItemId: "Order Detail ID",
+                wkType: "Work Type",
+                matnrId: "Matnr Id",
+                maktx: "Mats Name",
+                unit: 'Unit',
+                anfme: 'Stock Qty',
+                matnrCode: "Matnr Code",
+                workQty: 'Work Qty',
+                qty: 'Non Stock Qty',
+                splrId: 'supplier',
+                batch: "Supplier Batch",
+                spec: "Spec",
+                model: "Model",
+                fieldsIndex: "Fields Index",
+            },
             deviceBind: {
                 currentRow: "currentRow",
                 startRow: "startRow",
diff --git a/rsf-admin/src/i18n/zh.js b/rsf-admin/src/i18n/zh.js
index ff79c91..7e9dc69 100644
--- a/rsf-admin/src/i18n/zh.js
+++ b/rsf-admin/src/i18n/zh.js
@@ -11,6 +11,8 @@
         },
         button: {
             edit: "缂栬緫",
+            detail: "搴撳瓨鏄庣粏",
+            histories: "娴佹按璁板綍",
         },
         field: {
             id: 'ID',
@@ -30,6 +32,10 @@
             partners: '鎴樼暐鍚堜綔',
         },
         list: {
+            titles: {
+                stockInfo: '搴撳瓨鏄庣粏',
+            },
+
             empty: {
                 tip: '娌℃湁鍙樉绀烘暟鎹�',
             }
@@ -186,6 +192,7 @@
         outStockItem: '鍑哄簱鍗曟槑缁�',
         inStockPoces: '鍏ュ簱绠$悊',
         outStockPoces: '鍑哄簱绠$悊',
+        warehouseStock: '鍗虫椂搴撳瓨',
         deviceBind: '璁惧缁戝畾',
         tasks: '浠诲姟绠$悊',
         wave: '娉㈡绠$悊',
@@ -954,6 +961,28 @@
                 model: "鍨嬪彿",
                 fieldsIndex: "鍔ㄦ�佺储寮�",
             },
+            warehouseStock: {
+                locId: "搴撲綅ID",
+                locCode: "搴撲綅",
+                warehouse: '浠撳簱',
+                orderId: "璁㈠崟ID",
+                type: "鍗曟嵁绫诲瀷",
+                orderItemId: "鍗曟嵁鏄庣粏ID",
+                wkType: "涓氬姟绫诲瀷",
+                matnrId: "鐗╂枡ID",
+                maktx: "鐗╂枡鍚嶇О",
+                unit: '鍗曚綅',
+                anfme: '搴撳瓨鏁伴噺',
+                matnrCode: "鐗╂枡缂栫爜",
+                workQty: '鎵ц鏁�',
+                qty: '涓嶅彲鐢ㄥ簱瀛�',
+                splrId: '渚涘簲鍟�',
+                batch: "渚涘簲鍟嗘壒娆�",
+                splrBatch: "渚涘簲鍟嗘壒娆�",
+                spec: "瑙勬牸",
+                model: "鍨嬪彿",
+                fieldsIndex: "鍔ㄦ�佺储寮�",
+            },
             deviceBind: {
                 currentRow: "褰撳墠鎺掑彿",
                 startRow: "璧峰鎺掑彿",
diff --git a/rsf-admin/src/page/ResourceContent.js b/rsf-admin/src/page/ResourceContent.js
index 9b0737a..1a87dd5 100644
--- a/rsf-admin/src/page/ResourceContent.js
+++ b/rsf-admin/src/page/ResourceContent.js
@@ -47,6 +47,7 @@
 import wave from './orders/wave';
 import locItem from './locItem'
 import basStation from './basicInfo/basStation';
+import warehouseStock from './statistics/stockManage';
 
 const ResourceContent = (node) => {
     switch (node.component) {
@@ -78,6 +79,8 @@
             return warehouseAreas;
         case 'warehouseAreasItem':
             return warehouseAreasItem;
+        case 'warehouseStock':
+            return warehouseStock;
         case 'loc':
             return loc;
         case 'container':
diff --git a/rsf-admin/src/page/statistics/stockManage/MatnrListAside.jsx b/rsf-admin/src/page/statistics/stockManage/MatnrListAside.jsx
new file mode 100644
index 0000000..dd93b57
--- /dev/null
+++ b/rsf-admin/src/page/statistics/stockManage/MatnrListAside.jsx
@@ -0,0 +1,121 @@
+import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
+import request from '@/utils/request';
+import {
+    SavedQueriesList,
+    FilterLiveSearch,
+    useNotify,
+    useListContext,
+    SearchInput
+} from 'react-admin';
+import BookmarkIcon from '@mui/icons-material/BookmarkBorder';
+import { Box, Typography, Card, CardContent, useTheme, Input } from '@mui/material';
+import { RichTreeView } from "@mui/x-tree-view/RichTreeView";
+import { TreeItem2 } from "@mui/x-tree-view/TreeItem2";
+
+
+const MatListAside = () => {
+    const theme = useTheme();
+    const notify = useNotify();
+    const { setFilters } = useListContext(); // 鑾峰彇鍒楄〃涓婁笅鏂�
+    const [selectedOption, setSelectedOption] = useState(null);
+    const [treeData, setTreeData] = useState([]);
+    const [defaultIds, setDefaultIds] = useState(['65']);
+    const [condition, setCondition] = useState('');
+
+    const haveChildren = (item) => {
+        if (Array.isArray(item)) {
+            return item.map((k) => haveChildren(k));
+        }
+
+        if (item && typeof item === 'object') {
+            if (item.id !== undefined) {
+                item.id = item.id.toString();
+            }
+
+            if (item.children && Array.isArray(item.children)) {
+                item.children = haveChildren(item.children);
+            }
+        }
+
+        return item;
+    };
+    useEffect(() => {
+        http()
+    }, [condition]);
+
+    const http = () => {
+        request.post('/matnrGroup/tree', { condition })
+            .then(res => {
+                if (res?.data?.code === 200) {
+                    let data = res.data.data;
+                    let items = haveChildren(data)
+                    setTreeData(items)
+                    setDefaultIds([items.at(0).id])
+
+                } else {
+                    notify(res.data.msg);
+                }
+            })
+            .catch(error => {
+                notify('Error fetching tree data');
+            });
+
+    }
+    const handleNodeSelect = (event, nodeId) => {
+        setFilters({ groupId: nodeId });
+    };
+    const handleSearch = (e) => {
+        setCondition(e.target.value)
+    };
+
+
+    const CustomCheckbox = React.forwardRef(function CustomCheckbox(props, ref) {
+        return <input type="checkbox" ref={ref} {...props} />;
+    });
+
+    const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) {
+        return (
+            <TreeItem2
+                {...props}
+                ref={ref}
+                slots={{
+                    checkbox: CustomCheckbox,
+                }}
+            />
+        );
+    });
+
+
+    return (
+        <Card
+            sx={{
+                order: -1,
+                mr: 2,
+                mt: 8,
+                alignSelf: 'flex-start',
+                border: theme.palette.mode === 'light' && '1px solid #e0e0e3',
+                width: 250,
+                minWidth: 150,
+                height: `100%`,
+            }}
+        >
+            <CardContent>
+                <Input
+                    placeholder="鎼滅储鐗╂枡鍒嗙粍"
+                    sx={{ '--Input-focused': 1, marginBottom: '10px' }}
+                    onChange={handleSearch}
+                />
+                <RichTreeView
+                    defaultExpandedItems={defaultIds}
+                    expansionTrigger="iconContainer"
+                    items={treeData}
+                    slots={CustomTreeItem}
+                    onItemClick={handleNodeSelect} // 鐩戝惉鑺傜偣鐐瑰嚮浜嬩欢
+                />
+
+            </CardContent>
+        </Card>
+    )
+}
+
+export default MatListAside;
diff --git a/rsf-admin/src/page/statistics/stockManage/WarehouseHistories.jsx b/rsf-admin/src/page/statistics/stockManage/WarehouseHistories.jsx
new file mode 100644
index 0000000..236bf9a
--- /dev/null
+++ b/rsf-admin/src/page/statistics/stockManage/WarehouseHistories.jsx
@@ -0,0 +1,211 @@
+import { Dialog, DialogActions, DialogContent, DialogTitle, Box } from "@mui/material";
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import {
+    List,
+    DatagridConfigurable,
+    SearchInput,
+    TopToolbar,
+    Button,
+    SelectColumnsButton,
+    EditButton,
+    FilterButton,
+    CreateButton,
+    ExportButton,
+    BulkDeleteButton,
+    WrapperField,
+    Toolbar,
+    useRecordContext,
+    useTranslate,
+    useNotify,
+    useListContext,
+    FunctionField,
+    TextField,
+    NumberField,
+    DateField,
+    BooleanField,
+    ReferenceField,
+    TextInput,
+    DateTimeInput,
+    DateInput,
+    SelectInput,
+    NumberInput,
+    ReferenceInput,
+    ReferenceArrayInput,
+    AutocompleteInput,
+    DeleteButton,
+    Form,
+    SaveButton,
+    useRefresh,
+    useGetList,
+} from 'react-admin';
+import DialogCloseButton from "../../components/DialogCloseButton";
+import { styled } from '@mui/material/styles';
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
+import { Grid, Stack, width } from "@mui/system";
+import request from '@/utils/request';
+import SaveIcon from '@mui/icons-material/Save';
+import CheckCircleIcon from '@mui/icons-material/CheckCircle';
+
+const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
+    '& .css-1vooibu-MuiSvgIcon-root': {
+        height: '.9em'
+    },
+    '& .RaDatagrid-row': {
+        cursor: 'auto'
+    },
+    '& .status': {
+        width: 90
+    },
+}));
+
+const WarehouseHistories = (props) => {
+    const { open, setOpen, record } = props;
+    const translate = useTranslate();
+    const [params, setParams] = useState({});
+    const [item, setItem] = useState({});
+    const [poItemDialog, setPoItemDialog] = useState(false);
+    const [drawerVal, setDrawerVal] = useState(false);
+    const refresh = useRefresh();
+
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+        }
+    };
+    // const CustomFilter = () => {
+    //     const { filterValues, setFilters, refetch } = useListContext('deliveryItem');
+    //     const [formValues, setFormValues] = useState(filterValues);
+    //     const handleChange = (event) => {
+    //         if (event.target == undefined || event.target == null) { return }
+    //         setFormValues(formValues => ({
+    //             ...formValues,
+    //             [event.target.name]: event.target.value
+    //         }));
+    //     };
+
+    //     const handleSubmit = (event) => {
+    //         setParams(formValues)
+    //     };
+
+    //     return (
+    //         <Box sx={{ width: '100%', margin: 1, marginBottom: 8, "& .MuiDialogActions-root": { padding: 0 } }}>
+    //             <Form>
+    //                 <Grid container rowSpacing={2} columnSpacing={2} sx={{ padding: 2 }}>
+    //                     <Stack>
+    //                         <TextInput
+    //                             source="condition"
+    //                             label="common.action.search"
+    //                             resettable
+    //                             defaultValue={params?.condition}
+    //                             onChange={handleChange} />
+    //                     </Stack>
+    //                 </Grid>
+    //                 <DialogActions>
+    //                     <Toolbar sx={{ width: '100%', justifyContent: 'end' }}  >
+    //                         <SaveButton onClick={handleSubmit} label={"toolbar.query"} />
+    //                     </Toolbar>
+    //                 </DialogActions>
+    //             </Form>
+    //         </Box>
+    //     );
+    // };
+    return (
+        <Box>
+            <Dialog
+                open={open}
+                onClose={handleClose}
+                aria-labelledby="form-dialog-title"
+                aria-hidden
+                fullWidth
+                disableRestoreFocus
+                maxWidth="lg"
+            >
+                <DialogTitle id="form-dialog-title" sx={{
+                    position: 'sticky',
+                    top: 0,
+                    backgroundColor: 'background.paper',
+                    zIndex: 1000
+                }}>
+                    <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
+                        <DialogCloseButton onClose={handleClose} />
+                    </Box>
+                </DialogTitle>
+                <Grid container rowSpacing={2} columnSpacing={2}>
+                    <DialogContent>
+                        <Grid item sx={24}>
+                            <List
+                                storeKey="selectPurchase"
+                                resource="purchase/filters"
+                                sx={{
+                                    flexGrow: 1,
+                                    marginTop: 8,
+                                    height: 500,
+                                    transition: (theme) =>
+                                        theme.transitions.create(['all'], {
+                                            duration: theme.transitions.duration.enteringScreen,
+                                        }),
+                                    marginRight: 0,
+                                }}
+                                // filters={<CustomFilter />}
+                                queryOptions={{ meta: { ...params } }}
+                                empty={false}
+                                sort={{ field: "create_time", order: "desc" }}
+                                actions={false}
+                                perPage={DEFAULT_PAGE_SIZE}
+                            >
+                                <StyledDatagrid
+                                    preferenceKey='selectPurchase'
+                                    bulkActionButtons={
+                                        <>
+                                            <ConfirmSelectButton
+                                                setOpen={setOpen}
+                                                setPoItemDialog={setPoItemDialog}
+                                                setItem={setItem}
+                                                mutationMode="pessimistic" />
+                                        </>
+                                    }
+                                    rowClick={false}
+                                    expand={false}
+                                    expandSingle={true}
+                                    omit={['id', 'createTime', 'createBy', 'channel', 'platCode', 'memo', 'channel', 'startTime', 'workQty', 'endTime']}
+                                >
+                                    <NumberField source="id" />
+                                    <TextField source="code" label="table.field.purchase.code" />
+                                    <TextField source="type$" label="table.field.purchase.type" />
+                                    <TextField source="wkType$" label="table.field.purchase.wkType" />
+                                    <TextField source="source" label="table.field.purchase.source" />
+                                    <NumberField source="anfme" label="table.field.purchase.anfme" />
+                                    <NumberField source="qty" label="table.field.purchase.qty" />
+                                    <TextField source="channel" label="table.field.purchase.channel" />
+                                    <TextField source="platCode" label="table.field.purchase.platCode" />
+                                    <DateField source="preArr" label="table.field.purchase.preArr" showTime />
+                                    <DateField source="startTime" label="table.field.purchase.startTime" showTime />
+                                    <DateField source="endTime" label="table.field.purchase.endTime" showTime />
+                                    <TextField source="project" label="table.field.purchase.project" />
+                                    <TextField source="memo" label="common.field.memo" sortable={false} />
+                                </StyledDatagrid>
+                            </List>
+                        </Grid>
+                    </DialogContent>
+                </Grid>
+            </Dialog >
+        </Box>
+    )
+}
+
+export default WarehouseHistories;
+
+
+const ConfirmSelectButton = ({ setOpen, setPoItemDialog, setItem }) => {
+    const { selectedIds, onUnselectItems } = useListContext();
+    const confirmSelect = async (event) => {
+        setItem(selectedIds[0])
+        onUnselectItems();
+        setPoItemDialog(true)
+        setOpen(false);
+    }
+
+    return (
+        <Button label={"toolbar.confirm"} variant="contained" color="primary" size="medium" startIcon={<SaveIcon />} onClick={confirmSelect} />
+    )
+}
diff --git a/rsf-admin/src/page/statistics/stockManage/WarehouseStockCreate.jsx b/rsf-admin/src/page/statistics/stockManage/WarehouseStockCreate.jsx
new file mode 100644
index 0000000..b0b241d
--- /dev/null
+++ b/rsf-admin/src/page/statistics/stockManage/WarehouseStockCreate.jsx
@@ -0,0 +1,207 @@
+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,
+} from 'react-admin';
+import {
+    Dialog,
+    DialogActions,
+    DialogContent,
+    DialogTitle,
+    Stack,
+    Grid,
+    Box,
+} from '@mui/material';
+import DialogCloseButton from "../../components/DialogCloseButton";
+import StatusSelectInput from "../../components/StatusSelectInput";
+import MemoInput from "../../components/MemoInput";
+
+const WarehouseStockCreate = (props) => {
+    const { open, setOpen } = props;
+
+    const translate = useTranslate();
+    const notify = useNotify();
+
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+        }
+    };
+
+    const handleSuccess = async (data) => {
+        setOpen(false);
+        notify('common.response.success');
+    };
+
+    const handleError = async (error) => {
+        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
+    };
+
+    return (
+        <>
+            <CreateBase
+                record={{}}
+                transform={(data) => {
+                    return data;
+                }}
+                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
+            >
+                <Dialog
+                    open={open}
+                    onClose={handleClose}
+                    aria-labelledby="form-dialog-title"
+                    fullWidth
+                    disableRestoreFocus
+                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
+                >
+                    <Form>
+                        <DialogTitle id="form-dialog-title" sx={{
+                            position: 'sticky',
+                            top: 0,
+                            backgroundColor: 'background.paper',
+                            zIndex: 1000
+                        }}
+                        >
+                            {translate('create.title')}
+                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
+                                <DialogCloseButton onClose={handleClose} />
+                            </Box>
+                        </DialogTitle>
+                        <DialogContent sx={{ mt: 2 }}>
+                            <Grid container rowSpacing={2} columnSpacing={2}>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.locItem.locId"
+                                        source="locId"
+                                        autoFocus
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.locItem.locCode"
+                                        source="locCode"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.locItem.type"
+                                        source="type"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.locItem.orderItemId"
+                                        source="orderItemId"
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.locItem.wkType"
+                                        source="wkType"
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.locItem.matnrId"
+                                        source="matnrId"
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.locItem.maktx"
+                                        source="maktx"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.locItem.matnrCode"
+                                        source="matnrCode"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.locItem.unit"
+                                        source="unit"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.locItem.anfme"
+                                        source="anfme"
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.locItem.qty"
+                                        source="qty"
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.locItem.workQty"
+                                        source="workQty"
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.locItem.batch"
+                                        source="batch"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.locItem.spec"
+                                        source="spec"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.locItem.model"
+                                        source="model"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.locItem.fieldsIndex"
+                                        source="fieldsIndex"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                            </Grid>
+                        </DialogContent>
+                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                            <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
+                                <SaveButton />
+                            </Toolbar>
+                        </DialogActions>
+                    </Form>
+                </Dialog>
+            </CreateBase>
+        </>
+    )
+}
+
+export default WarehouseStockCreate;
diff --git a/rsf-admin/src/page/statistics/stockManage/WarehouseStockEdit.jsx b/rsf-admin/src/page/statistics/stockManage/WarehouseStockEdit.jsx
new file mode 100644
index 0000000..7ab90ce
--- /dev/null
+++ b/rsf-admin/src/page/statistics/stockManage/WarehouseStockEdit.jsx
@@ -0,0 +1,181 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import {
+    Edit,
+    SimpleForm,
+    FormDataConsumer,
+    useTranslate,
+    TextInput,
+    NumberInput,
+    BooleanInput,
+    DateInput,
+    SelectInput,
+    ReferenceInput,
+    ReferenceArrayInput,
+    AutocompleteInput,
+    SaveButton,
+    Toolbar,
+    Labeled,
+    NumberField,
+    required,
+    useRecordContext,
+    DeleteButton,
+} from 'react-admin';
+import { useWatch, useFormContext } from "react-hook-form";
+import { Stack, Grid, Box, Typography } from '@mui/material';
+import * as Common from '@/utils/common';
+import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
+import EditBaseAside from "../../components/EditBaseAside";
+import CustomerTopToolBar from "../../components/EditTopToolBar";
+import MemoInput from "../../components/MemoInput";
+import StatusSelectInput from "../../components/StatusSelectInput";
+
+const FormToolbar = () => {
+    const { getValues } = useFormContext();
+
+    return (
+        <Toolbar sx={{ justifyContent: 'end' }}>
+            <SaveButton />
+            {/* <DeleteButton mutationMode="optimistic" /> */}
+        </Toolbar>
+    )
+}
+
+const WarehouseStockEdit = () => {
+    const translate = useTranslate();
+
+    return (
+        <Edit
+            redirect="list"
+            mutationMode={EDIT_MODE}
+            actions={<CustomerTopToolBar />}
+            aside={<EditBaseAside />}
+        >
+            <SimpleForm
+                shouldUnregister
+                warnWhenUnsavedChanges
+                toolbar={<FormToolbar />}
+                mode="onTouched"
+                defaultValues={{}}
+            // validate={(values) => { }}
+            >
+                <Grid container width={{ xs: '100%', xl: '100%' }} rowSpacing={3} columnSpacing={3}>
+                    <Grid item xs={14} md={10}>
+                        <Typography variant="h6" gutterBottom>
+                            {translate('common.edit.title.main')}
+                        </Typography>
+                        <Stack direction='row' gap={2}>
+                            <NumberInput
+                                label="table.field.locItem.locId"
+                                source="locId"
+                                autoFocus
+                            />
+                            <TextInput
+                                label="table.field.locItem.locCode"
+                                source="locCode"
+                                parse={v => v}
+                            />
+                            <NumberInput
+                                label="table.field.locItem.orderId"
+                                source="orderId"
+                            />
+                            <TextInput
+                                label="table.field.locItem.type"
+                                source="type$"
+                                parse={v => v}
+                            />
+                        </Stack>
+                        <Stack direction='row' gap={2}>
+                            <NumberInput
+                                label="table.field.locItem.orderItemId"
+                                source="orderItemId"
+                            />
+                            <NumberInput
+                                label="table.field.locItem.wkType"
+                                source="wkType"
+                            />
+                            <NumberInput
+                                label="table.field.locItem.matnrId"
+                                source="matnrId"
+                            />
+                        </Stack>
+                        <Stack direction='row' gap={2}>
+                            <TextInput
+                                label="table.field.locItem.maktx"
+                                source="maktx"
+                                parse={v => v}
+                            />
+
+                            <TextInput
+                                label="table.field.locItem.matnrCode"
+                                source="matnrCode"
+                                parse={v => v}
+                            />
+                            <TextInput
+                                label="table.field.locItem.trackCode"
+                                source="trackCode"
+                                parse={v => v}
+                            />
+                            <TextInput
+                                label="table.field.locItem.unit"
+                                source="unit"
+                                parse={v => v}
+                            />
+                        </Stack>
+                        <Stack direction='row' gap={2}>
+                            <NumberInput
+                                label="table.field.locItem.anfme"
+                                source="anfme"
+                            />
+                            <NumberInput
+                                label="table.field.locItem.qty"
+                                source="qty"
+                            />
+                            <NumberInput
+                                label="table.field.locItem.workQty"
+                                source="workQty"
+                            />
+                            <TextInput
+                                label="table.field.locItem.batch"
+                                source="batch"
+                                parse={v => v}
+                            />
+                        </Stack>
+                        <Stack direction='row' gap={2}>
+                            <TextInput
+                                label="table.field.locItem.splrBatch"
+                                source="splrBatch"
+                                parse={v => v}
+                            />
+                            <TextInput
+                                label="table.field.locItem.spec"
+                                source="spec"
+                                parse={v => v}
+                            />
+                            <TextInput
+                                label="table.field.locItem.model"
+                                source="model"
+                                parse={v => v}
+                            />
+                            <TextInput
+                                label="table.field.locItem.fieldsIndex"
+                                source="fieldsIndex"
+                                parse={v => v}
+                            />
+                        </Stack>
+
+                    </Grid>
+                    <Grid item xs={10} md={2}>
+                        <Typography variant="h6" gutterBottom>
+                            {translate('common.edit.title.common')}
+                        </Typography>
+                        <StatusSelectInput />
+                        <Box mt="2em" />
+                        <MemoInput />
+                    </Grid>
+                </Grid>
+            </SimpleForm>
+        </Edit >
+    )
+}
+
+export default WarehouseStockEdit;
diff --git a/rsf-admin/src/page/statistics/stockManage/WarehouseStockInfo.jsx b/rsf-admin/src/page/statistics/stockManage/WarehouseStockInfo.jsx
new file mode 100644
index 0000000..deaf6ff
--- /dev/null
+++ b/rsf-admin/src/page/statistics/stockManage/WarehouseStockInfo.jsx
@@ -0,0 +1,383 @@
+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,
+} from 'react-admin';
+import {
+    Dialog,
+    DialogActions,
+    DialogContent,
+    DialogTitle,
+    Stack,
+    Grid,
+    TextField,
+    Box,
+    Button,
+    Paper,
+    TableContainer,
+    Table,
+    TableHead,
+    TableBody,
+    TableRow,
+    TableCell,
+    Tooltip,
+    IconButton,
+    styled,
+    Select,
+    MenuItem
+
+} from '@mui/material';
+import DialogCloseButton from "../../components/DialogCloseButton";
+import StatusSelectInput from "../../components/StatusSelectInput";
+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 _, { set } from 'lodash';
+import { DataGrid, useGridApiRef, GRID_DATE_COL_DEF, GRID_DATETIME_COL_DEF, getGridDateOperators, useGridApiContext } from '@mui/x-data-grid';
+import { LocalizationProvider, DatePicker, DateTimePicker } from '@mui/x-date-pickers';
+import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import DictionarySelect from "../../components/DictionarySelect";
+import DictSelect from "../../components/DictSelect";
+import { 'zhCN' as locale } from 'date-fns/locale';
+import { format, } from 'date-fns';
+import { DemoContainer } from '@mui/x-date-pickers/internals/demo';
+
+const WarehouseStockInfo = (props) => {
+    const { open, setOpen, billReload, record } = props;
+    const translate = useTranslate();
+    const notify = useNotify();
+    const refresh = useRefresh();
+    const [disabled, setDisabled] = useState(false)
+    const [createDialog, setCreateDialog] = useState(false);
+    const tableRef = useRef();
+
+    useEffect(() => {
+        if (open && record !== 0) {
+            requestGetBody()
+        }
+        setDisabled(false)
+    }, [open])
+
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+            refresh();
+            setTableData([])
+        }
+    };
+
+
+    const [tabelData, setTableData] = useState([]);
+
+    const resetData = () => {
+        setTableData([])
+    }
+
+    const setFinally = () => {
+        const rows = tableRef.current.state.editRows;
+        for (const key in rows) {
+            const find = tabelData.find(item => item.id === +key);
+            find.anfme = rows[key].anfme.value;
+        }
+        setTableData([...tabelData]);
+    }
+
+    const handleSubmit = async () => {
+        setFinally()
+        setDisabled(true)
+        const parmas = {
+            "purchaseId": record,
+            "items": tabelData,
+        }
+        const res = await request.post(`/asnOrder/purchases/save`, parmas);
+        if (res?.data?.code === 200) {
+            notify(res.data.msg);
+        } else {
+            notify(res.data.msg);
+        }
+        setOpen(false);
+        refresh();
+        resetData()
+        setDisabled(false)
+    };
+
+    const requestGetBody = async () => {
+        const res = await request.post(`warehouse/stock/page`, { matnrCode: record });
+        if (res?.data?.code === 200) {
+            setTableData(res.data.data.records)
+        } else {
+            notify(res.data.msg);
+        }
+    }
+
+    const [selectedRows, setSelectedRows] = useState([]);
+
+    return (
+        <>
+            <Dialog
+                open={open}
+                onClose={handleClose}
+                aria-labelledby="form-dialog-title"
+                aria-hidden
+                fullWidth
+                disableRestoreFocus
+                maxWidth="xl"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
+            >
+                <DialogTitle id="form-dialog-title" sx={{
+                    position: 'sticky',
+                    top: 0,
+                    backgroundColor: 'background.paper',
+                    zIndex: 1000
+                }}>
+                    {translate('common.list.titles.stockInfo')}
+                    <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
+                        <DialogCloseButton onClose={handleClose} />
+                    </Box>
+                </DialogTitle>
+                <DialogContent sx={{ mt: 2 }}>
+                    <Box></Box>
+                    <Box sx={{ mt: 2 }}>
+                        <AsnOrderModalTable tabelData={tabelData}
+                            setTableData={setTableData}
+                            record={record}
+                            selectedRows={selectedRows}
+                            setSelectedRows={setSelectedRows}
+                            tableRef={tableRef} />
+                    </Box>
+                </DialogContent>
+                <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                    <Toolbar sx={{ width: '100%', justifyContent: 'end' }}  >
+                        <Button disabled={disabled} onClick={handleSubmit} variant="contained" startIcon={<SaveIcon />}>
+                            {translate('toolbar.confirm')}
+                        </Button>
+                    </Toolbar>
+                </DialogActions>
+            </Dialog>
+        </>
+    )
+}
+
+export default WarehouseStockInfo;
+
+const SelectInputSplrNameEditCell = (params) => {
+    const [formData, setFormData] = useState([{}])
+    useEffect(() => {
+        getOptions();
+    }, []);
+    const getOptions = async () => {
+        const parmas = {
+            "type": "supplier"
+        }
+        const {
+            data: { code, data, msg },
+        } = await request.post("companys/page", parmas);
+        if (code === 200) {
+            setFormData(data.records)
+        } else {
+            notify(msg);
+        }
+    }
+
+    return (
+        <Select
+            value={params.value}
+            onChange={(e) => {
+                params.api.setEditCellValue({
+                    id: params.id,
+                    field: params.field,
+                    value: e.target.value,
+                })
+                // 鎵惧埌閫変腑鐨勪緵搴斿晢璁板綍
+                const selectedSupplier = formData.find(supplier => supplier.name === e.target.value);
+
+                // 濡傛灉鎵惧埌瀵瑰簲鐨勪緵搴斿晢璁板綍锛屽悓鏃舵洿鏂皊plrCode瀛楁
+                if (selectedSupplier) {
+                    params.api.setEditCellValue({
+                        id: params.id,
+                        field: 'splrCode',
+                        value: selectedSupplier.id,
+                    });
+                }
+            }}
+            fullWidth
+        >
+            {formData.map(e => {
+                return (
+                    <MenuItem value={e.name} children={e.name} key={e.id} />
+                );
+
+            })}
+
+        </Select>
+    );
+};
+
+const AsnOrderModalTable = ({ tabelData, setTableData, record, selectedRows, setSelectedRows, tableRef }) => {
+    const translate = useTranslate();
+    const notify = useNotify();
+
+    const [columns, setColumns] = useState([
+        {
+            field: 'matnrCode',
+            headerName: translate('table.field.asnOrderItem.matnrCode'),
+            width: 130,
+            editable: false,
+        },
+        {
+            field: 'matnrName',
+            headerName: translate('table.field.asnOrderItem.maktx'),
+            width: 250,
+            editable: false,
+        },
+        {
+            field: 'splrName',
+            headerName: translate('table.field.asnOrderItem.splrName') + "*",
+            minWidth: 150,
+            flex: 1,
+            editable: false,
+            renderEditCell: (params) => (
+                <SelectInputSplrNameEditCell {...params} />
+            ),
+        },
+        {
+            field: 'platItemId',
+            headerName: translate('table.field.asnOrderItem.platItemId') + "*",
+            minWidth: 100,
+            flex: 1,
+            editable: false,
+        },
+        {
+            field: 'anfme',
+            headerName: translate('table.field.asnOrderItem.anfme') + "*",
+            type: 'number',
+            minWidth: 100,
+            flex: 1,
+            editable: false,
+            valueFormatter: (val) => val < 0 ? 0 : val,
+        },
+        {
+            field: 'qty',
+            headerName: translate('table.field.asnOrderItem.qty') + "*",
+            type: 'number',
+            minWidth: 100,
+            flex: 1,
+            valueFormatter: (val) => val < 0 ? 0 : val,
+        },
+        {
+            field: 'unit',
+            headerName: translate('table.field.asnOrderItem.stockUnit'),
+            minWidth: 100,
+            flex: 1,
+            editable: false,
+        },
+    ])
+
+    let cdata = useRef([]);
+
+    useEffect(() => {
+        getDynamicFields();
+    }, []);
+
+    useEffect(() => {
+        cdata.current = tabelData
+    }, [tabelData]);
+
+
+    const getDynamicFields = async () => {
+        const {
+            data: { code, data, msg },
+        } = await request.get("/fields/enable/list");
+        if (code === 200) {
+            const cols = data.map(el => ({
+                field: el.fields,
+                valueGetter: (value, row) => {
+                    if (value != null && value != undefined) {
+                        return value;
+                    }
+                    if (row.extendFields == null || row.extendFields[el.fields] == null) {
+                        return ''
+                    } else {
+                        return `${row.extendFields[el.fields] == null ? '' : row.extendFields[el.fields]}`;
+                    }
+                },
+                headerName: el.fieldsAlise,
+                minWidth: 100,
+                flex: 1,
+                editable: true
+            }))
+            setColumns([...columns, ...cols])
+        } else {
+            notify(msg);
+        }
+    }
+
+    const processRowUpdate = (newRow, oldRow) => {
+        const rows = tabelData.map((r) =>
+            r.id === newRow.id ? { ...newRow } : r
+        )
+        setTableData(rows)
+
+        return newRow;
+    };
+
+
+    const handleSelectionChange = (ids) => {
+        setSelectedRows(ids)
+    };
+
+    tableRef.current = useGridApiRef();
+
+
+    return (
+        <div style={{ height: 400, width: '100%' }}>
+            <DataGrid
+                apiRef={tableRef}
+                rows={tabelData}
+                columns={columns}
+                disableRowSelectionOnClick
+                getRowId={(row) => row.id}
+                disableColumnFilter
+                disableColumnSelector
+                disableColumnSorting
+                disableMultipleColumnsSorting
+                processRowUpdate={processRowUpdate}
+                initialState={{
+                    pagination: {
+                        paginationModel: {
+                            pageSize: 25,
+                        },
+                    },
+                }}
+                pageSizeOptions={[10, 25, 50, 100]}
+                editMode="row"
+                checkboxSelection
+                onRowSelectionModelChange={handleSelectionChange}
+                selectionModel={selectedRows}
+                sx={{
+                    '& .MuiDataGrid-cell input': {
+                        border: '1px solid #ccc'
+                    },
+                }}
+            />
+        </div>
+    );
+};
+
diff --git a/rsf-admin/src/page/statistics/stockManage/WarehouseStockList.jsx b/rsf-admin/src/page/statistics/stockManage/WarehouseStockList.jsx
new file mode 100644
index 0000000..376e6ab
--- /dev/null
+++ b/rsf-admin/src/page/statistics/stockManage/WarehouseStockList.jsx
@@ -0,0 +1,183 @@
+import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
+import { useNavigate } from 'react-router-dom';
+import {
+    List,
+    DatagridConfigurable,
+    SearchInput,
+    TopToolbar,
+    SelectColumnsButton,
+    EditButton,
+    FilterButton,
+    CreateButton,
+    ExportButton,
+    BulkDeleteButton,
+    WrapperField,
+    useRecordContext,
+    useTranslate,
+    useNotify,
+    useListContext,
+    FunctionField,
+    TextField,
+    NumberField,
+    DateField,
+    BooleanField,
+    ReferenceField,
+    TextInput,
+    DateTimeInput,
+    DateInput,
+    SelectInput,
+    NumberInput,
+    ReferenceInput,
+    ReferenceArrayInput,
+    useRefresh,
+    AutocompleteInput,
+    DeleteButton,
+    Button,
+} from 'react-admin';
+import { Box, Typography, Card, Stack, LinearProgress } from '@mui/material';
+import { styled } from '@mui/material/styles';
+import EmptyData from "../../components/EmptyData";
+import MyCreateButton from "../../components/MyCreateButton";
+import MyExportButton from '../../components/MyExportButton';
+import PageDrawer from "../../components/PageDrawer";
+import MyField from "../../components/MyField";
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
+import * as Common from '@/utils/common';
+import request from '@/utils/request';
+import WarehouseStockCreate from "./WarehouseStockCreate";
+import WarehouseStockInfo from "./WarehouseStockInfo";
+import MatnrListAside from "./MatnrListAside";
+
+const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
+    '& .css-1vooibu-MuiSvgIcon-root': {
+        height: '.9em'
+    },
+    '& .RaDatagrid-row': {
+        cursor: 'auto'
+    },
+    '& .column-name': {
+    },
+    '& .opt': {
+        width: 220
+    },
+}));
+
+const filters = [
+    <SearchInput source="condition" alwaysOn />,
+    <NumberInput source="locId" label="table.field.locItem.locId" />,
+    <TextInput source="locCode" label="table.field.locItem.locCode" />,
+    <TextInput source="type" label="table.field.locItem.type" />,
+    <NumberInput source="orderItemId" label="table.field.locItem.orderItemId" />,
+    <NumberInput source="wkType" label="table.field.locItem.wkType" />,
+    <NumberInput source="matnrId" label="table.field.locItem.matnrId" />,
+    <TextInput source="maktx" label="table.field.locItem.maktx" />,
+    <TextInput source="unit" label="table.field.locItem.unit" />,
+    <NumberInput source="anfme" label="table.field.locItem.anfme" />,
+    <NumberInput source="workQty" label="table.field.locItem.workQty" />,
+    <TextInput source="batch" label="table.field.locItem.batch" />,
+    <TextInput source="spec" label="table.field.locItem.spec" />,
+    <TextInput source="model" label="table.field.locItem.model" />,
+    <TextInput source="fieldsIndex" label="table.field.locItem.fieldsIndex" />,
+    <TextInput label="common.field.memo" source="memo" />,
+]
+
+const WarehouseStockList = () => {
+    const translate = useTranslate();
+    const [createDialog, setCreateDialog] = useState(false);
+    const [matnrCode, setMatnrCode] = useState('');
+    const [drawerVal, setDrawerVal] = useState(false);
+
+    return (
+        <Box display="flex">
+            <List
+                sx={{
+                    flexGrow: 1,
+                    transition: (theme) =>
+                        theme.transitions.create(['all'], {
+                            duration: theme.transitions.duration.enteringScreen,
+                        }),
+                    marginRight: 0,
+                }}
+                resource="warehouse/stock"
+                title={"common.button.detail"}
+                empty={false}
+                filters={filters}
+                sort={{ field: "create_time", order: "desc" }}
+                actions={(
+                    <TopToolbar>
+                        <FilterButton />
+                        <SelectColumnsButton preferenceKey='locItem' />
+                    </TopToolbar>
+                )}
+                perPage={DEFAULT_PAGE_SIZE}
+            >
+                <StyledDatagrid
+                    preferenceKey='locItem'
+                    bulkActionButtons={false}
+                    rowClick={false}
+                    expand={false}
+                    expandSingle={true}
+                    omit={['id', 'createTime', 'locId', 'spec', 'model', 'locCode', 'orderId', 'orderItemId', 'matnrId', 'splrBatch', 'createBy', 'memo', 'fieldsIndex']}
+                >
+                    <NumberField source="id" />
+                    <NumberField source="locId" label="table.field.warehouseStock.locId" />
+                    <TextField source="locCode" label="table.field.warehouseStock.locCode" />
+                    <NumberField source="orderId" label="table.field.warehouseStock.orderId" />
+                    <NumberField source="orderItemId" label="table.field.warehouseStock.orderItemId" />
+                    <NumberField source="matnrId" label="table.field.warehouseStock.matnrId" />
+                    <TextField source="matnrCode" label="table.field.warehouseStock.matnrCode" />
+                    <TextField source="maktx" label="table.field.warehouseStock.maktx" />
+                    <NumberField source="anfme" label="table.field.warehouseStock.anfme" />
+                    <NumberField source="workQty" label="table.field.warehouseStock.qty" />
+                    <TextField source="spec" label="table.field.warehouseStock.spec" />
+                    <TextField source="model" label="table.field.warehouseStock.model" />
+                    {/* <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
+                        <TextField source="nickname" />
+                    </ReferenceField> */}
+                    {/* <ReferenceField source="splrId" label="table.field.warehouseStock.splrId" reference="companys">
+                        <TextField source="name" filterToQuery={(val) => ({ name: val })} />
+                    </ReferenceField>, */}
+                    <TextField source="batch" label="table.field.warehouseStock.batch" />
+                    <TextField source="unit" label="table.field.warehouseStock.unit" />
+                    <TextField source="fieldsIndex" label="table.field.warehouseStock.fieldsIndex" />
+                    <TextField source="updateBy$" label="common.field.updateBy" />
+                    <DateField source="updateTime" label="common.field.updateTime" showTime />
+                    <WrapperField cellClassName="opt" label="common.field.opt">
+                        <StockInfoButton />
+                        <HistoriesButton setCreateDialog={setCreateDialog} setMatnrCode={setMatnrCode} />
+                    </WrapperField>
+                </StyledDatagrid>
+            </List>
+            <WarehouseStockInfo
+                open={createDialog}
+                setOpen={setCreateDialog}
+                record={matnrCode}
+            />
+        </Box>
+    )
+}
+
+export default WarehouseStockList;
+
+const HistoriesButton = ({ setCreateDialog , setMatnrCode}) => {
+    const record = useRecordContext();
+    const historyClick = (event) => {
+        event.stopPropagation();
+        setCreateDialog(true)
+        setMatnrCode(record?.matnrCode)
+    }
+    return (
+        <Button label="common.button.histories" onClick={historyClick}></Button>
+    )
+}
+
+const StockInfoButton = () => {
+    const stockClick = (event) => {
+        event.stopPropagation();
+    }
+
+    return (
+        <Button label="common.button.detail" onClick={stockClick}></Button>
+    )
+}
+
diff --git a/rsf-admin/src/page/statistics/stockManage/WarehouseStockPanel.jsx b/rsf-admin/src/page/statistics/stockManage/WarehouseStockPanel.jsx
new file mode 100644
index 0000000..9aea26d
--- /dev/null
+++ b/rsf-admin/src/page/statistics/stockManage/WarehouseStockPanel.jsx
@@ -0,0 +1,165 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material';
+import {
+    useTranslate,
+    useRecordContext,
+} from 'react-admin';
+import PanelTypography from "../../components/PanelTypography";
+import * as Common from '@/utils/common'
+
+const WarehouseStockPanel = () => {
+    const record = useRecordContext();
+    if (!record) return null;
+    const translate = useTranslate();
+    return (
+        <>
+            <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}>
+                <CardContent>
+                    <Grid container spacing={2}>
+                        <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}>
+                            <Typography variant="h6" gutterBottom align="left" sx={{
+                                maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' },
+                                whiteSpace: 'nowrap',
+                                overflow: 'hidden',
+                                textOverflow: 'ellipsis',
+                            }}>
+                                {Common.camelToPascalWithSpaces(translate('table.field.locItem.id'))}: {record.id}
+                            </Typography>
+                            {/*  inherit, primary, secondary, textPrimary, textSecondary, error */}
+                            <Typography variant="h6" gutterBottom align="right" >
+                                ID: {record.id}
+                            </Typography>
+                        </Grid>
+                    </Grid>
+                    <Grid container spacing={2}>
+                        <Grid item xs={12} container alignContent="flex-end">
+                            <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
+                                {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo}
+                            </Typography>
+                        </Grid>
+                    </Grid>
+                    <Box height={20}>&nbsp;</Box>
+                    <Grid container spacing={2}>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.locId" 
+                                property={record.locId}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.locCode" 
+                                property={record.locCode}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.orderId" 
+                                property={record.orderId}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.type" 
+                                property={record.type}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.orderItemId" 
+                                property={record.orderItemId}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.wkType" 
+                                property={record.wkType}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.matnrId" 
+                                property={record.matnrId}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.maktx" 
+                                property={record.maktx}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.matnrCode" 
+                                property={record.matnrCode}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.trackCode" 
+                                property={record.trackCode}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.unit" 
+                                property={record.unit}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.anfme" 
+                                property={record.anfme}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.qty" 
+                                property={record.qty}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.workQty" 
+                                property={record.workQty}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.batch" 
+                                property={record.batch}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.splrBatch" 
+                                property={record.splrBatch}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.spec" 
+                                property={record.spec}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.model" 
+                                property={record.model}
+                            />
+                        </Grid>
+                        <Grid item xs={6}>
+                            <PanelTypography
+                                title="table.field.locItem.fieldsIndex" 
+                                property={record.fieldsIndex}
+                            />
+                        </Grid>
+
+                    </Grid>
+                </CardContent>
+            </Card >
+        </>
+    );
+};
+
+export default WarehouseStockPanel;
diff --git a/rsf-admin/src/page/statistics/stockManage/index.jsx b/rsf-admin/src/page/statistics/stockManage/index.jsx
new file mode 100644
index 0000000..31547c4
--- /dev/null
+++ b/rsf-admin/src/page/statistics/stockManage/index.jsx
@@ -0,0 +1,18 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import {
+    ListGuesser,
+    EditGuesser,
+    ShowGuesser,
+} from "react-admin";
+
+import WarehouseStockList from "./WarehouseStockList";
+import WarehouseStockEdit from "./WarehouseStockEdit";
+
+export default {
+    list: WarehouseStockList,
+    edit: WarehouseStockEdit,
+    show: ShowGuesser,
+    recordRepresentation: (record) => {
+        return `${record.id}`
+    }
+};
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseStockController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseStockController.java
new file mode 100644
index 0000000..72b4b86
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseStockController.java
@@ -0,0 +1,52 @@
+package com.vincent.rsf.server.manager.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.vincent.rsf.framework.common.R;
+import com.vincent.rsf.server.common.domain.BaseParam;
+import com.vincent.rsf.server.common.domain.PageParam;
+import com.vincent.rsf.server.common.utils.FieldsUtils;
+import com.vincent.rsf.server.manager.entity.LocItem;
+import com.vincent.rsf.server.manager.entity.WaitPakinItem;
+import com.vincent.rsf.server.manager.service.WarehouseStockService;
+import com.vincent.rsf.server.system.controller.BaseController;
+import io.swagger.annotations.Api;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+@Api("搴撳瓨绠$悊")
+@RestController
+@RequestMapping("/warehouse/stock")
+public class WarehouseStockController extends BaseController {
+
+    @Autowired
+    private WarehouseStockService warehouseStockService;
+
+
+    @PreAuthorize("hasAuthority('manager:locItem:list')")
+    @PostMapping("/page")
+    public R page(@RequestBody Map<String, Object> param) {
+        BaseParam baseParam = buildParam(param, BaseParam.class);
+        PageParam<LocItem, BaseParam> pageParam = new PageParam<>(baseParam, LocItem.class);
+        QueryWrapper<LocItem> queryWrapper = pageParam.buildWrapper(true);
+        IPage<LocItem> pageResult = warehouseStockService.pageByStock(pageParam, queryWrapper);
+        List<LocItem> records = pageResult.getRecords();
+        for (LocItem record : records) {
+            if (!Objects.isNull(record.getFieldsIndex())) {
+                Map<String, String> fields = FieldsUtils.getFields(record.getFieldsIndex());
+                record.setExtendFields(fields);
+            }
+        }
+        pageResult.setRecords(records);
+        return R.ok(pageResult);
+    }
+
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Stock.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Stock.java
index 28d567b..608d388 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Stock.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Stock.java
@@ -226,18 +226,4 @@
         return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime);
     }
 
-
-
-    public Boolean getStatusBool(){
-        if (null == this.status){ return null; }
-        switch (this.status){
-            case 1:
-                return true;
-            case 0:
-                return false;
-            default:
-                return null;
-        }
-    }
-
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/LocItemMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/LocItemMapper.java
index b6a4e29..669805d 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/LocItemMapper.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/LocItemMapper.java
@@ -1,12 +1,19 @@
 package com.vincent.rsf.server.manager.mapper;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.vincent.rsf.server.common.domain.BaseParam;
+import com.vincent.rsf.server.common.domain.PageParam;
 import com.vincent.rsf.server.manager.entity.LocItem;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 import org.springframework.stereotype.Repository;
 
 @Mapper
 @Repository
 public interface LocItemMapper extends BaseMapper<LocItem> {
 
+    IPage<LocItem> pageByStock(PageParam<LocItem, BaseParam> pageParam, @Param(Constants.WRAPPER) QueryWrapper<LocItem> queryWrapper);
 }
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WarehouseStockService.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WarehouseStockService.java
new file mode 100644
index 0000000..164a367
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WarehouseStockService.java
@@ -0,0 +1,13 @@
+package com.vincent.rsf.server.manager.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.vincent.rsf.server.common.domain.BaseParam;
+import com.vincent.rsf.server.common.domain.PageParam;
+import com.vincent.rsf.server.manager.entity.LocItem;
+
+public interface WarehouseStockService extends IService<LocItem> {
+
+    IPage<LocItem> pageByStock(PageParam<LocItem, BaseParam> pageParam, QueryWrapper<LocItem> queryWrapper);
+}
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WarehouseStockServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WarehouseStockServiceImpl.java
new file mode 100644
index 0000000..951809a
--- /dev/null
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WarehouseStockServiceImpl.java
@@ -0,0 +1,27 @@
+package com.vincent.rsf.server.manager.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.vincent.rsf.server.common.domain.BaseParam;
+import com.vincent.rsf.server.common.domain.PageParam;
+import com.vincent.rsf.server.manager.entity.LocItem;
+import com.vincent.rsf.server.manager.mapper.LocItemMapper;
+import com.vincent.rsf.server.manager.service.WarehouseStockService;
+import org.springframework.stereotype.Service;
+
+@Service("warehouseStockService")
+public class WarehouseStockServiceImpl extends ServiceImpl<LocItemMapper, LocItem> implements WarehouseStockService {
+
+    /**
+     * @author Ryan
+     * @date 2025/5/15
+     * @description: 鑾峰彇铏氭嫙涓哄瓨涓庣墿鏂欏簱瀛樺苟闆�
+     * @version 1.0
+     */
+    @Override
+    public IPage<LocItem> pageByStock(PageParam<LocItem, BaseParam> pageParam, QueryWrapper<LocItem> queryWrapper) {
+        IPage<LocItem>  results = this.baseMapper.pageByStock(pageParam, queryWrapper);
+        return results;
+    }
+}
diff --git a/rsf-server/src/main/resources/application-dev.yml b/rsf-server/src/main/resources/application-dev.yml
index 203032b..19cef04 100644
--- a/rsf-server/src/main/resources/application-dev.yml
+++ b/rsf-server/src/main/resources/application-dev.yml
@@ -14,7 +14,7 @@
     driver-class-name: com.mysql.jdbc.Driver
 #    url: jdbc:mysql://47.76.147.249:3306/rsf?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
     username: root
-    url: jdbc:mysql://127.0.0.1:3306/rsf?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
+    url: jdbc:mysql://192.168.4.50:3306/rsf?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
 #    username: rsf
     password: 34821015
     type: com.alibaba.druid.pool.DruidDataSource
diff --git a/rsf-server/src/main/resources/mapper/manager/LocItemMapper.xml b/rsf-server/src/main/resources/mapper/manager/LocItemMapper.xml
index 90ad348..852c4b5 100644
--- a/rsf-server/src/main/resources/mapper/manager/LocItemMapper.xml
+++ b/rsf-server/src/main/resources/mapper/manager/LocItemMapper.xml
@@ -2,4 +2,55 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.vincent.rsf.server.manager.mapper.LocItemMapper">
 
+    <select id="pageByStock" resultType="com.vincent.rsf.server.manager.entity.LocItem">
+        SELECT *
+        FROM (SELECT id,
+                     loc_id,
+                     loc_code,
+                     type,
+                     order_item_id,
+                     wk_type,
+                     matnr_id,
+                     maktx,
+                     matnr_code,
+                     unit,
+                     SUM(anfme)    anfme,
+                     SUM(qty)      qty,
+                     SUM(work_qty) work_qty,
+                     batch,
+                     spec,
+                     model,
+                     fields_index,
+                     update_by,
+                     create_by,
+                     update_time,
+                     create_time
+              FROM man_loc_item
+              GROUP BY matnr_id
+              UNION ALL
+              SELECT id,
+                     ''          AS loc_id,
+                     ''          AS loc_code,
+                     type,
+                     asn_item_id AS order_item_id,
+                     wk_type,
+                     matnr_id,
+                     maktx,
+                     matnr_code,
+                     unit,
+                     SUM(anfme)     anfme,
+                     SUM(qty)       qty,
+                     SUM(work_qty)  work_qty,
+                     batch,
+                     spec,
+                     model,
+                     fields_index,
+                     update_by,
+                     create_by,
+                     update_time,
+                     create_time
+              FROM man_warehouse_areas_item
+              GROUP BY matnr_id) t
+                ${ew.customSqlSegment}
+    </select>
 </mapper>

--
Gitblit v1.9.1