From 6a1343a69fac68c4259ceddbb037970461972c9e Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期一, 02 三月 2026 16:40:45 +0800
Subject: [PATCH] #备货单页面

---
 rsf-admin/src/page/orders/preparation/PreparationOrderItemEdit.jsx                               |  195 +++
 rsf-admin/src/page/orders/preparation/preparation.css                                            |    5 
 rsf-admin/src/page/orders/preparation/PreparationOrderCreate.jsx                                 |  221 +++
 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/MatPreparationController.java |  338 ++++
 rsf-admin/src/page/orders/preparation/PreparationStockWaveDialog.jsx                             |   64 +
 rsf-admin/src/page/orders/preparation/PreparationSelectMatnrModal.jsx                            |  587 ++++++++++
 rsf-admin/src/page/orders/preparation/PreparationOrderEdit.jsx                                   |  182 +++
 /dev/null                                                                                        |  120 --
 rsf-admin/src/page/orders/preparation/PreparationStockPublic.jsx                                 |   17 
 rsf-admin/src/page/orders/preparation/index.jsx                                                  |   10 
 rsf-admin/src/page/orders/preparation/PreparationOrderModal.jsx                                  |  282 ++++
 rsf-admin/src/page/orders/outStock/OutOrderList.jsx                                              |  163 +-
 rsf-admin/src/page/orders/preparation/PreparationOrderList.jsx                                   |  386 ++++++
 rsf-admin/src/page/orders/preparation/PreparationOrderPreview.jsx                                |  149 ++
 rsf-admin/src/page/orders/preparation/PreparationOrderItemCreate.jsx                             |  198 +++
 rsf-admin/src/page/orders/preparation/PreparationStockSiteDialog.jsx                             |   54 
 rsf-admin/src/page/orders/preparation/PreparationMatnrInfoModal.jsx                              |  270 ++++
 rsf-admin/src/page/orders/preparation/PreparationOrderItemList.jsx                               |  206 +++
 18 files changed, 3,194 insertions(+), 253 deletions(-)

diff --git a/rsf-admin/src/page/orders/outStock/OutOrderList.jsx b/rsf-admin/src/page/orders/outStock/OutOrderList.jsx
index a2ccae7..1f8ce47 100644
--- a/rsf-admin/src/page/orders/outStock/OutOrderList.jsx
+++ b/rsf-admin/src/page/orders/outStock/OutOrderList.jsx
@@ -2,11 +2,10 @@
 import { useLocation, useNavigate } from 'react-router-dom';
 import {
   List,
-  DatagridConfigurable,
   SearchInput,
   TopToolbar,
   Toolbar,
-  SelectColumnsButton,
+  ColumnsButton,
   EditButton,
   FilterButton,
   CreateButton,
@@ -42,8 +41,10 @@
 import { styled } from '@mui/material/styles';
 import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
 import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined';
-import { Box, Typography, Card, Stack, Drawer } from '@mui/material';
+import { Box, Typography, Card, Stack, Drawer, LinearProgress } from '@mui/material';
 import DictionarySelect from "../../components/DictionarySelect";
+import StickyDataTable from "../../components/StickyDataTable";
+import useTableLayout from '@/utils/useTableLayout';
 import BillStatusField from '../../components/BillStatusField';
 import MyCreateButton from "../../components/MyCreateButton";
 import PageEditDrawer from "../../components/PageEditDrawer";
@@ -62,25 +63,93 @@
 import OutOrderModal from "./OutOrderModal";
 import request from '@/utils/request';
 
-const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
-  '& .css-1vooibu-MuiSvgIcon-root': {
-    height: '.9em'
-  },
-  '& .RaDatagrid-row': {
-    cursor: 'auto'
-  },
-  '& .column-name': {
-  },
-  '& .opt': {
-    width: 420
-  },
-  '& .wkType': {
-    width: 110
-  },
-  '& .status': {
-    width: 90
-  },
-}));
+const ListContent = ({ drawerVal, setDrawerVal, setWaveRule, setSelectIds, setSelect }) => {
+  const { isLoading } = useListContext();
+  const { boxMaxWidth, boxMaxHeight } = useTableLayout(drawerVal);
+
+  const omittedFields = ['id', 'code', 'createTime', 'createBy$', 'memo', 'rleStatus$', 'logisNo', 'updateBy$', 'workQty', 'updateTime', 'type$'];
+
+  return (
+    <Box sx={{
+      position: 'relative',
+      maxHeight: boxMaxHeight,
+      maxWidth: boxMaxWidth,
+      overflowX: 'auto',
+      overflowY: 'auto',
+      '& .MuiTableCell-root': {
+        whiteSpace: 'nowrap',
+      },
+      '& .css-1vooibu-MuiSvgIcon-root': {
+        height: '.9em'
+      },
+      '& .RaDatagrid-row': {
+        cursor: 'auto'
+      },
+      '& .column-name': {
+      },
+      '& .opt': {
+        width: 420
+      },
+      '& .wkType': {
+        width: 110
+      },
+      '& .status': {
+        width: 90
+      },
+    }}>
+      {isLoading && (
+        <LinearProgress
+          sx={{
+            height: "2px",
+            position: 'absolute',
+            top: 0,
+            left: 0,
+            right: 0,
+          }}
+        />
+      )}
+      <StickyDataTable
+        storeKey='outStock'
+        bulkActionButtons={<PublicTaskButton setWaveRule={setWaveRule} setSelectIds={setSelectIds} />}
+        rowClick={false}
+        hiddenColumns={omittedFields}
+      >
+        <NumberField source="id" />
+        <TextField source="code" label="table.field.outStock.code" />
+        <TextField source="poCode" label="table.field.outStock.poCode" />
+        <TextField source="type$" label="table.field.outStock.type" />
+        <TextField cellClassName="wkType" source="wkType$" label="table.field.outStock.wkType" />
+        <NumberField source="anfme" label="table.field.outStock.anfme" />
+        <NumberField source="workQty" label="table.field.outStock.workQty" />
+        <NumberField source="qty" label="table.field.outStock.qty" />
+        <TextField source="logisNo" label="table.field.outStock.logisNo" />
+        <TextField source="rleStatus$" label="table.field.outStock.rleStatus" sortable={false} />
+        <TextField source="updateBy$" label="common.field.updateBy" />
+        <DateField source="updateTime" label="common.field.updateTime" showTime />
+        <TextField source="createBy$" label="common.field.createBy" />
+        <DateField source="createTime" label="common.field.createTime" showTime />
+        <TextField source="memo" label="common.field.memo" sortable={false} />
+
+        {/* 閾跺骇鏂板 */}
+        <TextField source="saleOrgName" label="閿�鍞粍缁�" />
+        <TextField source="saleUserName" label="閿�鍞憳" />
+        <DateField source="businessTime" label="鍑哄簱鏃ユ湡" />
+        <TextField source="customerId" label="瀹㈡埛缂栫爜" />
+        <TextField source="customerName" label="瀹㈡埛" />
+        <TextField source="stockOrgName" label="table.field.outStock.stockOrgName" />
+
+        <BillStatusField cellClassName="status" source="exceStatus" label="table.field.outStock.exceStatus" />
+        <WrapperField cellClassName="opt" label="common.field.opt" >
+          {/* <MyButton setCreateDialog={setManualDialog} setmodalType={setmodalType} /> */}
+          <EditButton label="toolbar.detail" icon={(<DetailsIcon />)}></EditButton>
+          <CancelButton />
+          <CompleteButton />
+          <PublicButton setDrawerVal={setDrawerVal} drawerVal={drawerVal} setSelect={setSelect} />
+        </WrapperField>
+      </StickyDataTable>
+    </Box>
+  )
+}
 
 const filters = [
   <SearchInput source="condition" alwaysOn />,
@@ -162,53 +231,19 @@
             <FilterButton />
             <CreateByOrderButton setCreateDialog={setCreateDialog} />
             <MyCreateButton onClick={() => { setManualDialog(true); setmodalType(0) }} />
-            <SelectColumnsButton preferenceKey='outStock' />
+            <ColumnsButton storeKey='outStock' />
             <ImportButton value={'outStockItem'} />
           </TopToolbar>
         )}
         perPage={DEFAULT_PAGE_SIZE}
       >
-        <StyledDatagrid
-          sx={{ width: '100%' }}
-          preferenceKey='outStock'
-          bulkActionButtons={<PublicTaskButton setWaveRule={setWaveRule} setSelectIds={setSelectIds} />}
-          rowClick={false}
-          expandSingle={true}
-          omit={['id', 'code', 'createTime', 'createBy$', 'memo', 'rleStatus$', 'logisNo', 'updateBy$', 'workQty', 'updateTime', 'type$']}
-        >
-          <NumberField source="id" />
-          <TextField source="code" label="table.field.outStock.code" />
-          <TextField source="poCode" label="table.field.outStock.poCode" />
-          <TextField source="type$" label="table.field.outStock.type" />
-          <TextField cellClassName="wkType" source="wkType$" label="table.field.outStock.wkType" />
-          <NumberField source="anfme" label="table.field.outStock.anfme" />
-          <NumberField source="workQty" label="table.field.outStock.workQty" />
-          <NumberField source="qty" label="table.field.outStock.qty" />
-          <TextField source="logisNo" label="table.field.outStock.logisNo" />
-          <TextField source="rleStatus$" label="table.field.outStock.rleStatus" sortable={false} />
-          <TextField source="updateBy$" label="common.field.updateBy" />
-          <DateField source="updateTime" label="common.field.updateTime" showTime />
-          <TextField source="createBy$" label="common.field.createBy" />
-          <DateField source="createTime" label="common.field.createTime" showTime />
-          <TextField source="memo" label="common.field.memo" sortable={false} />
-
-          {/* 閾跺骇鏂板 */}
-          <TextField source="saleOrgName" label="閿�鍞粍缁�" />
-          <TextField source="saleUserName" label="閿�鍞憳" />
-          <DateField source="businessTime" label="鍑哄簱鏃ユ湡" />
-          <TextField source="customerId" label="瀹㈡埛缂栫爜" />
-          <TextField source="customerName" label="瀹㈡埛" />
-          <TextField source="stockOrgName" label="table.field.outStock.stockOrgName" />  
-
-          <BillStatusField cellClassName="status" source="exceStatus" label="table.field.outStock.exceStatus" />
-          <WrapperField cellClassName="opt" label="common.field.opt" >
-            {/* <MyButton setCreateDialog={setManualDialog} setmodalType={setmodalType} /> */}
-            <EditButton label="toolbar.detail" icon={(<DetailsIcon />)}></EditButton>
-            <CancelButton />
-            <CompleteButton />
-            <PublicButton setDrawerVal={setDrawerVal} drawerVal={drawerVal} setSelect={setSelect} />
-          </WrapperField>
-        </StyledDatagrid>
+        <ListContent
+          drawerVal={drawerVal}
+          setDrawerVal={setDrawerVal}
+          setWaveRule={setWaveRule}
+          setSelectIds={setSelectIds}
+          setSelect={setSelect}
+        />
       </List>
       {/* <OutOrderCreate
         open={manualDialog}
diff --git a/rsf-admin/src/page/orders/preparation/MatPreparationItemList.jsx b/rsf-admin/src/page/orders/preparation/MatPreparationItemList.jsx
deleted file mode 100644
index 1f3317e..0000000
--- a/rsf-admin/src/page/orders/preparation/MatPreparationItemList.jsx
+++ /dev/null
@@ -1,216 +0,0 @@
-import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
-import { useFetcher, useNavigate } from 'react-router-dom';
-import {
-    List,
-    DatagridConfigurable,
-    SearchInput,
-    TopToolbar,
-    SelectColumnsButton,
-    FilterButton,
-    useTranslate,
-    TextField,
-    NumberField,
-    DateField,
-    BooleanField,
-    ReferenceField,
-    TextInput,
-    useRefresh,
-    DateInput,
-    useNotify,
-    SelectInput,
-    useListContext,
-    NumberInput,
-    useGetRecordId,
-} from 'react-admin';
-import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
-import { Box, Typography, Card, Stack, LinearProgress } from '@mui/material';
-import MyCreateButton from "../components/MyCreateButton";
-import PageDrawer from "../components/PageDrawer";
-import { styled } from '@mui/material/styles';
-// import TaskItemCreate from "./TaskItemCreate";
-import request from '@/utils/request';
-
-const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
-    '& .css-1vooibu-MuiSvgIcon-root': {
-        height: '.9em'
-    },
-    '& .RaDatagrid-row': {
-        cursor: 'auto',
-        maring: '1em'
-    },
-    '& .column-name': {
-    },
-    '& .opt': {
-        width: 220
-    },
-}));
-
-const filters = [
-    <SearchInput source="condition" alwaysOn />,
-    // <DateInput label='common.time.after' source="timeStart" />,
-    // <DateInput label='common.time.before' source="timeEnd" />,
-    // <NumberInput source="taskId" label="table.field.taskItem.taskId" />,
-    // <NumberInput source="orderId" label="table.field.taskItem.orderId" />,
-    // <NumberInput source="orderType" label="table.field.taskItem.orderType" />,
-    // <NumberInput source="orderItemId" label="table.field.taskItem.orderItemId" />,
-    <NumberInput source="matnrId" label="table.field.taskItem.matnrId" />,
-    <TextInput source="maktx" label="table.field.taskItem.maktx" />,
-    <TextInput source="matnrCode" label="table.field.taskItem.matnrCode" />,
-    <TextInput source="unit" label="table.field.taskItem.unit" />,
-    <NumberInput source="anfme" label="table.field.taskItem.anfme" />,
-    <TextInput source="platOrderCode" label="table.field.asnOrderItem.platOrderCode" />,
-    <TextInput source="platWorkCode" label="table.field.asnOrderItem.platWorkCode" />,
-    <TextInput source="projectCode" label="table.field.asnOrderItem.projectCode" />,
-    <TextInput source="batch" label="table.field.taskItem.batch" />,
-    <TextInput source="spec" label="table.field.taskItem.spec" />,
-    <TextInput source="model" label="table.field.taskItem.model" />,
-    <TextInput label="common.field.memo" source="memo" />,
-    <SelectInput
-        label="common.field.status"
-        source="status"
-        choices={[
-            { id: '1', name: 'common.enums.statusTrue' },
-            { id: '0', name: 'common.enums.statusFalse' },
-        ]}
-        resettable
-    />,
-]
-
-const MatPreparationItemList = () => {
-    const translate = useTranslate();
-    const [createDialog, setCreateDialog] = useState(false);
-    const [drawerVal, setDrawerVal] = useState(false);
-    const recordId = useGetRecordId();
-
-    return (
-        <Box display="flex">
-            <List
-                resource="preparationItem"
-                sx={{
-                    flexGrow: 1,
-                    transition: (theme) =>
-                        theme.transitions.create(['all'], {
-                            duration: theme.transitions.duration.enteringScreen,
-                        }),
-                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
-                }}
-                title={"menu.preparationItem"}
-                empty={false}
-                pagination={false}
-                filters={filters}
-                filter={{ taskId: recordId }}
-                sort={{ field: "create_time", order: "desc" }}
-                actions={(
-                    <TopToolbar>
-                        <FilterButton />
-                        {/* <MyCreateButton onClick={() => { setCreateDialog(true) }} /> */}
-                        <SelectColumnsButton preferenceKey='preparationItem' />
-                        {/* <MyExportButton /> */}
-                    </TopToolbar>
-                )}
-                perPage={DEFAULT_PAGE_SIZE}
-            >
-                <DynamicFields />
-            </List>
-            <TaskItemCreate
-                open={createDialog}
-                setOpen={setCreateDialog} />
-            <PageDrawer
-                title='PreparationItem Detail'
-                drawerVal={drawerVal}
-                setDrawerVal={setDrawerVal}
-            >
-            </PageDrawer>
-        </Box>
-    )
-}
-
-export default MatPreparationItemList;
-
-
-const DynamicFields = (props) => {
-    const translate = useTranslate();
-    const notify = useNotify();
-    const [columns, setColumns] = useState([]);
-    const { isLoading } = useListContext();
-    const refresh = useRefresh();
-    useEffect(() => {
-        getDynamicFields();
-    }, []);
-
-    const getDynamicFields = async () => {
-        const { data: { code, data, msg }, } = await request.get("/fields/enable/list");
-        if (code == 200) {
-            const arr = [
-                <TextField source="matnrCode" label="鐗╂枡缂栫爜" />,
-                <TextField source="maktx" label="鐗╂枡鍚嶇О" />,
-                <TextField source="spec" label="瑙勬牸" />,
-                <TextField source="unit" label="鍗曚綅" />,
-                <TextField source="anfme" label="鏁伴噺" />,
-
-
-                // <NumberField source="id" />,
-                // // <NumberField source="taskId" label="table.field.taskItem.taskId" />,
-                // // <NumberField source="orderId" label="table.field.taskItem.orderId" />,
-                // // <NumberField source="orderType$" label="table.field.taskItem.orderType" />,
-                // // <NumberField source="wkType$" label="table.field.taskItem.wkType" />,
-                // <NumberField source="orderItemId" label="table.field.taskItem.orderItemId" />,
-                // <NumberField source="matnrId" label="table.field.taskItem.matnrId" />,
-                // <TextField source="maktx" label="table.field.taskItem.maktx" />,
-                // <TextField source="matnrCode" label="table.field.taskItem.matnrCode" />,
-                // <TextField source="unit" label="table.field.taskItem.unit" />,
-                // <NumberField source="anfme" label="table.field.taskItem.anfme" />,
-                // <TextField source="platOrderCode" label="table.field.asnOrderItem.platOrderCode" />,
-                // <TextField source="platWorkCode" label="table.field.asnOrderItem.platWorkCode" />,
-                // <TextField source="projectCode" label="table.field.asnOrderItem.projectCode" />,
-                // <TextField source="batch" label="table.field.taskItem.batch" />,
-                // <TextField source="isptResult$" label="table.field.taskItem.isptResult" />,
-                // <TextField source="spec" label="table.field.taskItem.spec" />,
-                // <TextField source="model" label="table.field.taskItem.model" />,
-            ]
-            const fields = data.map(el => <TextField key={el.fields} source={`extendFields.[${el.fields}]`} label={el.fieldsAlise} />)
-            const lastArr = [
-                <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
-                    <TextField source="nickname" />
-                </ReferenceField>,
-                <DateField source="updateTime" label="common.field.updateTime" showTime />,
-                <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
-                    <TextField source="nickname" />
-                </ReferenceField>,
-                <DateField source="createTime" label="common.field.createTime" showTime />,
-                <BooleanField source="statusBool" label="common.field.status" sortable={false} />,
-                <TextField source="memo" label="common.field.memo" sortable={false} />,
-            ]
-            setColumns([...arr, ...fields, ...lastArr]);
-        } else {
-            notify(msg);
-        }
-    }
-
-    return (
-        <Box sx={{ position: 'relative', minHeight: "60vh", }}>
-            {isLoading && (
-                <LinearProgress
-                    sx={{
-                        height: "2px",
-                        position: 'absolute',
-                        top: 0,
-                        left: 0,
-                        right: 0,
-                    }}
-                />
-            )}
-            {columns.length > 0 &&
-                <StyledDatagrid
-                    preferenceKey='preparationItem'
-                    bulkActionButtons={false}
-                    rowClick={false}
-                    // expand={() => <TaskItemPanel />}
-                    expandSingle={true}
-                    omit={['id', 'createTime', 'createBy', 'memo', 'taskId', 'orderId', 'orderItemId', 'matnrId', 'platWorkCode', 'projectCode','statusBool']}
-                >
-                    {columns.map((column) => column)}
-                </StyledDatagrid>}
-        </Box>
-    )
-}
diff --git a/rsf-admin/src/page/orders/preparation/MatPreparationList.jsx b/rsf-admin/src/page/orders/preparation/MatPreparationList.jsx
deleted file mode 100644
index 58580fc..0000000
--- a/rsf-admin/src/page/orders/preparation/MatPreparationList.jsx
+++ /dev/null
@@ -1,422 +0,0 @@
-import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
-import { useNavigate } from 'react-router-dom';
-import request from '@/utils/request';
-
-import {
-    List,
-    DatagridConfigurable,
-    SearchInput,
-    TopToolbar,
-    SelectColumnsButton,
-    FilterButton,
-    BulkDeleteButton,
-    WrapperField,
-    useRecordContext,
-    useTranslate,
-    useNotify,
-    useRefresh,
-    TextField,
-    NumberField,
-    DateField,
-    AutocompleteInput,
-    BooleanField,
-    TextInput,
-    DateInput,
-    SelectInput,
-    NumberInput,
-    Button,
-    EditButton,
-} from 'react-admin';
-import { Box, Typography, Card, Stack, Drawer } from '@mui/material';
-import { styled } from '@mui/material/styles';
-import SwapVertIcon from '@mui/icons-material/SwapVert';
-import AlignVerticalTopIcon from '@mui/icons-material/AlignVerticalTop';
-import TaskAltIcon from '@mui/icons-material/TaskAlt';
-import CancelIcon from '@mui/icons-material/Cancel';
-import PageDrawer from "../../components/PageDrawer";
-import MatPreparationPanel from "./MatPreparationPanel";
-import MyField from "../../components/MyField";
-import ConfirmButton from "../../components/ConfirmButton";
-import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE, AUTO_RUN_CHECK_ORDERS } from '@/config/setting';
-import PlayArrowOutlinedIcon from '@mui/icons-material/PlayArrowOutlined';
-import PauseIcon from '@mui/icons-material/Pause';
-import PauseCircleOutlineIcon from '@mui/icons-material/PauseCircleOutline';
-import * as Common from '@/utils/common';
-import ColorizeOutlinedIcon from '@mui/icons-material/ColorizeOutlined';
-import GradingOutlinedIcon from '@mui/icons-material/GradingOutlined';
-
-const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
-    '& .css-1vooibu-MuiSvgIcon-root': {
-        height: '.9em'
-    },
-    '& .RaDatagrid-row': {
-        cursor: 'auto'
-    },
-    '& .column-name': {
-    },
-    '& .opt': {
-        width: 248
-    },
-    '& .MuiTableCell-root': {
-        whiteSpace: 'nowrap',
-        overflow: 'visible',
-        textOverflow: 'unset'
-    }
-
-}));
-
-
-const MatPreparationList = (props) => {
-    const translate = useTranslate();
-    const refresh = useRefresh();
-    const [drawerVal, setDrawerVal] = useState(false);
-    const [autoExce, setAutoExce] = useState(false);
-    const dict = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_warehouse_type')) || [];
-
-    useEffect(() => {
-        // getConfig()
-    }, [])
-
-    // const getConfig = async () => {
-    //     const { data: { code, data, msg } } = await request.get('/config/flag/' + AUTO_RUN_CHECK_ORDERS);
-    //     if (code === 200) {
-    //         setAutoExce(JSON.parse(data?.val))
-    //     }
-    // }
-
-    // useEffect(() => {
-    //     const interval = setInterval(() => {
-    //         refresh();
-    //     }, 5000); // 姣�5绉掑埛鏂颁竴娆�
-
-    //     return () => clearInterval(interval); // 娓呴櫎瀹氭椂鍣�
-    // }, [refresh])
-
-    const filters = [
-        <SearchInput source="condition" alwaysOn />,
-        // <DateInput label='common.time.after' source="timeStart" />,
-        // <DateInput label='common.time.before' source="timeEnd" />,
-        <TextInput source="orderCode" label="table.field.preparation.orderCode" />,
-        <NumberInput source="orderStatus" label="table.field.preparation.orderStatus" />,
-        <DateInput source="orderTime" label="table.field.preparation.orderTime" />,
-        <NumberInput source="anfme" label="table.field.preparation.anfme" />,
-
-        // <AutocompleteInput choices={dict} optionText='label' optionValue="value" source="warehType" label="table.field.task.warehType" />,
-        // <TextInput source="orgLoc" label="table.field.task.orgLoc" />,
-        // <TextInput source="orgSite" label="table.field.task.orgSite" />,
-        // <TextInput source="targLoc" label="table.field.task.targLoc" />,
-        // <TextInput source="targSite" label="table.field.task.targSite" />,
-        // <TextInput source="barcode" label="table.field.task.barcode" />,
-        // <TextInput source="robotCode" label="table.field.task.robotCode" />,
-        // <NumberInput source="exceStatus" label="table.field.task.exceStatus" />,
-        // <TextInput source="expDesc" label="table.field.task.expDesc" />,
-        // <NumberInput source="sort" label="table.field.task.sort" />,
-        // <TextInput source="expCode" label="table.field.task.expCode" />,
-        // <DateInput source="startTime" label="table.field.task.startTime" />,
-        // <DateInput source="endTime" label="table.field.task.endTime" />,
-        // <TextInput label="common.field.memo" source="memo" />,
-        // <SelectInput
-        //     label="common.field.status"
-        //     source="status"
-        //     choices={[
-        //         { id: '1', name: 'common.enums.statusTrue' },
-        //         { id: '0', name: 'common.enums.statusFalse' },
-        //     ]}
-        //     resettable
-        // />,
-    ]
-
-    return (
-        <Box display="flex">
-            <List
-                queryOptions={{ refetchInterval: 5000 }}
-                sx={{
-                    flexGrow: 1,
-                    transition: (theme) =>
-                        theme.transitions.create(['all'], {
-                            duration: theme.transitions.duration.enteringScreen,
-                        }),
-                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
-                }}
-                title={"menu.preparation"}
-                empty={false}
-                filters={filters}
-                sort={{ field: "sort", order: "desc" }}
-                actions={(
-                    <TopToolbar>
-                        {/* <BulkAutoStartButton autoExce={autoExce} setAutoExce={setAutoExce}/> */}
-                        <BulkAutoPauseButton autoExce={autoExce} setAutoExce={setAutoExce} />
-                        <FilterButton />
-                        <SelectColumnsButton preferenceKey='preparation' />
-                    </TopToolbar>
-                )}
-                perPage={DEFAULT_PAGE_SIZE}
-            >
-                <StyledDatagrid
-                    preferenceKey='preparation'
-                    bulkActionButtons={false}
-                    rowClick={false}
-                    expand={<MatPreparationPanel />}
-                    expandSingle={true}
-                    omit={['id', 'sort', 'createTime', 'createBy$', 'memo', 'robotCode', 'exceStatus', 'expDesc', 'expCode', 'status','warehType$']}
-                >
-                    <NumberField source="id" />
-                    <TextField source="taskCode" label="澶囨枡鍙�" />
-                    <NumberField source="status" label="table.field.task.exceStatus" />
-                    <DateField source="startTime" label="table.field.task.startTime" />
-                    <NumberField source="sort" label="鏁伴噺" />
-                    {/* table.field.task.sort */}
-                    
-                    {/* <TextField source="taskCode" label="table.field.task.taskCode" />
-                    <NumberField source="taskStatus$" label="table.field.task.taskStatus" />
-                    <NumberField source="taskType$" label="table.field.task.taskType" />
-                    <NumberField source="warehType$" label="table.field.task.warehType" />
-                    <TextField source="orgLoc" label="table.field.task.orgLoc" />
-                    <TextField source="orgSite" label="table.field.task.orgSite" />
-                    <TextField source="targLoc" label="table.field.task.targLoc" />
-                    <TextField source="targSite" label="table.field.task.targSite" />
-                    <TextField source="barcode" label="table.field.task.barcode" />
-                    <TextField source="robotCode" label="table.field.task.robotCode" />
-                    <NumberField source="exceStatus" label="table.field.task.exceStatus" />
-                    <TextField source="expDesc" label="table.field.task.expDesc" />
-                    <NumberField source="sort" label="table.field.task.sort" />
-                    <TextField source="expCode" label="table.field.task.expCode" />
-                    <TextField source="updateBy$" label="common.field.updateBy" />
-                    <TextField source="createBy$" label="common.field.createBy" />
-                    <DateField source="updateTime" label="common.field.updateTime" showTime />
-                    <DateField source="createTime" label="common.field.createTime" showTime />
-                    <TextField source="memo" label="common.field.memo" sortable={false} /> */}
-                    <WrapperField cellClassName="opt" label="common.field.opt" onClick={(e) => e.stopPropagation()} >
-                        <EditButton label="toolbar.detail" />
-                        <DoneButton title="杞崲" label="杞崲" sx={{ padding: '1px', fontSize: '.75rem' }} ></DoneButton>
-                        <CancelButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
-                        <CheckButton />
-                        <PickButton />
-                        {/* <SetTopButton /> */}
-                    </WrapperField>
-                </StyledDatagrid>
-            </List>
-            <PageDrawer
-                title='Preparation Detail'
-                drawerVal={drawerVal}
-                setDrawerVal={setDrawerVal}
-            >
-            </PageDrawer>
-        </Box>
-    )
-}
-
-export default MatPreparationList;
-
-/**
- * 鐩樼偣
- * @returns te
- */
-const CheckButton = () => {
-    const record = useRecordContext();
-    const notify = useNotify();
-    const refresh = useRefresh();
-
-    const checkClick = async () => {
-        const { data: { code, data, msg } } = await request.post(`/task/check/` + record.id);
-        if (code === 200) {
-            notify(msg);
-            refresh();
-        } else {
-            notify(msg);
-        }
-    }
-
-    return (record?.taskStatus == 199 && record?.taskType == 107 ? <ConfirmButton label={"toolbar.check"} startIcon={<GradingOutlinedIcon />} onConfirm={checkClick} /> : <></>)
-}
-
-/**鑷姩涓嬪彂浠诲姟**/
-const BulkAutoStartButton = ({ autoExce, setAutoExce }) => {
-    const notify = useNotify();
-    const startClick = async () => {
-        setAutoExce(true)
-        const { data: { code, data, msg } } = await request.post('/config/byFlag', { val: true, flag: 'AUTO_RUN_CHECK_ORDERS' });
-        if (code === 200) {
-            notify(msg);
-        } else {
-            notify(msg);
-        }
-    }
-    return (
-        !autoExce ? <Button label="toolbar.autoStartLocs" onClick={startClick} startIcon={<PlayArrowOutlinedIcon />} /> : <></>
-    )
-}
-
-const BulkAutoPauseButton = ({ autoExce, setAutoExce }) => {
-    const notify = useNotify();
-    const pauseClick = async () => {
-        const { data: { code, data, msg } } = await request.post('/config/byFlag', { val: false, flag: 'AUTO_RUN_CHECK_ORDERS' });
-        if (code === 200) {
-            notify(msg);
-            setAutoExce(false)
-        } else {
-            notify(msg);
-        }
-    }
-    return (
-        autoExce ? <Button label="toolbar.pause" onClick={pauseClick} startIcon={<PauseIcon />} /> : <></>
-    )
-}
-
-
-/**
- * 鎷f枡鍑哄簱
- * @returns 
- */
-const PickButton = () => {
-    const record = useRecordContext();
-    const notify = useNotify();
-    const refresh = useRefresh();
-
-    const pickClick = async () => {
-        const { data: { code, data, msg } } = await request.post(`/task/pick/` + record.id);
-        if (code === 200) {
-            notify(msg);
-            refresh();
-        } else {
-            notify(msg);
-        }
-    }
-
-    return (
-        record?.taskStatus == 199 && record?.taskType == 103 ? <ConfirmButton label={"toolbar.pick"} startIcon={<ColorizeOutlinedIcon />} onConfirm={pickClick} /> : <></>
-    )
-}
-
-
-/**
- * 瀹屾垚鎿嶄綔
- * @returns 
- */
-const DoneButton = (props) => {
-    const record = useRecordContext();
-    const notify = useNotify();
-    const refresh = useRefresh();
-    const clickComplete = () => {
-        completeTask(record)
-    };
-    //瀹屾垚浠诲姟
-    const completeTask = async (row) => {
-        const { data: { code, data, msg } } = await request.post(`task/complete/` + row.id);
-        if (code === 200) {
-            notify(msg);
-            refresh();
-        } else {
-            notify(msg);
-        }
-    }
-    return (
-        ((record?.taskStatus < 98) || (record?.taskType >= 101 && record?.taskStatus < 198)) || (record?.taskType == 11 && record?.taskStatus == 101) ? (<ConfirmButton label={"杞崲"} color="secondary" startIcon={<TaskAltIcon />} onConfirm={clickComplete} />) : (<></>)
-    )
-}
-
-/**
- * 鍙栨秷鎸夐挳
- * @returns 
- */
-const CancelButton = () => {
-    const record = useRecordContext();
-    const notify = useNotify();
-    const refresh = useRefresh();
-    const clickCancel = () => {
-        cancleTask(record)
-    };
-    //鍙栨秷浠诲姟
-    const cancleTask = async (row) => {
-        const { data: { code, data, msg } } = await request.post(`/task/remove/` + row.id);
-        if (code === 200) {
-            notify(msg);
-            refresh();
-        } else {
-            notify(msg);
-        }
-    }
-    return (
-        (record.taskStatus == 1 || record.taskStatus == 101) && (record.taskType == 1 || record.taskType == 101 || record.taskType == 10 || record.taskType == 107 || record.taskType == 103 || record.taskType == 11) ?
-            <ConfirmButton
-                onConfirm={clickCancel}
-                startIcon={<CancelIcon />}
-                label={"toolbar.cancel"}>
-            </ConfirmButton>
-            :
-            <></>
-    )
-}
-/**
- *  缃《鎿嶄綔
- * @returns 
- */
-const SetTopButton = () => {
-    const record = useRecordContext();
-    const notify = useNotify();
-    const refresh = useRefresh();
-    const clickTop = (event) => {
-        event.stopPropagation();
-        topTask(record)
-    };
-    //缃《浠诲姟
-    const topTask = async (row) => {
-        const { data: { code, data, msg } } = await request.post(`/task/top/` + row.id);
-        if (code === 200) {
-            notify(msg);
-            refresh();
-        } else {
-            notify(msg);
-        }
-    }
-    return (
-        (record.taskStatus == 1 || record.taskStatus == 101) && (record.taskType == 1 || record.taskType == 101 || record.taskType == 10 || record.taskType == 103 || record.taskType == 11) ?
-            <Button
-                onClick={clickTop}
-                label="toolbar.top">
-                <AlignVerticalTopIcon />
-            </Button>
-            :
-            <></>
-    )
-}
-
-/**
- * 鎵归噺鍙栨秷
- * @returns 
- */
-const BulkCancelButton = () => {
-    const record = useRecordContext();
-    const clickCancel = () => {
-        cancleTask([record])
-    };
-    //鍙栨秷浠诲姟
-    const cancleTask = async (row) => { }
-    return (
-        <Button
-            onClick={clickCancel}
-            label="toolbar.cancel">
-            <CancelIcon />
-        </Button>
-    )
-}
-
-/**
- * 鎵归噺鎺掑簭
- * @returns 
- */
-const BulkResortButton = () => {
-    const record = useRecordContext();
-    const bulkResort = () => {
-        resortTask([record])
-    };
-    //鎵归噺鎺掑簭
-    const resortTask = async (row) => { }
-    return (
-        <Button
-            onClick={bulkResort}
-            label="toolbar.resort">
-            <SwapVertIcon />
-        </Button>
-    )
-}
diff --git a/rsf-admin/src/page/orders/preparation/MatPreparationPanel.jsx b/rsf-admin/src/page/orders/preparation/MatPreparationPanel.jsx
deleted file mode 100644
index 3892186..0000000
--- a/rsf-admin/src/page/orders/preparation/MatPreparationPanel.jsx
+++ /dev/null
@@ -1,120 +0,0 @@
-import React, { useState, useRef, useEffect, useMemo } from "react";
-import { Box } from '@mui/material';
-import {
-    List,
-    DatagridConfigurable,
-    useRecordContext,
-    useTranslate,
-    TextField,
-    NumberField,
-    DateField,
-    BooleanField,
-    ReferenceField,
-} from 'react-admin';
-import { styled } from '@mui/material/styles';
-import PageDrawer from "../../components/PageDrawer";
-import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
-import * as Common from '@/utils/common';
-
-const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
-    '& .css-1vooibu-MuiSvgIcon-root': {
-        height: '.9em'
-    },
-    '& .RaDatagrid-row': {
-        cursor: 'auto'
-    },
-    '& .column-name': {
-    },
-    '& .opt': {
-        width: 200
-    },
-    // 娣诲姞鍒楀鏍峰紡
-    '& .col-code': {
-        width: 120,
-        minWidth: 120,
-        maxWidth: 150,
-    },
-    '& .col-name': {
-        width: 180,
-        minWidth: 150,
-        maxWidth: 250,
-    },
-    '& .col-spec': {
-        width: 120,
-        minWidth: 120,
-        maxWidth: 200,
-    },
-    '& .col-unit': {
-        width: 80,
-        minWidth: 60,
-        maxWidth: 100,
-    },
-    '& .col-quantity': {
-        width: 100,
-        minWidth: 80,
-        maxWidth: 120,
-    },
-}));
-
-const MatPreparationPanel = () => {
-    const record = useRecordContext();
-    const translate = useTranslate();
-    const [createDialog, setCreateDialog] = useState(false);
-    const [drawerVal, setDrawerVal] = useState(false);
-    return (
-        <>
-            <Box display="flex">
-                <List resource="preparationItem"
-                    sx={{
-                        flexGrow: 1,
-                        transition: (theme) =>
-                            theme.transitions.create(['all'], {
-                                duration: theme.transitions.duration.enteringScreen,
-                            }),
-                        marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
-                    }}
-                    filter={{ taskId: record.id }}
-                    // title={"menu.taskItem"}
-                    pagination={false}
-                    empty={false}
-                    actions={false}
-                >
-                    <StyledDatagrid
-                        preferenceKey='preparationItem'
-                        bulkActionButtons={false}
-                        rowClick={false}
-                        expandSingle
-                        omit={['id', 'taskId', 'createTime', 'createBy$', 'memo', 'taskId', 'orderId', 'orderItemId', 'matnrId']}
-                    >
-                        <TextField source="matnrCode" label="鐗╂枡缂栫爜" cellClassName="col-code" />,
-                        <TextField source="maktx" label="鐗╂枡鍚嶇О" cellClassName="col-name" />,
-                        {/* <TextField source="spec" label="瑙勬牸" cellClassName="col-spec" />, */}
-                        {/* <TextField source="unit" label="鍗曚綅" cellClassName="col-unit" />, */}
-                        <TextField source="anfme" label="鏁伴噺" cellClassName="col-quantity" />,
-                        {/* <NumberField source="id" />
-                        <NumberField source="taskId" label="table.field.taskItem.taskId" />
-                        <NumberField source="orderId" label="table.field.preparationItem.orderId" />
-                        <NumberField source="orderType$" label="table.field.preparationItem.orderType" />
-                        <TextField source="sourceCode" label="table.field.taskItem.sourceCode" />
-                        <NumberField source="orderItemId" label="table.field.preparationItem.orderItemId" />
-                        <NumberField source="matnrId" label="table.field.preparationItem.matnrId" />
-                        <TextField source="maktx" label="table.field.preparationItem.maktx" />
-                        <TextField source="matnrCode" label="table.field.preparationItem.matnrCode" />
-                        <TextField source="unit" label="table.field.preparationItem.unit" />
-                        <NumberField source="anfme" label="table.field.preparationItem.anfme" />
-                        <TextField source="batch" label="table.field.preparationItem.batch" />
-                        <TextField source="spec" label="table.field.preparationItem.spec" />
-                        <TextField source="model" label="table.field.preparationItem.model" /> */}
-                        {/* <TextField source="updateBy$" label="common.field.updateBy"/>
-                        <TextField source="createBy$" label="common.field.createBy"/>
-                        <DateField source="updateTime" label="common.field.updateTime" showTime />
-                        <DateField source="createTime" label="common.field.createTime" showTime />
-                        <TextField source="memo" label="common.field.memo" sortable={false} /> */}
-                    </StyledDatagrid>
-                </List>
-            </Box>
-        </>
-    );
-};
-
-export default MatPreparationPanel;
diff --git a/rsf-admin/src/page/orders/preparation/PreparationMatnrInfoModal.jsx b/rsf-admin/src/page/orders/preparation/PreparationMatnrInfoModal.jsx
new file mode 100644
index 0000000..e51d215
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/PreparationMatnrInfoModal.jsx
@@ -0,0 +1,270 @@
+import React, { useState, useEffect } from "react";
+import {
+    Dialog,
+    DialogActions,
+    DialogContent,
+    DialogTitle,
+    Stack,
+    Grid,
+    TextField,
+    Box,
+    Button,
+    Paper,
+    styled
+} from '@mui/material';
+import DialogCloseButton from "../../components/DialogCloseButton";
+import { EDIT_MODE, DEFAULT_START_PAGE, DEFAULT_PAGE_SIZE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
+import { useTranslate, useNotify, useRefresh } from 'react-admin';
+import request from '@/utils/request';
+import { DataGrid } from '@mui/x-data-grid';
+import SaveIcon from '@mui/icons-material/Save';
+import TreeSelectInput from "@/page/components/TreeSelectInput";
+const MatnrInfoModal = (props) => {
+    const { open, setOpen, data, setData } = props;
+
+    const translate = useTranslate();
+    const notify = useNotify();
+    const refresh = useRefresh();
+
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+        }
+    };
+
+    const [formData, setFormData] = useState({});
+    const [tableData, setTableData] = useState([]);
+    const [dyFields, setDyFields] = useState([]);
+    const [selectedRows, setSelectedRows] = useState([]);
+    const [page, setPage] = useState({ page: DEFAULT_START_PAGE, pageSize: DEFAULT_PAGE_SIZE });
+    const [rowCount, setRowCount] = useState(0);
+    const [isLoading, setIsLoading] = useState(false);
+    const handleChange = (e) => {
+        const { name, value } = e.target;
+        setFormData(() => ({
+            [name]: value
+        }));
+    };
+
+    const reset = () => {
+        setFormData({
+            name: null,
+            code: null,
+            groupId: null
+        })
+    }
+
+    const handleSubmit = () => {
+        const hasarr = data.map(el => +el.matnrId)
+        const selectedData = selectedRows.filter(item => !hasarr.includes(item)).map(id => (tableData.find(row => row.id === id)));
+        const value = selectedData.map((el => {
+            const dynamicFields = dyFields.reduce((acc, item) => {
+                acc[item.fields] = el['extendFields']?.[item.fields] || '';
+                return acc;
+            }, {});
+            return {
+                matnrId: el.id,
+                maktx: el.name,
+                matnrCode: el.code,
+                stockUnit: el.stockUnit || '',
+                purUnit: el.purchaseUnit || '',
+                ...dynamicFields
+            }
+        }))
+        setData([...data, ...value]);
+        setOpen(false);
+        reset();
+    };
+
+    const getData = async () => {
+        setIsLoading(true)
+        console.log(page);
+        const res = await request.post(`/matnr/page`, {
+            ...formData,
+            current: page?.page,
+            pageSize: page?.pageSize,
+            orderBy: "create_time desc"
+        });
+        if (res?.data?.code === 200) {
+            setTableData(res.data.data.records);
+            setRowCount(res.data?.data?.total);
+
+        } else {
+            notify(res.data.msg);
+        }
+        setIsLoading(false)
+
+    };
+
+    useEffect(() => {
+        getData();
+    }, [open, page]);
+
+    const handleSearch = () => {
+        getData()
+    };
+
+    return (
+        <Dialog
+            open={open}
+            onClose={handleClose}
+            aria-labelledby="form-dialog-title"
+            fullWidth
+            disableRestoreFocus
+            maxWidth="lg"
+        >
+            <DialogTitle id="form-dialog-title" sx={{
+                position: 'sticky',
+                top: 0,
+                backgroundColor: 'background.paper',
+                zIndex: 1000
+            }}>
+                {translate("common.action.newAddMats")}
+                <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
+                    <DialogCloseButton onClose={handleClose} />
+                </Box>
+            </DialogTitle>
+            <DialogContent sx={{ mt: 2 }}>
+                <Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
+                    <Grid container spacing={2}>
+                        <Grid item md={4}>
+                            <TextField
+                                label={translate('table.field.matnr.name')}
+                                name="name"
+                                value={formData.name}
+                                onChange={handleChange}
+                                size="small"
+                            />
+                        </Grid>
+                        <Grid item md={4}>
+                            <TextField
+                                label={translate('table.field.matnr.code')}
+                                name="code"
+                                value={formData.code}
+                                onChange={handleChange}
+                                size="small"
+                            />
+                        </Grid>
+                        <Grid item md={4}>
+                            <TreeSelectInput
+                                label="table.field.matnr.groupId"
+                                value={formData.groupId}
+                                resource={'matnrGroup'}
+                                source="groupId"
+                                name="groupId"
+                                onChange={handleChange}
+                            />
+                        </Grid>
+                    </Grid>
+                </Box>
+                <Box sx={{ mt: 2 }}>
+                    <Stack direction="row" spacing={2}>
+                        <Button variant="contained" onClick={handleSearch}>鎼滅储</Button>
+                    </Stack>
+                </Box>
+                <Box sx={{ mt: 2, height: 400, width: '100%' }}>
+                    <AsnWareModalTable
+                        tableData={tableData}
+                        setTableData={setTableData}
+                        dyFields={dyFields}
+                        page={page}
+                        rowCount={rowCount}
+                        setPage={setPage}
+                        isLoading={isLoading}
+                        setDyFields={setDyFields}
+                        selectedRows={selectedRows}
+                        setSelectedRows={setSelectedRows}
+                    />
+                </Box>
+            </DialogContent>
+            <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                <Box sx={{ width: '100%', display: 'flex', justifyContent: 'space-between' }}>
+                    <Button onClick={handleSubmit} variant="contained" startIcon={<SaveIcon />}>
+                        {translate('toolbar.confirm')}
+                    </Button>
+                </Box>
+            </DialogActions>
+        </Dialog>
+    );
+};
+
+export default MatnrInfoModal;
+
+const AsnWareModalTable = ({ tableData, setTableData, page, isLoading, pageSize, setPage, rowCount, selectedRows, setSelectedRows, dyFields, setDyFields }) => {
+    const translate = useTranslate();
+    const notify = useNotify();
+
+    const [columns, setColumns] = useState([
+        // { field: 'id', headerName: 'ID', width: 100 },
+        { field: 'name', headerName: translate('table.field.matnr.name'), width: 300 },
+        { field: 'code', headerName: translate('table.field.matnr.code'), width: 200 },
+        { field: 'groupId$', headerName: translate('table.field.matnr.groupId'), width: 100 },
+        { field: 'spec', headerName: translate('table.field.matnr.spec'), width: 100 },
+        { field: 'model', headerName: translate('table.field.matnr.model'), width: 100 },
+        { field: 'weight', headerName: translate('table.field.matnr.weight'), width: 100 },
+        { field: 'describle', headerName: translate('table.field.matnr.describle'), width: 100 },
+        { field: 'nromNum', headerName: translate('table.field.matnr.nromNum'), width: 100 },
+        { field: 'unit', headerName: translate('table.field.matnr.unit'), width: 100 },
+        { field: 'purchaseUnit', headerName: translate('table.field.matnr.purUnit'), width: 100 },
+        { field: 'stockUnit', headerName: translate('table.field.matnr.stockUnit'), width: 100 },
+        { field: 'stockLeval$', headerName: translate('table.field.matnr.stockLevel'), width: 100, sortable: false },
+    ])
+
+
+    const handleSelectionChange = (ids) => {
+        setSelectedRows(ids)
+    };
+
+    useEffect(() => {
+        getDynamicFields();
+    }, []);
+
+    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,
+                headerName: el.fieldsAlise,
+                minWidth: 100,
+                flex: 1,
+                editable: el.unique,
+                valueGetter: (value, row) => {
+                    return row.extendFields?.[el.fields] || '';
+                },
+            }))
+            setDyFields(data)
+            setColumns([...columns, ...cols])
+        } else {
+            notify(msg);
+        }
+    }
+
+    return (
+        <div style={{ height: 400, width: '100%' }}>
+            <DataGrid
+                size="small"
+                rows={tableData}
+                columns={columns}
+                checkboxSelection
+                onRowSelectionModelChange={handleSelectionChange}
+                selectionModel={selectedRows}
+                disableColumnMenu={true}
+                disableColumnSorting
+                disableMultipleColumnsSorting
+                rowCount={rowCount}
+                paginationMode="server"
+                paginationModel={page}
+                onPaginationModelChange={setPage}
+                loading={isLoading}
+                slotProps={{
+                    loadingOverlay: {
+                        variant: 'linear-progress',
+                        noRowsVariant: 'linear-progress',
+                    },
+                }}
+            />
+        </div>
+    );
+};
\ No newline at end of file
diff --git a/rsf-admin/src/page/orders/preparation/PreparationOrderCreate.jsx b/rsf-admin/src/page/orders/preparation/PreparationOrderCreate.jsx
new file mode 100644
index 0000000..ed3d15d
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/PreparationOrderCreate.jsx
@@ -0,0 +1,221 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import {
+    CreateBase,
+    useTranslate,
+    TextInput,
+    NumberInput,
+    BooleanInput,
+    TextField,
+    WrapperField,
+    NumberField,
+    DateInput,
+    TopToolbar,
+    SelectColumnsButton,
+    DatagridConfigurable,
+    SaveButton,
+    SelectInput,
+    ReferenceInput,
+    ReferenceArrayInput,
+    AutocompleteInput,
+    Toolbar,
+    required,
+    useDataProvider,
+    useNotify,
+    Form,
+    useCreateController,
+    useGetList,
+    useList,
+    ListContextProvider,
+    useListContext,
+    Button,
+    useRecordContext,
+} from 'react-admin';
+import {
+    Dialog,
+    DialogActions,
+    DialogContent,
+    DialogTitle,
+    Stack,
+    Grid,
+    Box,
+} from '@mui/material';
+import DialogCloseButton from "../../components/DialogCloseButton";
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
+import { styled } from '@mui/material/styles';
+import StatusSelectInput from "../../components/StatusSelectInput";
+import OutOrderItemList from "./PreparationOrderItemList";
+import MemoInput from "../../components/MemoInput";
+import AddIcon from '@mui/icons-material/Add';
+import SelectMatnrModal from "./PreparationSelectMatnrModal";
+
+const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
+    '& .css-1vooibu-MuiSvgIcon-root': {
+        height: '.9em'
+    },
+    '& .RaDatagrid-row': {
+        cursor: 'auto'
+    },
+    '& .column-name': {
+    },
+    '& .opt': {
+        width: 200
+    },
+}));
+
+
+const OutOrderCreate = (props) => {
+    const { open, setOpen, record } = props;
+    const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_order_type')) || [];
+    const business = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_business_type')) || [];
+    const translate = useTranslate();
+    const notify = useNotify();
+    const [drawerVal, setDrawerVal] = useState(false);
+    const [matCreate, setMatCreate] = useState(false);
+
+    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 } });
+    };
+
+    const { data, total, isPending, error, refetch, meta } = useGetList('/wave/locs/preview', { filter: { waveId: record?.id } });
+    const listContext = useList({ data, isPending });
+
+    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="xl"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
+                >
+                    <Form
+                        resource="outStock"
+                    >
+                        <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>
+                                    <Grid item xs={12} display="flex" gap={1}>
+                                        <TextInput
+                                            label="table.field.asnOrder.poCode"
+                                            source="poCode"
+                                            parse={v => v}
+                                        />
+                                        <NumberInput
+                                            label="table.field.asnOrder.poId"
+                                            source="poId"
+                                        />
+                                        {/* <AutocompleteInput
+                                        choices={dicts}
+                                        optionText="label"
+                                        label="table.field.asnOrder.type"
+                                        source="type"
+                                        optionValue="value"
+                                        validate={required()}
+                                        parse={v => v}
+                                    /> */}
+                                        <AutocompleteInput
+                                            choices={business}
+                                            optionText="label"
+                                            label="table.field.asnOrder.wkType"
+                                            source="wkType"
+                                            optionValue="value"
+                                            validate={required()}
+                                            parse={v => v}
+                                        />
+                                        <NumberInput
+                                            label="table.field.asnOrder.anfme"
+                                            source="anfme"
+                                            validate={required()}
+                                        />
+
+                                    </Grid>
+                                    <Grid item xs={12} display="flex" gap={1}>
+                                        <NumberInput
+                                            label="table.field.asnOrder.qty"
+                                            source="qty"
+                                        />
+                                        <TextInput
+                                            label="table.field.asnOrder.logisNo"
+                                            source="logisNo"
+                                            parse={v => v}
+                                        />
+                                        <DateInput
+                                            label="table.field.asnOrder.arrTime"
+                                            source="arrTime"
+                                        />
+                                        <SelectInput
+                                            label="table.field.asnOrder.rleStatus"
+                                            source="rleStatus"
+                                            choices={[
+                                                { id: 0, name: ' 姝e父' },
+                                                { id: 1, name: ' 宸查噴鏀�' },
+                                            ]}
+                                        />
+                                    </Grid>
+                                </Grid>
+                                <SelectMatnrModal open={matCreate} setOpen={setMatCreate} />
+                                <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                                    <Toolbar sx={{ width: '100%', justifyContent: 'end' }}  >
+                                        <AddOutOrderButton setMatCreate={setMatCreate} />
+                                        <SaveButton />
+                                    </Toolbar>
+                                </DialogActions>
+                                <Box>
+
+                                </Box>
+                            </>
+                        </DialogContent>
+                    </Form>
+                </Dialog>
+            </CreateBase>
+        </>
+    )
+}
+
+export default OutOrderCreate;
+
+
+const AddOutOrderButton = (setMatCreate) => {
+    const record = useRecordContext();
+    const addMats = (event) => {
+        event.stopPropagation();
+        setMatCreate(true)
+    }
+
+    return (
+        <Button label={"common.action.newAddMats"} onClick={addMats} variant="contained" sx={{ padding: '0.6em', marginRight: '1em' }}>
+            <AddIcon />
+        </Button>
+    );
+}
diff --git a/rsf-admin/src/page/orders/preparation/PreparationOrderEdit.jsx b/rsf-admin/src/page/orders/preparation/PreparationOrderEdit.jsx
new file mode 100644
index 0000000..cfbb930
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/PreparationOrderEdit.jsx
@@ -0,0 +1,182 @@
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import {
+    Edit,
+    SimpleForm,
+    useTranslate,
+    TextInput,
+    DateInput,
+    SelectInput,
+    AutocompleteInput,
+    SaveButton,
+    Toolbar,
+    required,
+    DeleteButton,
+} from 'react-admin';
+import { useWatch, useFormContext } from "react-hook-form";
+import { Stack, Grid, Box, Typography } from '@mui/material';
+import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
+import EditBaseAside from "../../components/EditBaseAside";
+import CustomerTopToolBar from "../../components/EditTopToolBar";
+import OutOrderItemList from "./PreparationOrderItemList";
+
+
+const PreparationOrderEdit = () => {
+    const translate = useTranslate();
+    const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_order_type')) || [];
+    const business = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_business_type')) || [];
+
+    return (
+        <>
+            <Edit
+                redirect="list"
+                mutationMode={EDIT_MODE}
+                actions={<CustomerTopToolBar />}
+                aside={<EditBaseAside />}
+            >
+                <SimpleForm
+                    shouldUnregister
+                    warnWhenUnsavedChanges
+                    toolbar={false}
+                    mode="onTouched"
+                    defaultValues={{}}
+                >
+                    <Grid container width={{ xs: '100%', xl: '100%' }} rowSpacing={3} columnSpacing={3}
+                        sx={{
+                            "& .MuiFormLabel-root.MuiInputLabel-root.Mui-disabled": {
+                                bgcolor: 'white',
+                                WebkitTextFillColor: "rgba(0, 0, 0)"
+                            },
+
+                            "& .MuiInputBase-input.MuiFilledInput-input.Mui-disabled": {
+                                bgcolor: 'white',
+                                WebkitTextFillColor: "rgba(0, 0, 0)"
+                            },
+                            "& .MuiFilledInput-root.MuiInputBase-sizeSmall": {
+                                bgcolor: 'white',
+                            }
+                        }}
+                    >
+                        <Grid item xs={24} md={12} >
+                            <Typography variant="h6" gutterBottom>
+                                {translate('common.edit.title.main')}
+                            </Typography>
+                            <Stack direction='row' gap={2}>
+                                {/* <TextInput
+                                    label="table.field.outStock.code"
+                                    source="code"
+                                    readOnly
+                                    parse={v => v}
+                                /> */}
+                                <TextInput
+                                    label="table.field.outStock.poCode"
+                                    source="poCode"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                {/* <AutocompleteInput
+                                    choices={dicts}
+                                    optionText="label"
+                                    label="table.field.outStock.type"
+                                    source="type"
+                                    optionValue="value"
+                                    parse={v => v}
+                                    readOnly
+                                /> */}
+                                <TextInput
+                                    label="table.field.outStock.wkType"
+                                    source="wkType$"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                {/* <AutocompleteInput
+                                    choices={business}
+                                    optionText="label"
+                                    label="table.field.outStock.wkType"
+                                    source="wkType$"
+                                    optionValue="value"
+                                    parse={v => v}
+                                    readOnly
+                                /> */}
+                                <TextInput
+                                    label="table.field.outStock.saleDate"
+                                    source="businessTime"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                <TextInput
+                                    label="table.field.outStock.anfme"
+                                    source="anfme"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                <TextInput
+                                    label="table.field.outStock.qty"
+                                    source="qty"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                            </Stack>
+                            <Stack direction='row' gap={2}>
+                                {/* 閾跺骇鏂板 */}
+                                <TextInput
+                                    label="table.field.outStock.saleOrgName"
+                                    source="saleOrgName"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                <TextInput
+                                    label="table.field.outStock.saleUserName"
+                                    source="saleUserName"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                <TextInput
+                                    label="table.field.outStock.customerId"
+                                    source="customerId"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                <TextInput
+                                    label="table.field.outStock.customerName"
+                                    source="customerName"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                <TextInput
+                                    label="table.field.outStock.stockOrgName"
+                                    source="stockOrgName"
+                                    readOnly
+                                    parse={v => v}
+                                />
+                                {/* <TextInput
+                                    label="table.field.outStock.logisNo"
+                                    source="logisNo"
+                                    readOnly
+                                    parse={v => v}
+                                /> */}
+                                {/* <DateInput
+                                    label="table.field.outStock.arrTime"
+                                    source="arrTime"
+                                    readOnly
+                                /> */}
+                                {/* <SelectInput
+                                    label="table.field.outStock.rleStatus"
+                                    source="rleStatus"
+                                    readOnly
+                                    choices={[
+                                        { id: 0, name: ' 姝e父' },
+                                        { id: 1, name: ' 宸查噴鏀�' },
+                                    ]}
+                                    validate={required()}
+                                /> */}
+                            </Stack>
+                        </Grid>
+                    </Grid>
+                </SimpleForm>
+            </Edit >
+            <OutOrderItemList />
+        </>
+    )
+}
+
+export default PreparationOrderEdit;
diff --git a/rsf-admin/src/page/orders/preparation/PreparationOrderItemCreate.jsx b/rsf-admin/src/page/orders/preparation/PreparationOrderItemCreate.jsx
new file mode 100644
index 0000000..e478b47
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/PreparationOrderItemCreate.jsx
@@ -0,0 +1,198 @@
+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 OutOrderItemCreate = (props) => {
+    const { open, setOpen, record } = 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
+                resource="outStockItem"
+                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>
+                                <Grid item xs={6} display="flex" gap={2}>
+                                    <NumberInput
+                                        label="table.field.outStockItem.orderId"
+                                        source="orderId"
+                                        readOnly
+                                        hidden
+                                        defaultValue={record?.id}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.orderCode"
+                                        source="orderCode"
+                                        readOnly
+                                        defaultValue={record?.code}
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.poDetlId"
+                                        source="poDetlId"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.outStockItem.matnrId"
+                                        source="matnrId"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.maktx"
+                                        source="maktx"
+                                        parse={v => v}
+                                    />
+                                    <NumberInput
+                                        label="table.field.outStockItem.anfme"
+                                        source="anfme"
+                                        validate={required()}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.outStockItem.stockUnit"
+                                        source="stockUnit"
+                                        parse={v => v}
+                                    />
+                                    <NumberInput
+                                        label="table.field.outStockItem.purQty"
+                                        source="purQty"
+                                        validate={required()}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.purUnit"
+                                        source="purUnit"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <NumberInput
+                                        label="table.field.outStockItem.qty"
+                                        source="qty"
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.splrCode"
+                                        source="splrCode"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.splrName"
+                                        source="splrName"
+                                        parse={v => v}
+                                    />
+
+                                </Grid>
+                                <Grid item xs={6} display="flex" gap={1}>
+                                    <TextInput
+                                        label="table.field.outStockItem.qrcode"
+                                        source="qrcode"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.barcode"
+                                        source="barcode"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.packName"
+                                        source="packName"
+                                        parse={v => v}
+                                    />
+                                </Grid>
+
+                                <Grid item xs={4} display="flex" gap={1}>
+                                    <StatusSelectInput />
+                                </Grid>
+                                <Grid item xs={4} display="flex" gap={1}>
+                                    <Stack direction="column" spacing={1}>
+                                        <MemoInput />
+                                    </Stack>
+                                </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 OutOrderItemCreate;
diff --git a/rsf-admin/src/page/orders/preparation/PreparationOrderItemEdit.jsx b/rsf-admin/src/page/orders/preparation/PreparationOrderItemEdit.jsx
new file mode 100644
index 0000000..1ae87ac
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/PreparationOrderItemEdit.jsx
@@ -0,0 +1,195 @@
+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,
+    Form,
+    useRecordContext,
+    useGetOne,
+    DeleteButton,
+    EditBase,
+    ReferenceField,
+} from 'react-admin';
+import { useWatch, useFormContext } from "react-hook-form";
+import { Stack, Grid, Box, Typography, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material';
+import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
+import DialogCloseButton from "../../components/DialogCloseButton";
+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 OutOrderItemEdit = (props) => {
+    const { open, setOpen, record } = props;
+    const translate = useTranslate();
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+        }
+    };
+    const { data, isPending, } = useGetOne('outStockItem', { id: record?.id });
+    if (data == null || data == undefined) { return }
+
+    return (
+        <Dialog
+            open={open}
+            onClose={handleClose}
+            aria-labelledby="form-dialog-title"
+            fullWidth
+            disableRestoreFocus
+            maxWidth="md"
+        >
+            <DialogTitle id="form-dialog-title" sx={{
+                position: 'sticky',
+                top: 0,
+                backgroundColor: 'background.paper',
+                zIndex: 1000
+            }}
+            >
+                {translate('update.title')}
+                <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
+                    <DialogCloseButton onClose={handleClose} />
+                </Box>
+            </DialogTitle>
+            <DialogContent sx={{ mt: 2 }}>
+                <EditBase
+                    id={record?.id}
+                    resource="outStockItem"
+                    mutationMode={EDIT_MODE}
+                    actions={<CustomerTopToolBar />}
+                >
+                    <Form
+                        shouldUnregister
+                        warnWhenUnsavedChanges
+                        mode="onTouched"
+                        defaultValues={{}}
+                    >
+                        <Grid container width={{ xs: '100%', xl: '100%' }}>
+                            <Grid item xs={24} md={14}>
+                                <Stack direction='row' gap={2}>
+                                    <NumberInput
+                                        label="table.field.outStockItem.orderId"
+                                        source="orderId"
+                                        readOnly
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.orderCode"
+                                        source="orderCode"
+                                        readOnly
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.poDetlId"
+                                        source="poDetlId"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.poDetlCode"
+                                        source="poDetlCode"
+                                        parse={v => v}
+                                    />
+                                </Stack>
+                                <Stack direction='row' gap={2}>
+                                    <TextInput
+                                        label="table.field.outStockItem.matnrId"
+                                        source="matnrId"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.maktx"
+                                        source="maktx"
+                                        parse={v => v}
+                                    />
+                                    <NumberInput
+                                        label="table.field.outStockItem.anfme"
+                                        source="anfme"
+                                        validate={required()}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.stockUnit"
+                                        source="stockUnit"
+                                        parse={v => v}
+                                    />
+                                </Stack>
+                                <Stack direction='row' gap={2}>
+                                    <NumberInput
+                                        label="table.field.outStockItem.purQty"
+                                        source="purQty"
+                                        validate={required()}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.purUnit"
+                                        source="purUnit"
+                                        parse={v => v}
+                                    />
+                                    <NumberInput
+                                        label="table.field.outStockItem.qty"
+                                        source="qty"
+                                        readOnly
+                                    />
+                                    <ReferenceInput source="splrName" label="table.field.outStockItem.splrName" reference="companys" filter={{type: 'supplier'}}>
+                                        <AutocompleteInput optionText="name" label="table.field.outStockItem.splrName" />
+                                    </ReferenceInput>
+                                </Stack>
+                                <Stack direction='row' gap={2}>
+                                    <TextInput
+                                        label="table.field.outStockItem.qrcode"
+                                        source="qrcode"
+                                        parse={v => v}
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.barcode"
+                                        source="trackCode"
+                                        parse={v => v}
+                                        readOnly
+                                    />
+                                    <TextInput
+                                        label="table.field.outStockItem.packName"
+                                        source="packName"
+                                        parse={v => v}
+                                    />
+                                </Stack>
+                            </Grid>
+                        </Grid>
+                        <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                            <Toolbar sx={{ width: '100%', justifyContent: 'end' }}  >
+                                <SaveButton type="button" mutationOptions={{
+                                    onSuccess: () => {
+                                        setOpen(false)
+                                    }
+                                }} />
+                            </Toolbar>
+                        </DialogActions>
+                    </Form>
+                </EditBase >
+            </DialogContent>
+        </Dialog>
+    )
+}
+
+export default OutOrderItemEdit;
diff --git a/rsf-admin/src/page/orders/preparation/PreparationOrderItemList.jsx b/rsf-admin/src/page/orders/preparation/PreparationOrderItemList.jsx
new file mode 100644
index 0000000..72298b8
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/PreparationOrderItemList.jsx
@@ -0,0 +1,206 @@
+import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
+import { useNavigate, useLocation } 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,
+  AutocompleteInput,
+  DeleteButton,
+  Button,
+  useEditContext,
+  useGetRecordId,
+  useRefresh,
+  useGetOne
+} from 'react-admin';
+import { Box, Typography, Card, Stack, Dialog, DialogActions, DialogTitle, LinearProgress } from '@mui/material';
+import { styled } from '@mui/material/styles';
+import OutOrderItemCreate from "./PreparationOrderItemCreate";
+import PageDrawer from "../../components/PageDrawer";
+import request from '@/utils/request';
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE, DEFAULT_ITEM_PAGE_SIZE } from '@/config/setting';
+import OutOrderItemEdit from "./PreparationOrderItemEdit";
+import { fetchOutOrderItemColumns } from "../config/orderItemColumns";
+
+const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
+  '& .css-1vooibu-MuiSvgIcon-root': {
+    height: '.9em',
+  },
+  '& .RaDatagrid-row': {
+    cursor: 'auto'
+  },
+  '& .column-name': {
+  },
+  '& .opt': {
+    width: 200
+  },
+  '& .MuiTableCell-root': {
+    whiteSpace: 'nowrap',
+    overflow: 'visible',
+    textOverflow: 'unset'
+  }
+
+}));
+
+const filters = [
+  <SearchInput source="condition" alwaysOn />,
+  <NumberInput source="orderId" label="table.field.outStockItem.orderId" />,
+  <TextInput source="orderCode" label="table.field.outStockItem.orderCode" alwaysOn />,
+  <TextInput source="poDetlId" label="table.field.outStockItem.poDetlId" />,
+  <TextInput source="matnrId" label="table.field.outStockItem.matnrId" />,
+  <TextInput source="maktx" label="table.field.outStockItem.maktx" alwaysOn />,
+  <TextInput source="matnrCode" label="table.field.outStockItem.matnrCode" alwaysOn />,
+  <NumberInput source="anfme" label="table.field.outStockItem.anfme" />,
+  <TextInput source="stockUnit" label="table.field.outStockItem.stockUnit" />,
+  <NumberInput source="purQty" label="table.field.outStockItem.purQty" />,
+  <TextInput source="purUnit" label="table.field.outStockItem.purUnit" />,
+  <NumberInput source="qty" label="table.field.outStockItem.qty" />,
+  <TextInput source="splrCode" label="table.field.outStockItem.splrCode" />,
+  <TextInput source="splrName" label="table.field.outStockItem.splrName" />,
+  <TextInput source="qrcode" label="table.field.outStockItem.qrcode" />,
+  <TextInput source="trackCode" label="table.field.outStockItem.barcode" />,
+  <TextInput source="packName" label="table.field.outStockItem.packName" />,
+  <TextInput label="common.field.memo" source="memo" />,
+  <SelectInput
+    label="common.field.status"
+    source="status"
+    choices={[
+      { id: '1', name: 'common.enums.statusTrue' },
+      { id: '0', name: 'common.enums.statusFalse' },
+    ]}
+    resettable
+  />,
+]
+
+const OutOrderItemList = () => {
+  const translate = useTranslate();
+  const [createDialog, setCreateDialog] = useState(false);
+  const [editDialog, setEditDialog] = useState(false);
+  const [drawerVal, setDrawerVal] = useState(false);
+  const [select, setSelect] = useState({});
+  const asnId = useGetRecordId();
+  const { data: dicts, isPending, error } = useGetOne('outStock', { id: asnId });
+
+  return (
+    <>
+      <Box display="flex">
+        <List
+          resource="outStockItem"
+          storeKey='outStockItem'
+          sx={{
+            flexGrow: 1,
+            transition: (theme) =>
+              theme.transitions.create(['all'], {
+                duration: theme.transitions.duration.enteringScreen,
+              }),
+            marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
+          }}
+          title={"menu.outStockItem"}
+          empty={false}
+          filter={{ orderId: asnId, deleted: 0 }}
+          filters={filters}
+          sort={{ field: "create_time", order: "desc" }}
+          actions={(
+            <TopToolbar>
+              <FilterButton />
+              <SelectColumnsButton preferenceKey='outStockItem' />
+            </TopToolbar>
+          )}
+          perPage={DEFAULT_ITEM_PAGE_SIZE}
+        >
+          <DynamicFields />
+        </List>
+        <OutOrderItemCreate
+          open={createDialog}
+          setOpen={setCreateDialog}
+          record={dicts}
+        />
+        <OutOrderItemEdit
+          open={editDialog}
+          setOpen={setEditDialog}
+          record={select}
+        />
+        <PageDrawer
+          title={false}
+          drawerVal={drawerVal}
+          setDrawerVal={setDrawerVal}
+        >
+        </PageDrawer>
+      </Box>
+    </>
+  )
+}
+OutOrderItemList.Context = React.createContext()
+
+export default OutOrderItemList;
+
+
+const DynamicFields = (props) => {
+  const translate = useTranslate();
+  const notify = useNotify();
+  const [columns, setColumns] = useState([]);
+  const { isLoading } = useListContext();
+  const refresh = useRefresh();
+  useEffect(() => {
+    getDynamicFields();
+  }, []);
+
+  const getDynamicFields = async () => {
+    const result = await fetchOutOrderItemColumns();
+    if (result.success) {
+      setColumns(result.columns);
+    } else {
+      notify(result.msg);
+    }
+  }
+
+  return (
+    <Box sx={{ position: 'relative', minHeight: "60vh", }}>
+      {isLoading && (
+        <LinearProgress
+          sx={{
+            height: "2px",
+            position: 'absolute',
+            top: 0,
+            left: 0,
+            right: 0,
+          }}
+        />
+      )}
+      {columns.length > 0 &&
+        <StyledDatagrid
+          preferenceKey='outStockItem'
+          bulkActionButtons={false}
+          rowClick={(id, resource, record) => false}
+          omit={['id', 'orderId', 'orderCode', 'poDetlId', 'poDetlCode', 'platOrderCode', 'matnrId', 'spec', 'model', 'purQty', 'purUnit',
+            'qrcode', 'trackCode', 'splrCode', 'projectCode', 'workQty', 'createBy', 'createTime', 'createBy$', 'sourceWareHouseId', 'splrName', 'supplierName']}
+        >
+          {columns.map((column) => column)}
+        </StyledDatagrid>}
+    </Box>
+  )
+}
diff --git a/rsf-admin/src/page/orders/preparation/PreparationOrderList.jsx b/rsf-admin/src/page/orders/preparation/PreparationOrderList.jsx
new file mode 100644
index 0000000..8f3a8ff
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/PreparationOrderList.jsx
@@ -0,0 +1,386 @@
+import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
+import { useLocation, useNavigate } from 'react-router-dom';
+import {
+  List,
+  SearchInput,
+  TopToolbar,
+  Toolbar,
+  ColumnsButton,
+  FilterButton,
+  EditButton,
+  CreateButton,
+  ExportButton,
+  BulkDeleteButton,
+  useDataProvider,
+  WrapperField,
+  useRecordContext,
+  useTranslate,
+  useNotify,
+  useRefresh,
+  useListContext,
+  FunctionField,
+  TextField,
+  NumberField,
+  DateField,
+  BooleanField,
+  ReferenceField,
+  TextInput,
+  DateTimeInput,
+  DateInput,
+  SelectInput,
+  NumberInput,
+  ReferenceInput,
+  ReferenceArrayInput,
+  AutocompleteInput,
+  DeleteButton,
+  Button,
+  useRedirect,
+  useUnselectAll,
+  useRecordSelection,
+} from 'react-admin';
+import { styled } from '@mui/material/styles';
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
+import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined';
+import { Box, Typography, Card, Stack, Drawer, LinearProgress } from '@mui/material';
+import DictionarySelect from "../../components/DictionarySelect";
+import StickyDataTable from "../../components/StickyDataTable";
+import useTableLayout from '@/utils/useTableLayout';
+import BillStatusField from '../../components/BillStatusField';
+import MyCreateButton from "../../components/MyCreateButton";
+import PageEditDrawer from "../../components/PageEditDrawer";
+import ConfirmButton from '../../components/ConfirmButton';
+import ImportButton from "../../components/ImportButton";
+import DetailsIcon from '@mui/icons-material/Details';
+import OutStockWaveDialog from "./PreparationStockWaveDialog";
+import AddTaskIcon from '@mui/icons-material/AddTask';
+import PublicIcon from '@mui/icons-material/Public';
+import SelectMatnrModal from "./PreparationSelectMatnrModal";
+import EditIcon from '@mui/icons-material/Edit';
+import TaskIcon from '@mui/icons-material/Task';
+import OutOrderPreview from "./PreparationOrderPreview";
+import AddIcon from '@mui/icons-material/Add';
+import OutStockPublic from "./PreparationStockPublic";
+import OutOrderModal from "./PreparationOrderModal";
+import request from '@/utils/request';
+
+const ListContent = ({ drawerVal, setDrawerVal, setWaveRule, setSelectIds, setSelect }) => {
+  const { isLoading } = useListContext();
+  const { boxMaxWidth, boxMaxHeight } = useTableLayout(drawerVal);
+
+  const omittedFields = ['id', 'code', 'createTime', 'createBy$', 'memo', 'rleStatus$', 'logisNo', 'updateBy$', 'workQty', 'updateTime', 'type$'];
+
+  return (
+    <Box sx={{
+      position: 'relative',
+      maxHeight: boxMaxHeight,
+      maxWidth: boxMaxWidth,
+      overflowX: 'auto',
+      overflowY: 'auto',
+      '& .MuiTableCell-root': {
+        whiteSpace: 'nowrap',
+      },
+      '& .css-1vooibu-MuiSvgIcon-root': {
+        height: '.9em'
+      },
+      '& .RaDatagrid-row': {
+        cursor: 'auto'
+      },
+      '& .column-name': {
+      },
+      '& .opt': {
+        width: 420
+      },
+      '& .wkType': {
+        width: 110
+      },
+      '& .status': {
+        width: 90
+      },
+    }}>
+      {isLoading && (
+        <LinearProgress
+          sx={{
+            height: "2px",
+            position: 'absolute',
+            top: 0,
+            left: 0,
+            right: 0,
+          }}
+        />
+      )}
+      <StickyDataTable
+        storeKey='outStock'
+        bulkActionButtons={<PublicTaskButton setWaveRule={setWaveRule} setSelectIds={setSelectIds} />}
+        rowClick={false}
+        hiddenColumns={omittedFields}
+      >
+        <NumberField source="id" />
+        <TextField source="code" label="table.field.outStock.code" />
+        <TextField source="poCode" label="table.field.outStock.poCode" />
+        <TextField source="type$" label="table.field.outStock.type" />
+        <TextField cellClassName="wkType" source="wkType$" label="table.field.outStock.wkType" />
+        <NumberField source="anfme" label="table.field.outStock.anfme" />
+        <NumberField source="workQty" label="table.field.outStock.workQty" />
+        <NumberField source="qty" label="table.field.outStock.qty" />
+        <TextField source="logisNo" label="table.field.outStock.logisNo" />
+        <TextField source="rleStatus$" label="table.field.outStock.rleStatus" sortable={false} />
+        <TextField source="updateBy$" label="common.field.updateBy" />
+        <DateField source="updateTime" label="common.field.updateTime" showTime />
+        <TextField source="createBy$" label="common.field.createBy" />
+        <DateField source="createTime" label="common.field.createTime" showTime />
+        <TextField source="memo" label="common.field.memo" sortable={false} />
+
+        {/* 閾跺骇鏂板 */}
+        <TextField source="saleOrgName" label="閿�鍞粍缁�" />
+        <TextField source="saleUserName" label="閿�鍞憳" />
+        <DateField source="businessTime" label="鍑哄簱鏃ユ湡" />
+        <TextField source="customerId" label="瀹㈡埛缂栫爜" />
+        <TextField source="customerName" label="瀹㈡埛" />
+        <TextField source="stockOrgName" label="table.field.outStock.stockOrgName" />
+
+        <BillStatusField cellClassName="status" source="exceStatus" label="table.field.outStock.exceStatus" />
+        <WrapperField cellClassName="opt" label="common.field.opt" >
+          {/* <MyButton setCreateDialog={setManualDialog} setmodalType={setmodalType} /> */}
+          <EditButton label="toolbar.detail" icon={(<DetailsIcon />)}></EditButton>
+          <CancelButton />
+          <CompleteButton />
+          <PublicButton setDrawerVal={setDrawerVal} drawerVal={drawerVal} setSelect={setSelect} />
+        </WrapperField>
+      </StickyDataTable>
+    </Box>
+  )
+}
+
+const filters = [
+  <SearchInput source="condition" alwaysOn />,
+  <TextInput source="code" label="table.field.outStock.code" alwaysOn />,
+  <TextInput source="poCode" label="table.field.outStock.poCode" />,
+  <NumberInput source="poId" label="table.field.outStock.poId" />,
+
+  <NumberInput source="anfme" label="table.field.outStock.anfme" />,
+  <NumberInput source="qty" label="table.field.outStock.qty" />,
+  <TextInput source="logisNo" label="table.field.outStock.logisNo" />,
+  <DateInput source="arrTime" label="table.field.outStock.arrTime" />,
+  <SelectInput source="rleStatus" label="table.field.outStock.rleStatus"
+    choices={[
+      { id: 0, name: ' 姝e父' },
+      { id: 1, name: ' 宸查噴鏀�' },
+    ]}
+  />,
+
+  <TextInput label="common.field.memo" source="memo" />,
+  <DictionarySelect
+    label='table.field.outStock.exceStatus'
+    name="exceStatus"
+    dictTypeCode="sys_asn_exce_status"
+    alwaysOn
+  />,
+]
+
+const PreparationOrderList = (props) => {
+
+  const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_business_type')) || [];
+  const [createDialog, setCreateDialog] = useState(false);
+  const [manualDialog, setManualDialog] = useState(false);
+  const [drawerVal, setDrawerVal] = useState(false);
+  const [waveRule, setWaveRule] = useState(false);
+  const [selectIds, setSelectIds] = useState([]);
+  const [preview, setPreview] = useState(false);
+  const [modalType, setmodalType] = useState(0);
+  const [select, setSelect] = useState(0);
+  const translate = useTranslate();
+  const refresh = useRefresh();
+  const notify = useNotify();
+  const billReload = useRef();
+
+  //鑾峰彇娉㈡瑙勫垯
+  const closeDialog = async (value) => {
+    setWaveRule(false)
+    const res = await request.post(`/outStock/generate/wave`, { ids: selectIds, waveRuleId: value.id });
+    if (res?.data?.code === 200) {
+      notify(res.data.msg);
+    } else {
+      notify(res.data.msg);
+    }
+    refresh()
+  }
+
+  return (
+    <Box display="flex">
+      <List
+        resource="preparation"
+        sx={{
+          flexGrow: 1,
+          transition: (theme) =>
+            theme.transitions.create(['all'], {
+              duration: theme.transitions.duration.enteringScreen,
+            }),
+        }}
+        title={"menu.outStock"}
+        empty={false}
+        filters={filters}
+        filter={{ deleted: 0, type: 'out', wkType: 23 }}
+        sort={{ field: "create_time", order: "desc" }}
+        actions={(
+          <TopToolbar>
+            <FilterButton />
+            <CreateByOrderButton setCreateDialog={setCreateDialog} />
+            <MyCreateButton onClick={() => { setManualDialog(true); setmodalType(0) }} />
+            <ColumnsButton storeKey='outStock' />
+            <ImportButton value={'outStockItem'} />
+          </TopToolbar>
+        )}
+        perPage={DEFAULT_PAGE_SIZE}
+      >
+        <ListContent
+          drawerVal={drawerVal}
+          setDrawerVal={setDrawerVal}
+          setWaveRule={setWaveRule}
+          setSelectIds={setSelectIds}
+          setSelect={setSelect}
+        />
+      </List>
+      {/* <OutOrderCreate
+        open={manualDialog}
+        setOpen={setManualDialog}
+      /> */}
+      <SelectMatnrModal
+        asnId={modalType}
+        billReload={billReload}
+        open={manualDialog}
+        setOpen={setManualDialog}
+      />
+      <OutOrderModal
+        open={createDialog}
+        setOpen={setCreateDialog}
+        preview={preview}
+        setPreview={setPreview}
+      />
+      <OutStockWaveDialog open={waveRule} setOpen={setWaveRule} onClose={closeDialog} />
+      <OutOrderPreview open={preview} setOpen={setPreview} />
+      <PageEditDrawer
+        title={"toolbar.publicWorking"}
+        drawerVal={drawerVal}
+        setDrawerVal={setDrawerVal}
+      >
+        <OutStockPublic record={select} open={drawerVal} setOpen={setDrawerVal} />
+      </PageEditDrawer>
+    </Box >
+  )
+}
+export default PreparationOrderList;
+
+
+const PublicTaskButton = ({ setWaveRule, setSelectIds }) => {
+  const record = useRecordContext();
+  const { selectedIds, onUnselectItems } = useListContext();
+  const notify = useNotify();
+  const redirect = useRedirect();
+
+  const pubClick = async () => {
+    onUnselectItems();
+    setWaveRule(true);
+    setSelectIds(selectedIds)
+  }
+
+  return (
+    <Button
+      onClick={pubClick}
+      label={"toolbar.createWave"}
+      startIcon={<PublicIcon />}
+    />
+  );
+}
+
+//瀹屾垚鍗曟嵁
+const CompleteButton = () => {
+  const record = useRecordContext();
+  const notify = useNotify();
+  const refresh = useRefresh();
+  const requestComplete = async () => {
+    const { data: { code, data, msg } } = await request.get(`/outStock/complete/${record.id}`);
+    if (code === 200) {
+      notify(msg);
+      refresh()
+    } else {
+      notify(msg);
+    }
+  }
+
+  return (
+    // record.exceStatus === 1 && (record.anfme === record.qty ? <Button onClick={requestComplete} label={"toolbar.complete"} color="secondary">
+    //   <TaskIcon />
+    // </Button> : )
+    record.exceStatus != 15 ? <ConfirmButton label={"toolbar.complete"} color="secondary" startIcon={<TaskIcon />} onConfirm={requestComplete} /> : <></>
+  )
+}
+
+const MyButton = ({ setCreateDialog, setmodalType }) => {
+  const record = useRecordContext();
+  const handleEditClick = (btn) => {
+    btn.stopPropagation();
+    const id = record.id;
+    setmodalType(id);
+    setCreateDialog(true);
+  };
+  return (
+    <Button
+      color="primary"
+      startIcon={<EditIcon />}
+      onClick={(btn) => handleEditClick(btn)}
+      sx={{ ml: 1 }}
+      label={'ra.action.edit'}
+    >
+    </Button>
+  )
+}
+
+const CreateByOrderButton = ({ setCreateDialog }) => {
+  const record = useRecordContext();
+  const notify = useNotify();
+  const refresh = useRefresh();
+  const createByOrder = async (event) => {
+    event.stopPropagation();
+    setCreateDialog(true);
+  }
+
+  return (
+    <Button onClick={createByOrder} label={'toolbar.asnCreate'}> <AddIcon /> </Button>
+  )
+}
+
+const CancelButton = () => {
+  const record = useRecordContext();
+  const notify = useNotify();
+  const refresh = useRefresh();
+  const cancelOrder = async () => {
+    const { data: { code, data, msg } } = await request.get(`/outStock/cancel/${record?.id}`);
+    if (code === 200) {
+      notify(msg);
+      refresh()
+    } else {
+      notify(msg);
+    }
+  }
+
+  return (
+    record?.exceStatus == 10 ? <ConfirmButton label={"toolbar.cancel"} startIcon={<CancelOutlinedIcon />} onConfirm={cancelOrder} size={"small"} /> : <></>
+  )
+}
+
+//涓嬪彂鎵ц
+const PublicButton = ({ setDrawerVal, setSelect }) => {
+  const record = useRecordContext();
+  const refresh = useRefresh();
+  const taskEvent = () => {
+    setDrawerVal(true)
+    setSelect(record)
+    refresh();
+  }
+
+  return (
+    record.workQty < record.anfme ? <Button label={"toolbar.publicWorking"} startIcon={<AddTaskIcon />} onClick={taskEvent} size={"small"} /> : <></>
+  )
+}
+
+
diff --git a/rsf-admin/src/page/orders/preparation/PreparationOrderModal.jsx b/rsf-admin/src/page/orders/preparation/PreparationOrderModal.jsx
new file mode 100644
index 0000000..6e43095
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/PreparationOrderModal.jsx
@@ -0,0 +1,282 @@
+import { Dialog, DialogActions, DialogContent, DialogTitle, Box, LinearProgress } from "@mui/material";
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import {
+    List,
+    DatagridConfigurable,
+    SearchInput,
+    TopToolbar,
+    Toolbar,
+    Button,
+    SelectColumnsButton,
+    EditButton,
+    FilterButton,
+    CreateButton,
+    ExportButton,
+    BulkDeleteButton,
+    WrapperField,
+    useRecordContext,
+    useTranslate,
+    useNotify,
+    useListContext,
+    FunctionField,
+    TextField,
+    NumberField,
+    DateField,
+    BooleanField,
+    ReferenceField,
+    TextInput,
+    DateTimeInput,
+    DateInput,
+    SelectInput,
+    NumberInput,
+    ReferenceInput,
+    ReferenceArrayInput,
+    AutocompleteInput,
+    DeleteButton,
+    SimpleForm,
+    Form,
+    SaveButton,
+    useRefresh,
+    useGetList,
+} from 'react-admin';
+
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
+import DialogCloseButton from "../../components/DialogCloseButton";
+import EditableTextField from "../../components/EditableTextField";
+import CheckCircleIcon from '@mui/icons-material/CheckCircle';
+import { Grid, margin, Stack, width } from "@mui/system";
+import SaveIcon from '@mui/icons-material/Save';
+import OutOrderPreview from "./PreparationOrderPreview";
+import { styled } from '@mui/material/styles';
+import request from '@/utils/request';
+
+
+const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
+    '& .css-1vooibu-MuiSvgIcon-root': {
+        height: '.9em'
+    },
+    '& .RaDatagrid-row': {
+        cursor: 'auto'
+    },
+    '& .status': {
+        width: 90
+    },
+}));
+
+const StyledForm = styled(Form)(({ theme }) => ({
+    width: '100%',
+    marginBottom: '45px',
+
+    '& .MuiGrid-root': {
+        margin: '0 10px'
+    }
+}));
+
+
+const filters = [
+    <SearchInput source="condition" alwaysOn />,
+    <DateInput label='common.time.after' source="timeStart" />,
+    <DateInput label='common.time.before' source="timeEnd" />,
+    <TextInput source="code" label="table.field.delivery.code" />,
+    <TextInput source="platId" label="table.field.delivery.platId" />,
+    <TextInput source="type" label="table.field.delivery.type" />,
+    <TextInput source="wkType" label="table.field.delivery.wkType" />,
+    <TextInput source="source" label="table.field.delivery.source" />,
+    <SelectInput
+        label="common.field.status"
+        source="status"
+        choices={[
+            { id: '1', name: 'common.enums.statusTrue' },
+            { id: '0', name: 'common.enums.statusFalse' },
+        ]}
+        resettable
+    />,
+]
+
+const OutOrderModal = (props) => {
+    const { open, setOpen, preview, setPreview, record } = props;
+    const [drawerVal, setDrawerVal] = useState(false);
+    const [params, setParams] = useState({});
+    const [select, setSelect] = useState([]);
+    const translate = useTranslate();
+    const refresh = useRefresh();
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+        }
+    };
+
+    const CustomFilter = () => {
+        const { filterValues, setFilters, refetch } = useListContext();
+        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 (
+            <StyledForm>
+                <Grid container rowSpacing={3} columnSpacing={3} >
+                    <Stack>
+                        <TextInput
+                            source="condition"
+                            label="common.action.search"
+                            resettable
+                            defaultValue={params?.condition}
+                            onChange={handleChange} />
+                    </Stack>
+                    <Stack>
+                        <TextInput
+                            source="deliveryCode"
+                            label="table.field.deliveryItem.deliveryCode"
+                            defaultValue={params?.deliveryCode}
+                            onChange={handleChange}
+                            resettable
+                        />
+                    </Stack>
+                    <Stack>
+                        <TextInput
+                            source="maktx"
+                            label="table.field.deliveryItem.matnrName"
+                            defaultValue={params?.maktx}
+                            onChange={handleChange}
+                            resettable
+                        />
+                    </Stack>
+                    <Stack>
+                        <TextInput
+                            source="matnrCode"
+                            label="table.field.deliveryItem.matnrCode"
+                            defaultValue={params?.matnrCode}
+                            resettable
+                            onChange={handleChange} />
+                    </Stack>
+                    <Stack>
+                        <TextInput
+                            source="splrName"
+                            label="table.field.deliveryItem.splrName"
+                            defaultValue={params?.splrName}
+                            resettable
+                            onChange={handleChange} />
+                    </Stack>
+                </Grid>
+                <DialogActions>
+                    <Toolbar sx={{ width: '100%', justifyContent: 'end' }}  >
+                        <SaveButton onClick={handleSubmit} label={"toolbar.query"} />
+                    </Toolbar>
+                </DialogActions>
+            </StyledForm>
+        );
+    };
+    return (
+        <Dialog
+            open={open}
+            onClose={handleClose}
+            aria-labelledby="form-dialog-title"
+            aria-hidden
+            fullWidth
+            disableRestoreFocus
+            maxWidth="xl"
+        >
+            <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>
+            <Grid container rowSpacing={2} columnSpacing={2}>
+                <DialogContent>
+                    <Grid item>
+                        <List
+                            resource="/deliveryItem/filters"
+                            sx={{
+                                flexGrow: 1,
+                                transition: (theme) =>
+                                    theme.transitions.create(['all'], {
+                                        duration: theme.transitions.duration.enteringScreen,
+                                    }),
+                                marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
+                            }}
+                            filters={<CustomFilter />}
+                            queryOptions={{ meta: { ...params } }}
+                            empty={false}
+                            sort={{ field: "create_time", order: "desc" }}
+                            actions={(
+                                <TopToolbar>
+                                    <></>
+                                </TopToolbar>
+                            )}
+                            perPage={DEFAULT_PAGE_SIZE}
+                        >
+                            <Box sx={{ position: 'relative', minHeight: "60vh", }}>
+                                <LinearProgress
+                                    sx={{ height: "2px", position: 'absolute', top: 0, left: 0, right: 0, }}
+                                />
+                                <StyledDatagrid
+                                    preferenceKey='deliveryItem'
+                                    bulkActionButtons={<AddOutStockButton setOpen={setOpen} setPreview={setPreview} setSelect={setSelect} />}
+                                    rowClick={(id, resource, record) => false}
+                                    expand={false}
+                                    expandSingle={true}
+                                    omit={['id', 'createTime', 'createBy', 'memo', 'startTime', 'endTime', 'updateBy', 'createTime']}
+                                >
+                                    <NumberField source="id" />
+                                    <TextField source="deliveryCode" label="table.field.deliveryItem.deliveryCode" />
+                                    <TextField source="matnrCode" label="table.field.deliveryItem.matnrCode" />
+                                    <TextField source="maktx" label="table.field.deliveryItem.matnrName" />
+                                    <TextField source="unit" label="table.field.deliveryItem.unit" />
+                                    <NumberField source="anfme" label="table.field.deliveryItem.anfme" />
+                                    <NumberField source="workQty" label="table.field.outStockItem.workQty" />
+                                    <TextField source="splrName" label="table.field.deliveryItem.splrName" />
+                                    <TextField source="splrBatch" label="table.field.deliveryItem.splrBatch" />
+                                    <TextField source="updateBy$" label="common.field.updateBy" />
+                                    <DateField source="updateTime" label="common.field.updateTime" showTime />
+                                    <TextField source="createBy$" label="common.field.createBy" />
+                                    <DateField source="createTime" label="common.field.createTime" showTime />
+                                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
+                                    <TextField source="memo" label="common.field.memo" sortable={false} />
+                                </StyledDatagrid>
+                            </Box>
+                        </List>
+                    </Grid>
+                </DialogContent>
+            </Grid>
+            <Grid>
+                <OutOrderPreview open={preview} setOpen={setPreview} selectedIds={select} setCloseParent={setOpen} />
+            </Grid>
+        </Dialog >
+    )
+}
+
+export default OutOrderModal;
+
+const AddOutStockButton = (props) => {
+    const { setOpen, setPreview, setSelect } = props;
+    const { selectedIds, onUnselectItems } = useListContext();
+    const notify = useNotify();
+    const refresh = useRefresh();
+    const confirm = async (event) => {
+        setPreview(true)
+        setSelect(selectedIds);
+        onUnselectItems();
+        // refresh();
+    }
+
+    return (
+        <Button label={"toolbar.confirmSelect"} onClick={confirm}>
+            <CheckCircleIcon />
+        </Button>
+    )
+}
\ No newline at end of file
diff --git a/rsf-admin/src/page/orders/preparation/PreparationOrderPreview.jsx b/rsf-admin/src/page/orders/preparation/PreparationOrderPreview.jsx
new file mode 100644
index 0000000..6acbd99
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/PreparationOrderPreview.jsx
@@ -0,0 +1,149 @@
+import { Dialog, DialogActions, DialogContent, DialogTitle, Box, LinearProgress, Grid, } from "@mui/material";
+import React, { useState, useRef, useEffect, useMemo, memo } from "react";
+import {
+    Toolbar,
+    Button,
+    useTranslate,
+    useNotify,
+    useRefresh,
+    useGetList,
+} from 'react-admin';
+import request from '@/utils/request';
+import { styled } from '@mui/material/styles';
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
+import { DataGrid, useGridApiContext, GridActionsCellItem, useGridApiRef } from '@mui/x-data-grid';
+import DialogCloseButton from "../../components/DialogCloseButton";
+
+
+const OutOrderPreview = (props) => {
+    const { open, setOpen, record, selectedIds, setCloseParent } = props;
+    const translate = useTranslate();
+    const gridRef = useGridApiRef();
+    const [rows, setRows] = useState([]);
+    const notify = useNotify();
+    const refresh = useRefresh();
+    const handleClose = async (event, reason) => {
+        if (reason !== "backdropClick") {
+            // const res = await request.get(`/outStock/items/cancel/` + selectedIds);
+            setOpen(false);
+            setCloseParent(false)
+        }
+    };
+
+    if (!selectedIds) { return }
+
+    const { data, isLoading, error } = useGetList('/deliveryItem/edit', {
+        pagination: { page: 1, perPage: 1000 },
+        filter: { deleted: 0, ids: selectedIds }
+    });
+
+    return (
+        <Dialog
+            open={open}
+            onClose={handleClose}
+            aria-labelledby="form-dialog-title"
+            aria-hidden
+            fullWidth
+            maxWidth="lg"
+        >
+            <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>
+                <Grid container xl={12}>
+                    <Grid item xl={12}>
+                        <Box display="flex" sx={{ height: 400, width: '100%', '& .RaConfigurable-root': { width: '100%' } }}>
+                            <LinearProgress sx={{ height: "2px", position: 'absolute', top: 0, left: 0, right: 0, }} />
+                            <OrderPreview rows={data} gridRef={gridRef} />
+                        </Box >
+                    </Grid>
+                </Grid>
+                <Toolbar sx={{ justifyContent: 'end' }}>
+                    <ConfirmButton label="toolbar.confirm" variant="contained" size="large" gridRef={gridRef} setOpen={setOpen} setCloseParent={setCloseParent} />
+                </Toolbar>
+            </DialogContent>
+        </Dialog>
+    )
+}
+
+export default OutOrderPreview;
+
+const ConfirmButton = ({ gridRef, setOpen, setCloseParent }) => {
+    const refresh = useRefresh();
+    const notify = useNotify();
+    const confirm = async () => {
+        const items = gridRef.current?.getSortedRows();
+        const { data: { code, msg } } = await request.post(`/outStock/generate/orders`, { ids: items });
+        if (code === 200) {
+            notify(msg);
+            refresh()
+            setOpen(false)
+            setCloseParent(false)
+        } else {
+            notify(msg);
+        }
+    }
+
+    return (
+        <Button label="toolbar.confirm" variant="contained" size="large" onClick={confirm} />
+    )
+}
+
+const OrderPreview = ({ rows, gridRef }) => {
+    gridRef.current = useGridApiRef();
+
+    const columns = [
+        { field: 'matnrCode', headerName: '鐗╂枡缂栫爜', width: 110 },
+        { field: 'maktx', headerName: '鐗╂枡鍚嶇О', width: 190 },
+        {
+            field: 'anfme', headerName: '鍑哄簱鏁伴噺', width: 110, type: 'number', editable: true,
+            valueGetter: (value, row) => {
+                return row.anfme - row.workQty - row.qty;
+            },
+            preProcessEditCellProps: (params) => {
+                const hasError = !params.props.value || params.props.value.trim() === '';
+                return {
+                    ...params.props,
+                    error: hasError,
+                    message: hasError ? '璁″垝鏁伴噺涓嶈兘涓虹┖锛侊紒' : '',
+                };
+            },
+        },
+        {
+            field: 'workQty', headerName: '鍓╀綑鏁伴噺', width: 110, type: 'number',
+            valueGetter: (value, row) => {
+                return row.anfme - row.workQty - row.qty;
+            },
+        },
+        { field: 'unit', headerName: '鍗曚綅', width: 110 },
+        { field: 'splrBatch', headerName: '鎵规', width: 110 },
+        { field: 'splrName', headerName: '渚涘簲鍟�', width: 110 },
+        { field: 'updateTime', headerName: '鏇存柊鏃堕棿', width: 110 },
+        { field: 'updateBy$', headerName: '鏇存柊浜哄憳', width: 110 },
+    ]
+
+    return (
+        <DataGrid
+            storeKey={"outOrderItemPreview"}
+            rows={rows}
+            columns={columns}
+            apiRef={gridRef}
+            disableRowSelectionOnClick
+            hideFooterPagination={true}  // 闅愯棌鍒嗛〉鎺т欢
+            hideFooter={true}
+            onRowSelectionModelChange={(ids) => {
+                setSelectedIds(ids)
+            }}
+        />
+    )
+}
+
+
diff --git a/rsf-admin/src/page/orders/preparation/PreparationSelectMatnrModal.jsx b/rsf-admin/src/page/orders/preparation/PreparationSelectMatnrModal.jsx
new file mode 100644
index 0000000..c15d5dc
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/PreparationSelectMatnrModal.jsx
@@ -0,0 +1,587 @@
+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 { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form";
+import DialogCloseButton from "../../components/DialogCloseButton";
+import StatusSelectInput from "../../components/StatusSelectInput";
+import DictionarySelect from "../../components/DictionarySelect";
+import { DataGrid, useGridApiRef } from '@mui/x-data-grid';
+import ConfirmButton from "../../components/ConfirmButton";
+import { Add, Edit, Delete } from '@mui/icons-material';
+import DictSelect from "../../components/DictSelect";
+import SaveIcon from '@mui/icons-material/Save';
+import MatnrInfoModal from "./PreparationMatnrInfoModal";
+import request from '@/utils/request';
+import "./preparation.css";
+
+const SelectMatnrModal = (props) => {
+    const { open, setOpen, asnId, billReload } = 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 && asnId !== 0) {
+            requestGetHead()
+            requestGetBody()
+        }
+        setDisabled(false)
+    }, [open])
+
+    const handleClose = (event, reason) => {
+        if (reason !== "backdropClick") {
+            setOpen(false);
+            refresh();
+            setFormData({ type: '', wkType: '' })
+            setTableData([])
+        }
+    };
+
+    const [formData, setFormData] = useState({
+        type: '',
+        wkType: '',
+        poCode: '',
+        logisNo: '',
+        arrTime: ''
+    });
+
+    const [tabelData, setTableData] = useState([]);
+
+    const handleChange = (value, name) => {
+        setFormData((prevData) => ({
+            ...prevData,
+            [name]: value
+        }));
+    };
+
+    const resetData = () => {
+        setFormData({
+            type: '',
+            wkType: '',
+            poCode: '',
+            logisNo: '',
+            arrTime: ''
+        })
+        setTableData([])
+    }
+
+    const setFinally = () => {
+        const rows = tableRef.current.state.editRows;
+        for (const key in rows) {
+            const find = tabelData.find(item => item.matnrId === +key);
+            find.anfme = rows[key].anfme.value;
+        }
+        setTableData([...tabelData]);
+    }
+
+    const handleSubmit = async () => {
+        setFinally()
+        setDisabled(true)
+
+        if (asnId === 0) {
+            const parmas = {
+                "orders": formData,
+                "items": tabelData,
+            }
+            const res = await request.post(`/outStock/items/save`, parmas);
+            if (res?.data?.code === 200) {
+                setOpen(false);
+                refresh();
+                resetData()
+            } else {
+                notify(res.data.msg);
+            }
+        } else {
+            const parmas = {
+                "orders": formData,
+                "items": tabelData,
+            }
+            const res = await request.post(`/outStock/items/update`, parmas);
+            if (res?.data?.code === 200) {
+                setOpen(false);
+                refresh();
+                resetData()
+            } else {
+                notify(res.data.msg);
+            }
+        }
+        setDisabled(false)
+
+    };
+
+
+    const handleDelete = async () => {
+        const res = await request.post(`/outStock/remove/${asnId}`);
+        if (res?.data?.code === 200) {
+            setOpen(false);
+            refresh();
+        } else {
+            notify(res.data.msg);
+        }
+    };
+
+    const requestGetHead = async () => {
+        const res = await request.get(`/outStock/${asnId}`);
+        if (res?.data?.code === 200) {
+            setFormData(res.data.data)
+        } else {
+            notify(res.data.msg);
+        }
+    }
+
+    const requestGetBody = async () => {
+        const res = await request.post(`/outStockItem/page`, { orderId: asnId });
+        if (res?.data?.code === 200) {
+            setTableData(res.data.data.records)
+        } else {
+            notify(res.data.msg);
+        }
+    }
+
+    const [selectedRows, setSelectedRows] = useState([]);
+
+    const handleDeleteItem = () => {
+        const newTableData = _.filter(tabelData, (item) => !selectedRows.includes(item.matnrId));
+        setTableData(newTableData);
+    }
+
+    return (
+        <>
+            <Dialog
+                open={open}
+                onClose={handleClose}
+                aria-labelledby="form-dialog-title"
+                aria-hidden
+                fullWidth
+                disableRestoreFocus
+                maxWidth="lg"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
+            >
+                <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 }}>
+                    <Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
+                        <Form defaultValues={formData}>
+                            <Grid container spacing={2}>
+                                {/* <Grid item md={2}>
+                                    <DictSelect
+                                        label={translate("table.field.asnOrder.type")}
+                                        value={formData.type}
+                                        onChange={(e) => handleChange(e.target.value, 'type')}
+                                        dictTypeCode="sys_order_type"
+                                        required
+                                    />
+                                </Grid> */}
+                                <Grid item md={2}>
+                                    <DictSelect
+                                        label={translate("table.field.outStock.wkType")}
+                                        value={formData.wkType}
+                                        variant="filled"
+                                        group='2'
+                                        onChange={(e) => handleChange(e.target.value, 'wkType')}
+                                        dictTypeCode="sys_business_type"
+                                        required
+                                    />
+                                </Grid>
+                                <Grid item md={2}>
+                                    <TextField
+                                        label={translate("table.field.outStock.poCode")}
+                                        value={formData.poCode}
+                                        variant="filled"
+                                        size='small'
+                                        onChange={(e) => handleChange(e.target.value, 'poCode')}
+                                    />
+                                </Grid>
+                                <Grid item md={2}>
+                                    <TextField
+                                        label={translate("table.field.outStock.logisNo")}
+                                        value={formData.logisNo}
+                                        variant="filled"
+                                        size='small'
+                                        onChange={(e) => handleChange(e.target.value, 'logisNo')}
+                                    />
+                                </Grid>
+                                <Grid item md={2}>
+                                    <DateInput
+                                        source="arrTime"
+                                        label="table.field.outStock.arrTime"
+                                        size='small'
+                                        variant="filled"
+                                        value={formData.arrTime}
+                                        onChange={(e) => handleChange(e.target.value, 'arrTime')}
+                                    />
+                                </Grid>
+                            </Grid>
+                        </Form>
+                    </Box>
+
+                    <Box sx={{ mt: 2 }}>
+                        <Stack direction="row" spacing={2}>
+                            <Button variant="contained" onClick={() => setCreateDialog(true)}>鏂板鐗╂枡</Button>
+                            {/* {asnId !== '' && <ConfirmButton label={'鍒犻櫎'} variant="outlined" color="error" onConfirm={handleDelete} />} */}
+                            <ConfirmButton label={'鍒犻櫎'} variant="outlined" color="error" onConfirm={handleDeleteItem} />
+                        </Stack>
+                    </Box>
+                    <Box sx={{ mt: 2 }}>
+                        <AsnOrderModalTable tabelData={tabelData} setTableData={setTableData} asnId={asnId} selectedRows={selectedRows} setSelectedRows={setSelectedRows} tableRef={tableRef}></AsnOrderModalTable>
+                    </Box>
+                </DialogContent>
+                <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
+                    <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }}  >
+                        <Button disabled={disabled} onClick={handleSubmit} variant="contained" startIcon={<SaveIcon />}>
+                            {translate('toolbar.confirm')}
+                        </Button>
+                    </Toolbar>
+                </DialogActions>
+            </Dialog>
+            <MatnrInfoModal
+                open={createDialog}
+                setOpen={setCreateDialog}
+                data={tabelData}
+                setData={setTableData}
+            />
+        </>
+    )
+}
+
+export default SelectMatnrModal;
+
+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 SelectInputSplrCodeEditCell = (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.id === e.target.value);
+
+                // 濡傛灉鎵惧埌瀵瑰簲鐨勪緵搴斿晢璁板綍锛屽悓鏃舵洿鏂皊plrCode瀛楁
+                if (selectedSupplier) {
+                    params.api.setEditCellValue({
+                        id: params.id,
+                        field: 'splrName',
+                        value: selectedSupplier.name,
+                    });
+                }
+            }}
+            fullWidth
+
+        >
+            {formData.map(e => {
+                return (
+                    <MenuItem value={e.id} children={e.name} key={e.id} />
+                );
+
+            })}
+
+        </Select>
+    );
+};
+
+
+
+
+const AsnOrderModalTable = ({ tabelData, setTableData, asnId, selectedRows, setSelectedRows, tableRef }) => {
+    const translate = useTranslate();
+    const notify = useNotify();
+
+    const [columns, setColumns] = useState([
+        {
+            field: 'maktx',
+            headerName: translate('table.field.outStockItem.maktx'),
+            width: 250,
+            editable: false,
+        },
+        {
+            field: 'matnrCode',
+            headerName: translate('table.field.outStockItem.matnrCode'),
+            width: 130,
+            editable: false,
+        },
+        {
+            field: 'anfme',
+            headerName: translate('table.field.outStockItem.anfme') + "*",
+            type: 'number',
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+            valueFormatter: (val) => val < 0 ? 0 : val,
+            headerClassName: "custom",
+        },
+        {
+            field: 'splrCode',
+            headerName: translate('table.field.outStockItem.splrCode') + "*",
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+            renderEditCell: (params) => (
+                <SelectInputSplrCodeEditCell {...params} />
+            ),
+            headerClassName: "custom",
+        },
+        {
+            field: 'splrName',
+            headerName: translate('table.field.outStockItem.splrName') + "*",
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+            renderEditCell: (params) => (
+                <SelectInputSplrNameEditCell {...params} />
+            ),
+            headerClassName: "custom",
+        },
+        {
+            field: 'splrBatch',
+            headerName: translate('table.field.outStockItem.splrBatch'),
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+        },
+        {
+            field: 'poCode',
+            headerName: translate('table.field.outStockItem.poDetlCode'),
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+        },
+        {
+            field: 'stockUnit',
+            headerName: translate('table.field.outStockItem.stockUnit'),
+            minWidth: 100,
+            flex: 1,
+            editable: true,
+        },
+    ])
+
+    const action = {
+        field: 'action',
+        headerName: '鎿嶄綔',
+        width: 70,
+        lockPosition: 'left',
+        renderCell: (params) => (
+            <Tooltip title="Delete">
+                <IconButton onClick={() => handleDelete(params.row)}>
+                    <Delete />
+                </IconButton>
+            </Tooltip>
+        ),
+
+    }
+
+    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,
+                headerName: el.fieldsAlise,
+                minWidth: 100,
+                flex: 1,
+                editable: el.unique,
+                valueGetter: (value, row) => {
+                    return row.extendFields?.[el.fields] || '';
+                },
+            }))
+            setColumns([...columns, ...cols, action])
+        } else {
+            notify(msg);
+        }
+    }
+
+
+    const handleDelete = (row) => {
+        const newData = _.filter(cdata.current, (item) => item.matnrId !== row.matnrId);
+        setTableData(newData);
+    };
+
+
+    const processRowUpdate = (newRow, oldRow) => {
+        const rows = tabelData.map((r) =>
+            r.matnrId === newRow.matnrId ? { ...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.matnrId ? row.matnrId : row.id}
+                disableColumnFilter
+                disableColumnSelector
+                disableColumnSorting
+                disableMultipleColumnsSorting
+                processRowUpdate={processRowUpdate}
+                initialState={{
+                    pagination: {
+                        paginationModel: {
+                            pageSize: 25,
+                        },
+                    },
+                }}
+                pageSizeOptions={[10, 20, 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/orders/preparation/MatPreparationPublic.jsx b/rsf-admin/src/page/orders/preparation/PreparationStockPublic.jsx
similarity index 95%
rename from rsf-admin/src/page/orders/preparation/MatPreparationPublic.jsx
rename to rsf-admin/src/page/orders/preparation/PreparationStockPublic.jsx
index c40f37d..f962a5c 100644
--- a/rsf-admin/src/page/orders/preparation/MatPreparationPublic.jsx
+++ b/rsf-admin/src/page/orders/preparation/PreparationStockPublic.jsx
@@ -47,7 +47,7 @@
 import CloseSharpIcon from '@mui/icons-material/CloseSharp';
 import ConfirmButton from '../../components/ConfirmButton';
 import { Delete, Edit, Add } from '@mui/icons-material';
-// import OutStockSiteDialog from "./OutStockSiteDialog";
+import OutStockSiteDialog from "./PreparationStockSiteDialog";
 
 const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
     '& .css-1vooibu-MuiSvgIcon-root': {
@@ -67,7 +67,7 @@
 
 }));
 
-const MatPerparationPublic = (props) => {
+const OutStockPublic = (props) => {
     const { record, open, setOpen, setManualDialog } = props;
     const notify = useNotify();
     const gridRef = useGridApiRef();
@@ -201,19 +201,14 @@
                                     sx={{ height: "2px", position: 'absolute', top: 0, left: 0, right: 0, }}
                                 />
                                 <StyledDatagrid
-                                    storeKey={"matPerparationPublic"}
+                                    storeKey={"outStockPublic"}
                                     preferenceKey='outStockItem'
                                     bulkActionButtons={<>
                                         <ComfirmButton />
                                     </>}
                                     omit={['id', 'splrName', 'qty', 'poCode',]}
                                 >
-                                    <TextField source="matnrCode" label="鐗╂枡缂栫爜" />,
-                                    <TextField source="maktx" label="鐗╂枡鍚嶇О" />,
-                                    <TextField source="spec" label="瑙勬牸" />,
-                                    <TextField source="unit" label="鍗曚綅" />,
-                                    <TextField source="anfme" label="鏁伴噺" />,
-                                    {/* <NumberField source="id" />
+                                    <NumberField source="id" />
                                     <TextField source="asnCode" label="table.field.outStockItem.orderCode" />
                                     <TextField source="poCode" label="table.field.outStockItem.poCode" />
                                     <TextField source="matnrCode" label="table.field.outStockItem.matnrCode" />
@@ -222,7 +217,7 @@
                                     <NumberField source="workQty" label="table.field.outStockItem.workQty" />
                                     <NumberField source="qty" label="table.field.outStockItem.qty" />
                                     <TextField source="stockUnit" label="table.field.outStockItem.stockUnit" />
-                                    <TextField source="splrName" label="table.field.outStockItem.splrName" /> */}
+                                    <TextField source="splrName" label="table.field.outStockItem.splrName" />
                                 </StyledDatagrid>
                             </List>
                         </Card>
@@ -483,6 +478,6 @@
     )
 }
 
-export default MatPerparationPublic;
+export default OutStockPublic;
 
 
diff --git a/rsf-admin/src/page/orders/preparation/PreparationStockSiteDialog.jsx b/rsf-admin/src/page/orders/preparation/PreparationStockSiteDialog.jsx
new file mode 100644
index 0000000..e0b0ced
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/PreparationStockSiteDialog.jsx
@@ -0,0 +1,54 @@
+import { Box, Card, Grid, List, LinearProgress, Select, MenuItem, ListItemText, ListItemAvatar, Avatar, ListItemButton, Dialog, DialogTitle, ListItem } from "@mui/material";
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE, DEFAULT_ITEM_PAGE_SIZE } from '@/config/setting';
+import { Delete, Edit, Add } from '@mui/icons-material';
+import request from '@/utils/request';
+import { useTranslate } from "react-admin";
+
+const OutStockSiteDialog = (props) => {
+    const translate = useTranslate();
+    const { onClose, selectedValue, open } = props;
+    const [siteNos, setSiteNos] = useState([]);
+
+    const handleClose = () => {
+        onClose(selectedValue);
+    }
+
+    const handleListItemClick = (value) => {
+        onClose(value);
+    }
+
+    useEffect(() => {
+        getSiteNos()
+    }, [open])
+
+
+    const getSiteNos = async () => {
+        const { data: { code, data, msg } } = await request.get('/outStock/tasks/sites');
+        if (code === 200) {
+            setSiteNos(data);
+        } else {
+            notify(msg);
+        }
+    }
+
+    return (
+        <Dialog
+            onClose={handleClose}
+            open={open}
+        >
+            <DialogTitle>{translate("toolbar.modiftySite")}</DialogTitle>
+            <List sx={{ pt: 0 }}>
+                {siteNos.map((site) => (
+                    <ListItem disableGutters key={site?.id}>
+                        <ListItemButton onClick={() => handleListItemClick(site)}>
+                            <ListItemText primary={site.site} />
+                        </ListItemButton>
+                    </ListItem>
+                ))}
+            </List>
+        </Dialog>
+    );
+}
+
+export default OutStockSiteDialog;
\ No newline at end of file
diff --git a/rsf-admin/src/page/orders/preparation/PreparationStockWaveDialog.jsx b/rsf-admin/src/page/orders/preparation/PreparationStockWaveDialog.jsx
new file mode 100644
index 0000000..6bd93c7
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/PreparationStockWaveDialog.jsx
@@ -0,0 +1,64 @@
+import { Box, Card, Grid, List, LinearProgress, Select, MenuItem, ListItemText, ListItemAvatar, Avatar, ListItemButton, Dialog, DialogTitle, ListItem, DialogContent, DialogActions, Button } from "@mui/material";
+import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE, DEFAULT_ITEM_PAGE_SIZE, DEFAULT_TYPE } from '@/config/setting';
+import { SimpleForm, TextInput, ReferenceInput, useTranslate, AutocompleteInput } from "react-admin";
+import React, { useState, useRef, useEffect, useMemo } from "react";
+import { Delete, Edit, Add } from '@mui/icons-material';
+import SaveIcon from '@mui/icons-material/Save';
+import request from '@/utils/request';
+
+const OutStockWaveDialog = (props) => {
+    const translate = useTranslate();
+    const { onClose, selectedValue, open, setOpen } = props;
+    const [siteNos, setSiteNos] = useState([]);
+    const [formData, setFormData] = useState({
+        id: DEFAULT_TYPE
+    });
+
+    const handleClose = () => {
+        onClose(null);
+    }
+
+    const handleSelect = () => {
+        onClose(formData);
+
+    }
+
+    const handleListItemClick = (value) => {
+        onClose(value);
+    }
+
+    const handleChange = (event) => {
+        console.log(event);
+        setFormData({ id: event })
+    };
+
+    return (
+        <Dialog
+            onClose={handleClose}
+            open={open}
+            aria-labelledby="alert-dialog-title"
+            aria-describedby="alert-dialog-description"
+        >
+            <DialogTitle id="alert-dialog-title">{translate("toolbar.selectWave")}</DialogTitle>
+            <DialogContent sx={{ width: 600 }}>
+                <SimpleForm toolbar={false} defaultValue={formData}>
+                    <ReferenceInput source="id" reference="waveRule" >
+                        <AutocompleteInput
+                            label={'toolbar.selectWave'}
+                            defaultValue={DEFAULT_TYPE}
+                            onChange={handleChange}
+                        />
+                    </ReferenceInput>
+                </SimpleForm>
+            </DialogContent>
+            <DialogActions>
+                <Button onClick={handleClose} variant="outlined">{translate('toolbar.cancel')}</Button>
+                <Button onClick={handleSelect} autoFocus startIcon={<SaveIcon />} variant="contained">
+                    {translate('toolbar.confirm')}
+                </Button>
+            </DialogActions>
+        </Dialog>
+    );
+}
+
+export default OutStockWaveDialog;
\ No newline at end of file
diff --git a/rsf-admin/src/page/orders/preparation/index.jsx b/rsf-admin/src/page/orders/preparation/index.jsx
index 6214961..8ac1ccd 100644
--- a/rsf-admin/src/page/orders/preparation/index.jsx
+++ b/rsf-admin/src/page/orders/preparation/index.jsx
@@ -5,14 +5,14 @@
     ShowGuesser,
 } from "react-admin";
 
-import MatPreparationList from "./MatPreparationList";
-// import TaskEdit from "./TaskEdit";
+import PreparationOrderList from "./PreparationOrderList";
+import PreparationOrderEdit from "./PreparationOrderEdit";
 
 export default {
-    list: MatPreparationList,
-    // edit: TaskEdit,
+    list: PreparationOrderList,
+    edit: PreparationOrderEdit,
     show: ShowGuesser,
     recordRepresentation: (record) => {
-        return `${record.id}`
+        return `${record.name}`
     }
 };
diff --git a/rsf-admin/src/page/orders/preparation/preparation.css b/rsf-admin/src/page/orders/preparation/preparation.css
new file mode 100644
index 0000000..0df941e
--- /dev/null
+++ b/rsf-admin/src/page/orders/preparation/preparation.css
@@ -0,0 +1,5 @@
+
+.custom {
+    color: rgb(0, 195, 255) !important;    
+  }
+
diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/MatPreparationController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/MatPreparationController.java
index 09194cd..9e416e6 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/MatPreparationController.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/MatPreparationController.java
@@ -1,96 +1,328 @@
 package com.vincent.rsf.server.manager.controller;
 
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.vincent.rsf.framework.common.Cools;
 import com.vincent.rsf.framework.common.R;
+import com.vincent.rsf.framework.exception.CoolException;
+import com.vincent.rsf.server.common.annotation.OperationLog;
 import com.vincent.rsf.server.common.domain.BaseParam;
+import com.vincent.rsf.server.common.domain.KeyValVo;
 import com.vincent.rsf.server.common.domain.PageParam;
+import com.vincent.rsf.server.common.utils.ExcelUtil;
 import com.vincent.rsf.server.common.utils.FieldsUtils;
-import com.vincent.rsf.server.manager.entity.Task;
-import com.vincent.rsf.server.manager.entity.TaskItem;
-import com.vincent.rsf.server.manager.entity.WkOrder;
+import com.vincent.rsf.server.manager.controller.params.AsnOrderAndItemsParams;
+import com.vincent.rsf.server.manager.controller.params.GenWaveParams;
+import com.vincent.rsf.server.manager.controller.params.OrderOutTaskParam;
+import com.vincent.rsf.server.manager.controller.params.OutStockToTaskParams;
+import com.vincent.rsf.server.manager.entity.*;
+import com.vincent.rsf.server.manager.entity.excel.AsnOrderTemplate;
+import com.vincent.rsf.server.manager.enums.AsnExceStatus;
 import com.vincent.rsf.server.manager.enums.OrderType;
+import com.vincent.rsf.server.manager.enums.OrderWorkType;
+import com.vincent.rsf.server.manager.service.OutStockItemService;
 import com.vincent.rsf.server.manager.service.OutStockService;
 import com.vincent.rsf.server.manager.service.TaskService;
+import com.vincent.rsf.server.system.constant.SerialRuleCode;
 import com.vincent.rsf.server.system.controller.BaseController;
+import com.vincent.rsf.server.system.utils.SerialRuleUtils;
 import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+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.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
 import java.util.*;
 
 @RestController
 @Api(tags = "澶囨枡鍗�")
 public class MatPreparationController extends BaseController {
 
-    Logger logger = LoggerFactory.getLogger(OutStockController.class);
+    @Autowired
+    private OutStockService outStockService;
+    @Autowired
+    private OutStockItemService outStockItemService;
 
-    @Resource
-    private TaskService taskService;
-
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
     @PostMapping("/preparation/page")
     public R page(@RequestBody Map<String, Object> map) {
-
-//        JSONObject jsonObject = new JSONObject(map);
-//        jsonObject.put("orderCode", "BL25090927787678");
-//        jsonObject.put("orderStatus", 1);
-//        jsonObject.put("orderTime", new Date());
-//        jsonObject.put("anfme", 100);
-//        List<JSONObject> list = new ArrayList<>();
-//        list.add(jsonObject);
-//
-//        return R.ok().add(list);
-
         BaseParam baseParam = buildParam(map, BaseParam.class);
-        PageParam<Task, BaseParam> pageParam = new PageParam<>(baseParam, Task.class);
-        QueryWrapper<Task> queryWrapper = pageParam.buildWrapper(true);
-        return R.ok().add(taskService.page(pageParam, queryWrapper));
+        PageParam<WkOrder, BaseParam> pageParam = new PageParam<>(baseParam, WkOrder.class);
+        QueryWrapper<WkOrder> queryWrapper = pageParam.buildWrapper(true);
+//        List<String> list = Arrays.asList(OrderType.ORDER_OUT.type);
+//        queryWrapper.in("type", list);
+        return R.ok().add(outStockService.page(pageParam, queryWrapper));
     }
 
-//    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    @PostMapping("/preparation/dialog/page")
+    public R dialogPage(@RequestBody Map<String, Object> map) {
+        BaseParam baseParam = buildParam(map, BaseParam.class);
+        PageParam<WkOrder, BaseParam> pageParam = new PageParam<>(baseParam, WkOrder.class);
+        QueryWrapper<WkOrder> queryWrapper = pageParam.buildWrapper(true);
+        List<String> list = Arrays.asList(OrderType.ORDER_OUT.type);
+        queryWrapper.in("type", list);
+        return R.ok().add(outStockService.page(pageParam, queryWrapper));
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
     @PostMapping("/preparation/list")
     public R list(@RequestBody Map<String, Object> map) {
+        return R.ok().add(outStockService.list(new LambdaQueryWrapper<WkOrder>().eq(WkOrder::getType, OrderType.ORDER_OUT.type)));
+    }
 
-        return R.ok().add(taskService.list());
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    @PostMapping({"/preparation/many/{ids}", "/asnOrders/many/{ids}"})
+    public R many(@PathVariable Long[] ids) {
+        return R.ok().add(outStockService.listByIds(Arrays.asList(ids)));
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    @OperationLog("琛ㄥ崟鏌ヨ")
+    @GetMapping("/preparation/{id}")
+    public R get(@PathVariable("id") Long id) {
+        return R.ok().add(outStockService.getById(id));
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:save')")
+    @OperationLog("Create 鍑哄簱鍗曟嵁")
+    @PostMapping("/preparation/save")
+    @ApiOperation("淇濆瓨")
+    public R save(@RequestBody WkOrder wkOrder) {
+        wkOrder.setCreateBy(getLoginUserId())
+                .setUpdateBy(getLoginUserId());
+        if (Objects.isNull(wkOrder.getCode())) {
+            String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_OUT_STOCK_CODE, wkOrder);
+            if (Objects.isNull(ruleCode) || StringUtils.isBlank(ruleCode)) {
+                return R.error("缂栫爜瑙勫垯閿欒锛氱紪鐮併�孲YS_OUT_STOCK_CODE銆嶆槸鏈缃垚鍔燂紒锛�");
+            }
+            wkOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)
+                    .setCode(ruleCode);
+        }
+        if (!outStockService.save(wkOrder)) {
+            return R.error("Save Fail");
+        }
+        return R.ok("Save Success").add(wkOrder);
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:update')")
+    @OperationLog("Update 锛涘嚭搴撳崟鎹�")
+    @PostMapping("/preparation/update")
+    @ApiOperation("鏇存柊")
+    public R update(@RequestBody WkOrder wkOrder) {
+        wkOrder.setType(OrderType.ORDER_OUT.type)
+                .setUpdateBy(getLoginUserId())
+                .setUpdateTime(new Date());
+        if (Objects.isNull(wkOrder.getCode())) {
+            String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_OUT_STOCK_CODE, wkOrder);
+            wkOrder.setCode(ruleCode);
+        }
+        if (!outStockService.updateById(wkOrder)) {
+            return R.error("Update Fail");
+        }
+        return R.ok("Update Success").add(wkOrder);
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:remove')")
+    @OperationLog("Delete 鍑哄簱鍗曟嵁")
+    @PostMapping("/preparation/remove/{ids}")
+    public R remove(@PathVariable Long[] ids) {
+        if (!outStockService.removeByIds(Arrays.asList(ids))) {
+            return R.error("Delete Fail");
+        }
+        return R.ok("Delete Success").add(ids);
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    @PostMapping("/preparation/query")
+    @ApiOperation("鏌ヨ")
+    public R query(@RequestParam(required = false) String condition) {
+        List<KeyValVo> vos = new ArrayList<>();
+        LambdaQueryWrapper<WkOrder> wrapper = new LambdaQueryWrapper<>();
+        if (!Cools.isEmpty(condition)) {
+            wrapper.like(WkOrder::getCode, condition);
+        }
+        outStockService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
+                item -> vos.add(new KeyValVo(item.getId(), item.getCode()))
+        );
+        return R.ok().add(vos);
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:update')")
+    @ApiOperation("鍙栨秷鍑哄簱鍗曟嵁")
+    @GetMapping("/preparation/cancel/{id}")
+    public R cancel(@PathVariable String id) {
+        if (Objects.isNull(id)) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        return outStockService.cancelOutOrder(id);
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:update')")
+    @ApiOperation("鍒犻櫎宸茬敓鎴愭槑缁�")
+    @GetMapping("/preparation/items/cancel/{ids}")
+    public R cancel(@PathVariable Long[] ids) {
+        List<Long> list = Arrays.asList(ids);
+        if (list.isEmpty()) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        List<WkOrderItem> orderItems = outStockItemService.list(new LambdaQueryWrapper<WkOrderItem>().in(WkOrderItem::getId, list));
+        if (orderItems.isEmpty()) {
+            throw new CoolException("鍗曟嵁鏄庣粏涓嶅瓨鍦紒锛�");
+        }
+        return outStockService.cancelOutOrderByItems(orderItems);
     }
 
 
-//    @PreAuthorize("hasAuthority('manager:taskItem:list')")
-    @PostMapping("/preparationItem/page")
-    public R itemPage(@RequestBody Map<String, Object> map) {
-//        BaseParam baseParam = buildParam(map, BaseParam.class);
-//        PageParam<TaskItem, BaseParam> pageParam = new PageParam<>(baseParam, TaskItem.class);
-//        /**鎷兼帴鎵╁睍瀛楁*/
-//        PageParam<TaskItem, BaseParam> page = taskItemService.page(pageParam, pageParam.buildWrapper(true));
-//        List<TaskItem> records = page.getRecords();
-//        for (TaskItem record : records) {
-//            if (!Objects.isNull(record.getFieldsIndex())) {
-//                Map<String, String> fields = FieldsUtils.getFields(record.getFieldsIndex());
-//                record.setExtendFields(fields);
-//            }
-//        }
-//        page.setRecords(records);
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    @PostMapping("/preparation/export")
+    @ApiOperation("瀵煎嚭")
+    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
+        List<WkOrder> orders = new ArrayList<>();
+        if (!Objects.isNull(map.get("ids"))) {
+            List<Long> ids = JSONArray.parseArray(JSONObject.toJSONString(map.get("ids")), Long.class);
+            if (!ids.isEmpty()) {
+                orders = outStockService.list(new LambdaQueryWrapper<WkOrder>().in(WkOrder::getId, ids));
+            } else {
+                orders = outStockService.list(new LambdaQueryWrapper<>());
+            }
+        } else {
+            orders = outStockService.list();
+        }
+        List<AsnOrderTemplate> orderTemplates = new ArrayList<>();
+        for (WkOrder order : orders) {
+            List<WkOrderItem> orderItems = outStockItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, order.getId()));
+            for (WkOrderItem item : orderItems) {
+                if (Objects.isNull(item)) {
+                    continue;
+                }
+                AsnOrderTemplate template = new AsnOrderTemplate();
+                template.setCode(order.getCode())
+                        .setType(OrderType.getValType(order.getType()))
+                        .setWkType(OrderWorkType.getWorkDesc(order.getWkType()))
+                        .setAnfme(item.getAnfme() + "")
+                        .setMaktx(item.getMaktx())
+                        .setMemo(item.getMemo())
+                        .setMatnrCode(item.getMatnrCode())
+                        .setPoCode(item.getPoCode())
+                        .setSplrName(item.getSplrName())
+                        .setPlatItemId(item.getPlatItemId())
+                        .setSplrBatch(item.getSplrBatch())
+                        .setSplrCode(item.getSplrCode());
+                orderTemplates.add(template);
+            }
+        }
+        ExcelUtil.build(ExcelUtil.create(orderTemplates, AsnOrderTemplate.class), response);
+    }
 
 
-        JSONObject jsonObject = new JSONObject(map);
-        jsonObject.put("matnrCode", "BL25090927787678");
-        jsonObject.put("maktx", "鎷夋潌");
-        jsonObject.put("unit", "涓�");
-        jsonObject.put("spec", "閾濆悎閲� 閾惰壊");
-        jsonObject.put("model", "澶�");
-        jsonObject.put("anfme", 100);
-        jsonObject.put("workQty", 60);
-        jsonObject.put("status", 11);
-        List<JSONObject> list = new ArrayList<>();
-        list.add(jsonObject);
-        return R.ok().add(list);
+    @PreAuthorize("hasAuthority('manager:outStock:update')")
+    @ApiOperation("閫氳繃DO鍗曠敓鎴愬嚭搴撳崟")
+    @PostMapping("/preparation/generate/orders")
+    public R genOutStock(@RequestBody Map<String, Object> params) {
+        if (Objects.isNull(params.get("ids"))) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        List<DeliveryItem> items = JSON.parseArray(JSONObject.toJSONString(params.get("ids")), DeliveryItem.class);
+        return outStockService.genOutStock(items, getLoginUserId());
+    }
+
+    @PreAuthorize("hasAuthority('manager:outStock:update')")
+    @ApiOperation("鍑哄簱鍗曠敓鎴愭尝娆�")
+    @PostMapping("/preparation/generate/wave")
+    public R generateWave(@RequestBody GenWaveParams params) {
+        if (Objects.isNull(params)) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        return outStockService.generateWaves(params);
+    }
+
+    @PostMapping("/preparation/items/save")
+    @ApiOperation("淇濆瓨涓诲崟鍙婃槑缁�")
+    @PreAuthorize("hasAuthority('manager:outStock:save')")
+    public R saveOutStock(@RequestBody AsnOrderAndItemsParams params) throws Exception {
+        if (Objects.isNull(params)) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        return outStockService.saveOutStock(params, getLoginUserId());
+    }
+
+    @GetMapping("/preparation/complete/{id}")
+    @ApiOperation("瀹屾垚鍑哄簱鍗�")
+    @PreAuthorize("hasAuthority('manager:outStock:update')")
+    public R complete(@PathVariable Long id) {
+        if (Objects.isNull(id)) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        return outStockService.completeOutOrderById(id);
+    }
+
+
+    @ApiOperation("鍗曟嵁淇℃伅淇敼")
+    @PostMapping("/preparation/items/update")
+    @PreAuthorize("hasAuthority('manager:outStock:update')")
+    public R orderAndrItemUpdate(@RequestBody AsnOrderAndItemsParams params) throws Exception {
+        if (Objects.isNull(params)) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        return outStockService.updateOrderItem(params, getLoginUserId());
+    }
+
+    @PostMapping("/preparation/order/getOutTaskItems")
+    @ApiOperation("鍑哄簱鍗曞簱浣嶉瑙�")
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    public R getOrderOutTaskItem(@RequestBody OrderOutTaskParam param) {
+        if (Cools.isEmpty(param)) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖");
+        }
+
+        return outStockService.getOrderOutTaskItem(param);
+    }
+
+
+    /**
+     * 鍑哄簱鍗曠敓鎴愬嚭搴撲换鍔�
+     * @param params
+     * @return
+     */
+    @PostMapping("/preparation/generate/tasks")
+    @ApiOperation("鍑哄簱鍗曠敓鎴愬嚭搴撲换鍔�")
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    public R genOutStockTask( @RequestBody Map<String, Object> params) {
+        if (Cools.isEmpty()) {
+            return R.error("鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        if (Objects.isNull(params.get("outId"))) {
+            return R.error("鍑哄簱鍙傛暟涓嶈兘涓虹┖锛侊紒");
+        }
+        List<OutStockToTaskParams> taskParams = JSONArray.parseArray(JSONArray.toJSONString(params.get("items")), OutStockToTaskParams.class);
+        List<OutStockToTaskParams> tasks = new ArrayList<>();
+        for (OutStockToTaskParams taskParam : taskParams) {
+            if (StringUtils.isNotBlank(taskParam.getLocCode())) {
+                tasks.add(taskParam);
+            }
+        }
+        return  outStockService.genOutStockTask(tasks, getLoginUserId(), Long.parseLong(params.get("outId").toString()));
+    }
+
+    /**
+     * 鑾峰彇鍑哄簱绔欑偣
+     * @return
+     */
+    @GetMapping("/preparation/tasks/sites")
+    @ApiOperation("鑾峰彇鍑哄簱搴撳彛")
+    @PreAuthorize("hasAuthority('manager:outStock:list')")
+    public R getSiteNos() {
+        return outStockService.getSiteNos();
     }
 
 }

--
Gitblit v1.9.1