zhou zhou
5 小时以前 6a1343a69fac68c4259ceddbb037970461972c9e
#备货单页面
3个文件已删除
1 文件已重命名
13个文件已添加
3个文件已修改
4085 ■■■■ 已修改文件
rsf-admin/src/page/orders/outStock/OutOrderList.jsx 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/MatPreparationItemList.jsx 216 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/MatPreparationList.jsx 422 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/MatPreparationPanel.jsx 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationMatnrInfoModal.jsx 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationOrderCreate.jsx 221 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationOrderEdit.jsx 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationOrderItemCreate.jsx 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationOrderItemEdit.jsx 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationOrderItemList.jsx 206 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationOrderList.jsx 386 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationOrderModal.jsx 282 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationOrderPreview.jsx 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationSelectMatnrModal.jsx 587 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationStockPublic.jsx 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationStockSiteDialog.jsx 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/PreparationStockWaveDialog.jsx 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/index.jsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/preparation/preparation.css 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/MatPreparationController.java 338 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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}
rsf-admin/src/page/orders/preparation/MatPreparationItemList.jsx
File was deleted
rsf-admin/src/page/orders/preparation/MatPreparationList.jsx
File was deleted
rsf-admin/src/page/orders/preparation/MatPreparationPanel.jsx
File was deleted
rsf-admin/src/page/orders/preparation/PreparationMatnrInfoModal.jsx
New file
@@ -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>
    );
};
rsf-admin/src/page/orders/preparation/PreparationOrderCreate.jsx
New file
@@ -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: ' 正常' },
                                                { 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>
    );
}
rsf-admin/src/page/orders/preparation/PreparationOrderEdit.jsx
New file
@@ -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: ' 正常' },
                                        { id: 1, name: ' 已释放' },
                                    ]}
                                    validate={required()}
                                /> */}
                            </Stack>
                        </Grid>
                    </Grid>
                </SimpleForm>
            </Edit >
            <OutOrderItemList />
        </>
    )
}
export default PreparationOrderEdit;
rsf-admin/src/page/orders/preparation/PreparationOrderItemCreate.jsx
New file
@@ -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;
rsf-admin/src/page/orders/preparation/PreparationOrderItemEdit.jsx
New file
@@ -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;
rsf-admin/src/page/orders/preparation/PreparationOrderItemList.jsx
New file
@@ -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>
  )
}
rsf-admin/src/page/orders/preparation/PreparationOrderList.jsx
New file
@@ -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: ' 正常' },
      { 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"} /> : <></>
  )
}
rsf-admin/src/page/orders/preparation/PreparationOrderModal.jsx
New file
@@ -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>
    )
}
rsf-admin/src/page/orders/preparation/PreparationOrderPreview.jsx
New file
@@ -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)
            }}
        />
    )
}
rsf-admin/src/page/orders/preparation/PreparationSelectMatnrModal.jsx
New file
@@ -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);
                // 如果找到对应的供应商记录,同时更新splrCode字段
                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);
                // 如果找到对应的供应商记录,同时更新splrCode字段
                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>
    );
};
rsf-admin/src/page/orders/preparation/PreparationStockPublic.jsx
File was renamed from rsf-admin/src/page/orders/preparation/MatPreparationPublic.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;
rsf-admin/src/page/orders/preparation/PreparationStockSiteDialog.jsx
New file
@@ -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;
rsf-admin/src/page/orders/preparation/PreparationStockWaveDialog.jsx
New file
@@ -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;
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}`
    }
};
rsf-admin/src/page/orders/preparation/preparation.css
New file
@@ -0,0 +1,5 @@
.custom {
    color: rgb(0, 195, 255) !important;
  }
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("编码规则错误:编码「SYS_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();
    }
}