skyouc
6 天以前 a0c27ec444c85326efe3bacf3205dfecbd66451d
#新增
1. 新增出库单明细
2. 编码规则bug修改
14个文件已添加
7个文件已修改
3069 ■■■■■ 已修改文件
rsf-admin/.env 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/en.js 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/zh.js 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/ResourceContent.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/AsnOrderModal.jsx 643 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/AsnOrderPanel.jsx 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/AsnWareModal.jsx 249 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderCreate.jsx 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderEdit.jsx 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderItemCreate.jsx 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderItemEdit.jsx 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderItemList.jsx 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderList.jsx 263 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/PrintModal.jsx 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/asnOrder.css 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/system/serialRule/SerialRuleEdit.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/system/serialRule/SerialRuleItemList.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutOrderItemController.java 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/.env
@@ -1,3 +1,3 @@
VITE_BASE_IP=192.168.4.24
VITE_BASE_IP=192.168.4.25
# VITE_BASE_IP=47.76.147.249
VITE_BASE_PORT=8080
rsf-admin/src/i18n/en.js
@@ -170,6 +170,7 @@
        logs: 'Logs',
        permissions: 'Permissions',
        delivery: 'Delivery',
        outStock: 'Out Stock',
    },
    table: {
        field: {
rsf-admin/src/i18n/zh.js
@@ -171,6 +171,7 @@
        logs: '日志',
        permissions: '权限管理',
        delivery: 'DO单',
        outStock: '出库单',
    },
    table: {
@@ -531,6 +532,20 @@
                ntyStatus: "上报状态",
                exceStatus: '单据状态'
            },
            outStock: {
                code: "出库单号",
                poCode: "平台单号",
                poId: "PO标识",
                type: "类型",
                wkType: "业务类型",
                anfme: "数量",
                qty: "已出库数量",
                logisNo: "物流单号",
                arrTime: "预计到达时间",
                rleStatus: "释放状态",
                ntyStatus: "上报状态",
                exceStatus: '单据状态'
            },
            asnOrderItem: {
                asnId: "主单标识",
                asnCode: "主单编码",
@@ -554,6 +569,28 @@
                prodTime: "生产日期",
                platItemId: '行号'
            },
            outStockItem: {
                asnId: "主单标识",
                asnCode: "单号",
                poDetlId: "平台明细ID",
                matnrId: "物料标识",
                maktx: "物料名称",
                matnrCode: "物料编码",
                anfme: "计划出库数",
                stockUnit: "库存单位",
                purQty: "下单数量",
                purUnit: "单位",
                qty: "完成数量",
                splrBatch: "供应商批次",
                splrCode: "供应商编码",
                splrName: "供应商名称",
                qrcode: "二维码",
                barcode: "条形码",
                packName: "包装",
                ntyStatus: "报检状态",
                prodTime: "生产日期",
                platItemId: '行号'
            },
            asnOrderLog: {
                code: "编码",
                poCode: "PO编码",
rsf-admin/src/page/ResourceContent.js
@@ -42,6 +42,8 @@
import taskLog from './histories/taskLog';
import stock from './orders/stock';
import delivery from './orders/delivery';
import outStock from './orders/outStock';
const ResourceContent = (node) => {
    switch (node.component) {
@@ -121,6 +123,8 @@
            return stock;
        case 'delivery':
            return delivery;
        case 'outStock':
            return outStock;
        default:
            return {
                list: ListGuesser,
rsf-admin/src/page/orders/outStock/AsnOrderModal.jsx
New file
@@ -0,0 +1,643 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    CreateBase,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SaveButton,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    Toolbar,
    required,
    useDataProvider,
    useNotify,
    Form,
    useCreateController,
    useListContext,
    useRefresh,
} from 'react-admin';
import {
    Dialog,
    DialogActions,
    DialogContent,
    DialogTitle,
    Stack,
    Grid,
    TextField,
    Box,
    Button,
    Paper,
    TableContainer,
    Table,
    TableHead,
    TableBody,
    TableRow,
    TableCell,
    Tooltip,
    IconButton,
    styled,
    Select,
    MenuItem
} from '@mui/material';
import DialogCloseButton from "../../components/DialogCloseButton";
import StatusSelectInput from "../../components/StatusSelectInput";
import ConfirmButton from "../../components/ConfirmButton";
import AsnWareModal from "./AsnWareModal";
import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form";
import SaveIcon from '@mui/icons-material/Save';
import request from '@/utils/request';
import { Add, Edit, Delete } from '@mui/icons-material';
import _, { set } from 'lodash';
import { DataGrid, useGridApiRef } from '@mui/x-data-grid';
import DictionarySelect from "../../components/DictionarySelect";
import DictSelect from "../../components/DictSelect";
import "./asnOrder.css";
const AsnOrderModal = (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
        }));
        console.log(formData);
    };
    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(`/asnOrder/items/save`, parmas);
            if (res?.data?.code === 200) {
                setOpen(false);
                refresh();
                billReload?.current()
                resetData()
            } else {
                notify(res.data.msg);
            }
        } else {
            const parmas = {
                "orders": formData,
                "items": tabelData,
            }
            const res = await request.post(`/asnOrder/items/update`, parmas);
            if (res?.data?.code === 200) {
                setOpen(false);
                refresh();
                billReload?.current()
                resetData()
            } else {
                notify(res.data.msg);
            }
        }
        setDisabled(false)
    };
    const handleDelete = async () => {
        const res = await request.post(`/asnOrder/remove/${asnId}`);
        if (res?.data?.code === 200) {
            setOpen(false);
            refresh();
        } else {
            notify(res.data.msg);
        }
    };
    const requestGetHead = async () => {
        const res = await request.get(`/asnOrder/${asnId}`);
        if (res?.data?.code === 200) {
            setFormData(res.data.data)
        } else {
            notify(res.data.msg);
        }
    }
    const requestGetBody = async () => {
        const res = await request.post(`/asnOrderItem/page`, { 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={3}>
                                    <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={3}>
                                    <DictSelect
                                        label={translate("table.field.asnOrder.wkType")}
                                        value={formData.wkType}
                                        onChange={(e) => handleChange(e.target.value, 'wkType')}
                                        dictTypeCode="sys_business_type"
                                        required
                                    />
                                </Grid>
                                <Grid item md={3}>
                                    <TextField
                                        label={translate("table.field.asnOrder.poCode")}
                                        value={formData.poCode}
                                        onChange={(e) => handleChange(e.target.value, 'poCode')}
                                    />
                                </Grid>
                                <Grid item md={3}>
                                    <TextField
                                        label={translate("table.field.asnOrder.logisNo")}
                                        value={formData.logisNo}
                                        onChange={(e) => handleChange(e.target.value, 'logisNo')}
                                    />
                                </Grid>
                                <Grid item md={3}>
                                    {/* <TextField
                                        label={translate("table.field.asnOrder.arrTime")}
                                        value={formData.arrTime}
                                        onChange={(e) => handleChange(e.target.value, 'arrTime')}
                                    /> */}
                                    <DateInput
                                        source="arrTime"
                                        label="table.field.asnOrder.arrTime"
                                        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>
            <AsnWareModal
                open={createDialog}
                setOpen={setCreateDialog}
                data={tabelData}
                setData={setTableData}
            />
        </>
    )
}
export default AsnOrderModal;
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)
            console.log(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)
            console.log(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.asnOrderItem.maktx'),
            width: 250,
            editable: false,
        },
        {
            field: 'matnrCode',
            headerName: translate('table.field.asnOrderItem.matnrCode'),
            width: 130,
            editable: false,
        },
        {
            field: 'anfme',
            headerName: translate('table.field.asnOrderItem.anfme')+"*",
            type: 'number',
            minWidth: 100,
            flex: 1,
            editable: true,
            valueFormatter: (val) => val < 0 ? 0 : val,
            headerClassName: "custom",
        },
        {
            field: 'splrCode',
            headerName: translate('table.field.asnOrderItem.splrCode')+"*",
            minWidth: 100,
            flex: 1,
            editable: true,
            renderEditCell: (params) => (
                <SelectInputSplrCodeEditCell {...params} />
            ),
            headerClassName: "custom",
        },
        {
            field: 'splrName',
            headerName: translate('table.field.asnOrderItem.splrName')+"*",
            minWidth: 100,
            flex: 1,
            editable: true,
            renderEditCell: (params) => (
                <SelectInputSplrNameEditCell {...params} />
            ),
            headerClassName: "custom",
        },
        // {
        //     field: 'packName',
        //     headerName: translate('table.field.asnOrderItem.packName'),
        //     minWidth: 100,
        //     flex: 1,
        //     editable: true,
        // },
        // {
        //     field: 'poDetlId',
        //     headerName: translate('table.field.asnOrderItem.poDetlId'),
        //     minWidth: 100,
        //     flex: 1,
        // },
        {
            field: 'poCode',
            headerName: translate('table.field.asnOrderItem.poDetlCode')+"*",
            minWidth: 100,
            flex: 1,
            editable: true,
            headerClassName: "custom",
        },
        {
            field: 'stockUnit',
            headerName: translate('table.field.asnOrderItem.stockUnit'),
            minWidth: 100,
            flex: 1,
            editable: false,
        },
        // {
        //     field: 'purQty',
        //     headerName: translate('table.field.asnOrderItem.purQty'),
        //     minWidth: 100,
        //     flex: 1,
        //     editable: true,
        // },
        {
            field: 'purUnit',
            headerName: translate('table.field.asnOrderItem.purUnit'),
            minWidth: 100,
            flex: 1,
            editable: false,
        },
    ])
    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: false
            }))
            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)
        // setTableData((prevData) =>
        //     prevData.map((r) =>
        //         r.matnrId === newRow.matnrId ? { ...newRow } : r
        //     )
        // );
        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}
                disableColumnFilter
                disableColumnSelector
                disableColumnSorting
                disableMultipleColumnsSorting
                processRowUpdate={processRowUpdate}
                initialState={{
                    pagination: {
                        paginationModel: {
                            pageSize: 25,
                        },
                    },
                }}
                pageSizeOptions={[10, 25, 50, 100]}
                editMode="row"
                checkboxSelection
                onRowSelectionModelChange={handleSelectionChange}
                selectionModel={selectedRows}
                sx={{
                    '& .MuiDataGrid-cell input': {
                        border: '1px solid #ccc'
                    },
                }}
            />
        </div>
    );
};
rsf-admin/src/page/orders/outStock/AsnOrderPanel.jsx
New file
@@ -0,0 +1,239 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { Box, Card, CardContent, Grid, Typography, Button, TextField, Tooltip, Paper, TableContainer, Table, TableBody, TableCell, TableHead, TableRow } from '@mui/material';
import {
    useTranslate,
    useRecordContext,
    useNotify,
    useRefresh,
    useListContext,
} from 'react-admin';
import PanelTypography from "../../components/PanelTypography";
import * as Common from '@/utils/common'
import { styled } from "@mui/material/styles";
import request from '@/utils/request';
import debounce from 'lodash/debounce';
import { DataGrid } from '@mui/x-data-grid';
import PrintModal from './PrintModal';
import PrintIcon from '@mui/icons-material/Print';
const AsnOrderPanel = ({ billReload }) => {
    const record = useRecordContext();
    if (!record) return null;
    const translate = useTranslate();
    const notify = useNotify();
    const [rows, setRows] = useState([]);
    const [maktx, setMaktx] = useState('');
    const asnId = record.id;
    useEffect(() => {
        debouncedHttp({ maktx });
    }, [asnId, maktx]);
    const http = async (parmas) => {
        const res = await request.post('/asnOrderItem/page', { ...parmas, asnId });
        if (res?.data?.code === 200) {
            setRows(res.data.data.records)
        } else {
            notify(res.data.msg);
        }
    }
    useEffect(() => {
        billReload.current = http
    }, []);
    const debouncedHttp = useMemo(() => debounce(http, 300), []);
    const columns = [
        {
            field: 'asnId',
            headerName: translate('table.field.asnOrderItem.asnId')
        },
        {
            field: 'asnCode',
            headerName: translate('table.field.asnOrderItem.asnCode'),
            width: 150,
        },
        // {
        //     field: 'poDetlId',
        //     headerName: translate('table.field.asnOrderItem.poDetlId')
        // },
        {
            field: 'poCode',
            headerName: translate('table.field.asnOrderItem.poDetlCode')
        },
        {
            field: 'matnrCode',
            headerName: translate('table.field.asnOrderItem.matnrCode'),
            width: 150,
        },
        {
            field: 'maktx',
            headerName: translate('table.field.asnOrderItem.maktx'),
            width: 200,
        },
        {
            field: 'anfme',
            headerName: translate('table.field.asnOrderItem.purQty')
        },
        {
            field: 'stockUnit',
            headerName: translate('table.field.asnOrderItem.stockUnit')
        },
        // {
        //     field: 'purQty',
        //     headerName: translate('table.field.asnOrderItem.purQty')
        // },
        {
            field: 'purUnit',
            headerName: translate('table.field.asnOrderItem.purUnit')
        },
        {
            field: 'qty',
            headerName: translate('table.field.asnOrderItem.qty')
        },
        {
            field: 'splrBatch',
            headerName: translate('table.field.asnOrderItem.splrBatch')
        },
        {
            field: 'splrCode',
            headerName: translate('table.field.asnOrderItem.splrCode')
        },
        {
            field: 'splrName',
            headerName: translate('table.field.asnOrderItem.splrName')
        },
        {
            field: 'trackCode',
            headerName: translate('table.field.asnOrderItem.barcode'),
            width: 150
        },
        {
            field: 'prodTime',
            headerName: translate('table.field.asnOrderItem.prodTime')
        },
        {
            field: 'packName',
            headerName: translate('table.field.asnOrderItem.packName')
        },
        {
            field: 'action',
            headerName: '操作',
            width: 70,
            lockPosition: 'left',
            renderCell: (params) => (
                <PrintButton rows={[params.row.id]} />
            ),
        },]
    const [selectedRows, setSelectedRows] = useState([]);
    const handleSelectionChange = (ids) => {
        setSelectedRows(ids)
    };
    const maktxChange = (value) => {
        setMaktx(value)
    }
    return (
        <Box sx={{
            position: 'relative',
            padding: '5px 10px'
        }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px', alignItems: 'center' }}>
                <TextField value={maktx} onChange={(e) => maktxChange(e.target.value)} label="搜索物料" sx={{ width: '300px' }} size="small" />
                <div style={{ display: 'flex', gap: '10px' }}>
                    <PrintsButton rows={selectedRows} />
                </div>
            </div>
            <DataGrid
                sx={{ width: 'calc(100vw - 280px)' }}
                size="small"
                rows={rows}
                columns={columns}
                disableRowSelectionOnClick
                checkboxSelection
                onRowSelectionModelChange={handleSelectionChange}
                selectionModel={selectedRows}
                disableColumnMenu={true}
                disableColumnSorting
                disableMultipleColumnsSorting
                columnHeaderHeight={40}
                rowHeight={42}
                initialState={{
                    pagination: {
                        paginationModel: {
                            pageSize: 10,
                        },
                    },
                }}
                pageSizeOptions={[10, 25, 50]}
            />
        </Box >
    );
};
export default AsnOrderPanel;
const PrintsButton = ({ rows }) => {
    const record = useRecordContext();
    const { resource, selectedIds } = useListContext();
    const notify = useNotify();
    const refresh = useRefresh();
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const modalChange = () => {
        if (rows?.length === 0) {
            notify('请选择物料');
            return;
        } else {
            setCreateDialog(true)
        }
    }
    return (
        <>
            <Button size="small" color="secondary" onClick={modalChange} startIcon={<PrintIcon />}>{translate("toolbar.batchPrint")}</Button>
            <PrintModal
                open={createDialog}
                setOpen={setCreateDialog}
                rows={rows}
            />
        </>
    )
}
const PrintButton = ({ rows }) => {
    const record = useRecordContext();
    const notify = useNotify();
    const refresh = useRefresh();
    const [createDialog, setCreateDialog] = useState(false);
    const translate = useTranslate();
    return (
        <>
            <Button size="small" color="secondary" onClick={() => setCreateDialog(true)} startIcon={<PrintIcon />}>{translate("toolbar.print")}</Button>
            <PrintModal
                open={createDialog}
                setOpen={setCreateDialog}
                rows={rows}
            />
        </>
    )
}
rsf-admin/src/page/orders/outStock/AsnWareModal.jsx
New file
@@ -0,0 +1,249 @@
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 { 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 AsnWareModal = (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 handleChange = (e) => {
        const { name, value } = e.target;
        setFormData(() => ({
            [name]: value
        }));
    };
    const reset = () => {
        setFormData({
            name: '',
            code: '',
            groupId: 0
        })
    }
    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 () => {
        const res = await request.post(`/matnr/page`, {
            ...formData,
            current: 1,
            pageSize: 100,
            orderBy: "create_time desc"
        });
        if (res?.data?.code === 200) {
            setTableData(res.data.data.records);
        } else {
            notify(res.data.msg);
        }
    };
    useEffect(() => {
        getData();
    }, [open]);
    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
            }}>
                选择物料
                <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}
                        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 AsnWareModal;
const AsnWareModalTable = ({ tableData, setTableData, 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
            />
        </div>
    );
};
rsf-admin/src/page/orders/outStock/OutOrderCreate.jsx
New file
@@ -0,0 +1,184 @@
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 OutOrderCreate = (props) => {
    const { open, setOpen } = props;
    const translate = useTranslate();
    const notify = useNotify();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
    };
    return (
        <>
            <CreateBase
                record={{}}
                transform={(data) => {
                    return data;
                }}
                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
            >
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                        }}
                        >
                            {translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
                        <DialogContent sx={{ mt: 2 }}>
                            <Grid container rowSpacing={2} columnSpacing={2}>
                                {/* <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.asnOrder.code"
                                        source="code"
                                        parse={v => v}
                                        autoFocus
                                    />
                                </Grid> */}
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.asnOrder.poCode"
                                        source="poCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.asnOrder.poId"
                                        source="poId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.asnOrder.type"
                                        source="type"
                                        parse={v => v}
                                        validate={required()}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.asnOrder.wkType"
                                        source="wkType"
                                        parse={v => v}
                                        validate={required()}
                                    />
                                </Grid>
                                {/* <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.asnOrder.anfme"
                                        source="anfme"
                                        validate={required()}
                                    />
                                </Grid> */}
                                {/* <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.asnOrder.qty"
                                        source="qty"
                                        validate={required()}
                                    />
                                </Grid> */}
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.asnOrder.logisNo"
                                        source="logisNo"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <DateInput
                                        label="table.field.asnOrder.arrTime"
                                        source="arrTime"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <SelectInput
                                        label="table.field.asnOrder.rleStatus"
                                        source="rleStatus"
                                        choices={[
                                            { id: 0, name: ' 正常' },
                                            { id: 1, name: ' 已释放' },
                                        ]}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <StatusSelectInput />
                                </Grid>
                                <Grid item xs={12} display="flex" gap={1}>
                                    <Stack direction="column" spacing={1} width={'100%'}>
                                        <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 OutOrderCreate;
rsf-admin/src/page/orders/outStock/OutOrderEdit.jsx
New file
@@ -0,0 +1,150 @@
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 AsnOrderItemList from "./AsnOrderItemList";
const FormToolbar = () => {
    const { getValues } = useFormContext();
    return (
        <Toolbar sx={{ justifyContent: 'end' }}>
            <></>
            {/* <SaveButton />
            <DeleteButton mutationMode="optimistic" /> */}
        </Toolbar>
    )
}
const OutOrderEdit = () => {
    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={<FormToolbar />}
                    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.asnOrder.code"
                                    source="code"
                                    readOnly
                                    parse={v => v}
                                />
                                <TextInput
                                    label="table.field.asnOrder.poCode"
                                    source="poCode"
                                    readOnly
                                    parse={v => v}
                                />
                                <AutocompleteInput
                                    choices={dicts}
                                    optionText="label"
                                    label="table.field.asnOrder.type"
                                    source="type"
                                    optionValue="value"
                                    parse={v => v}
                                    readOnly
                                />
                                <AutocompleteInput
                                    choices={business}
                                    optionText="label"
                                    label="table.field.asnOrder.wkType"
                                    source="wkType"
                                    optionValue="value"
                                    parse={v => v}
                                    readOnly
                                />
                            </Stack>
                            <Stack direction='row' gap={2}>
                                <TextInput
                                    label="table.field.asnOrder.logisNo"
                                    source="logisNo"
                                    readOnly
                                    parse={v => v}
                                />
                                <TextInput
                                    label="table.field.asnOrder.anfme"
                                    source="anfme"
                                    readOnly
                                    parse={v => v}
                                />
                                <TextInput
                                    label="table.field.asnOrder.qty"
                                    source="qty"
                                    readOnly
                                    parse={v => v}
                                />
                                <DateInput
                                    label="table.field.asnOrder.arrTime"
                                    source="arrTime"
                                    readOnly
                                />
                                <SelectInput
                                    label="table.field.asnOrder.rleStatus"
                                    source="rleStatus"
                                    readOnly
                                    choices={[
                                        { id: 0, name: ' 正常' },
                                        { id: 1, name: ' 已释放' },
                                    ]}
                                    validate={required()}
                                />
                            </Stack>
                        </Grid>
                    </Grid>
                </SimpleForm>
            </Edit >
            <AsnOrderItemList />
        </>
    )
}
export default OutOrderEdit;
rsf-admin/src/page/orders/outStock/OutOrderItemCreate.jsx
New file
@@ -0,0 +1,204 @@
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="asnOrderItem"
                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.asnOrderItem.asnId"
                                        source="asnId"
                                        readOnly
                                        hidden
                                        defaultValue={record?.id}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.asnCode"
                                        source="asnCode"
                                        readOnly
                                        defaultValue={record?.code}
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={2}>
                                    <TextInput
                                        label="table.field.asnOrderItem.poDetlId"
                                        source="poDetlId"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.poDetlCode"
                                        source="poDetlCode"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.matnrId"
                                        source="matnrId"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.asnOrderItem.maktx"
                                        source="maktx"
                                        parse={v => v}
                                    />
                                    <NumberInput
                                        label="table.field.asnOrderItem.anfme"
                                        source="anfme"
                                        validate={required()}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.stockUnit"
                                        source="stockUnit"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.asnOrderItem.purQty"
                                        source="purQty"
                                        validate={required()}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.purUnit"
                                        source="purUnit"
                                        parse={v => v}
                                    />
                                    <NumberInput
                                        label="table.field.asnOrderItem.qty"
                                        source="qty"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.asnOrderItem.splrCode"
                                        source="splrCode"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.splrName"
                                        source="splrName"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.qrcode"
                                        source="qrcode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.asnOrderItem.barcode"
                                        source="barcode"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.packName"
                                        source="packName"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <StatusSelectInput />
                                </Grid>
                                <Grid item xs={12} display="flex" gap={1}>
                                    <Stack direction="column" spacing={1} width={'100%'}>
                                        <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/outStock/OutOrderItemEdit.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('asnOrderItem', { 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="asnOrderItem"
                    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.asnOrderItem.asnId"
                                        source="asnId"
                                        readOnly
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.asnCode"
                                        source="asnCode"
                                        readOnly
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.poDetlId"
                                        source="poDetlId"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.poDetlCode"
                                        source="poDetlCode"
                                        parse={v => v}
                                    />
                                </Stack>
                                <Stack direction='row' gap={2}>
                                    <TextInput
                                        label="table.field.asnOrderItem.matnrId"
                                        source="matnrId"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.maktx"
                                        source="maktx"
                                        parse={v => v}
                                    />
                                    <NumberInput
                                        label="table.field.asnOrderItem.anfme"
                                        source="anfme"
                                        validate={required()}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.stockUnit"
                                        source="stockUnit"
                                        parse={v => v}
                                    />
                                </Stack>
                                <Stack direction='row' gap={2}>
                                    <NumberInput
                                        label="table.field.asnOrderItem.purQty"
                                        source="purQty"
                                        validate={required()}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.purUnit"
                                        source="purUnit"
                                        parse={v => v}
                                    />
                                    <NumberInput
                                        label="table.field.asnOrderItem.qty"
                                        source="qty"
                                        readOnly
                                    />
                                    <ReferenceInput source="splrName" label="table.field.asnOrderItem.splrName" reference="companys" filter={{type: 'supplier'}}>
                                        <AutocompleteInput optionText="name" label="table.field.asnOrderItem.splrName" />
                                    </ReferenceInput>
                                </Stack>
                                <Stack direction='row' gap={2}>
                                    <TextInput
                                        label="table.field.asnOrderItem.qrcode"
                                        source="qrcode"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.barcode"
                                        source="trackCode"
                                        parse={v => v}
                                        readOnly
                                    />
                                    <TextInput
                                        label="table.field.asnOrderItem.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/outStock/OutOrderItemList.jsx
New file
@@ -0,0 +1,189 @@
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,
  useGetOne
} from 'react-admin';
import { Box, Typography, Card, Stack, Dialog, DialogActions, DialogTitle } from '@mui/material';
import { styled } from '@mui/material/styles';
import OutOrderItemCreate from "./OutOrderItemCreate";
import EmptyData from "../../components/EmptyData";
import MyCreateButton from "../../components/MyCreateButton";
import MyExportButton from '../../components/MyExportButton';
import PageDrawer from "../../components/PageDrawer";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE, DEFAULT_ITEM_PAGE_SIZE } from '@/config/setting';
import OutOrderItemEdit from "./OutOrderItemEdit";
import ImportButton from "../../components/ImportButton";
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
  '& .css-1vooibu-MuiSvgIcon-root': {
    height: '.9em',
  },
  '& .RaDatagrid-row': {
    cursor: 'auto'
  },
  '& .column-name': {
  },
  '& .opt': {
    width: 200
  },
}));
const filters = [
  <SearchInput source="condition" alwaysOn />,
  <NumberInput source="asnId" label="table.field.asnOrderItem.asnId" />,
  <TextInput source="asnCode" label="table.field.asnOrderItem.asnCode" />,
  <TextInput source="poDetlId" label="table.field.asnOrderItem.poDetlId" />,
  <TextInput source="poDetlCode" label="table.field.asnOrderItem.poDetlCode" />,
  <TextInput source="matnrId" label="table.field.asnOrderItem.matnrId" />,
  <TextInput source="maktx" label="table.field.asnOrderItem.maktx" />,
  <NumberInput source="anfme" label="table.field.asnOrderItem.anfme" />,
  <TextInput source="stockUnit" label="table.field.asnOrderItem.stockUnit" />,
  <NumberInput source="purQty" label="table.field.asnOrderItem.purQty" />,
  <TextInput source="purUnit" label="table.field.asnOrderItem.purUnit" />,
  <NumberInput source="qty" label="table.field.asnOrderItem.qty" />,
  <TextInput source="splrCode" label="table.field.asnOrderItem.splrCode" />,
  <TextInput source="splrName" label="table.field.asnOrderItem.splrName" />,
  <TextInput source="qrcode" label="table.field.asnOrderItem.qrcode" />,
  <TextInput source="trackCode" label="table.field.asnOrderItem.barcode" />,
  <TextInput source="packName" label="table.field.asnOrderItem.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('asnOrder', { id: asnId });
  return (
    <>
      <Box display="flex">
        <List
          resource="asnOrderItem"
          sx={{
            flexGrow: 1,
            transition: (theme) =>
              theme.transitions.create(['all'], {
                duration: theme.transitions.duration.enteringScreen,
              }),
            marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
          }}
          title={"menu.asnOrderItem"}
          empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
          filter={{ asnId: asnId, deleted: 0 }}
          filters={filters}
          sort={{ field: "create_time", order: "desc" }}
          actions={(
            <TopToolbar>
              <FilterButton />
              <MyCreateButton onClick={() => { setCreateDialog(true) }} />
              <SelectColumnsButton preferenceKey='asnOrderItem' />
              {/* <MyExportButton /> */}
            </TopToolbar>
          )}
          perPage={DEFAULT_ITEM_PAGE_SIZE}
        >
          <StyledDatagrid
            preferenceKey='asnOrderItem'
            bulkActionButtons={false}
            rowClick={(id, resource, record) => {
              setSelect(record)
              setEditDialog(true)
            }}
             omit={['id', 'createTime', 'createBy', 'memo', 'poDetlId', 'matnrId', 'asnId']}
          >
            <NumberField source="id" />
            <NumberField source="asnId" label="table.field.asnOrderItem.asnId" />
            <TextField source="asnCode" label="table.field.asnOrderItem.asnCode" />
            <TextField source="poDetlId" label="table.field.asnOrderItem.poDetlId" />
            <TextField source="poDetlCode" label="table.field.asnOrderItem.poDetlCode" />
            <TextField source="matnrId" label="table.field.asnOrderItem.matnrId" />
            <TextField source="matnrCode" label="table.field.asnOrderItem.matnrCode" />
            <TextField source="maktx" label="table.field.asnOrderItem.maktx" />
            <NumberField source="anfme" label="table.field.asnOrderItem.anfme" />
            <TextField source="stockUnit" label="table.field.asnOrderItem.stockUnit" />
            <NumberField source="purQty" label="table.field.asnOrderItem.purQty" />
            <TextField source="purUnit" label="table.field.asnOrderItem.purUnit" />
            <NumberField source="qty" label="table.field.asnOrderItem.qty" />
            <TextField source="splrCode" label="table.field.asnOrderItem.splrCode" />
            <TextField source="splrName" label="table.field.asnOrderItem.splrName" />
            <TextField source="qrcode" label="table.field.asnOrderItem.qrcode" />
            <TextField source="trackCode" label="table.field.asnOrderItem.barcode" />
            <TextField source="packName" label="table.field.asnOrderItem.packName" />
            <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="status$" label="common.field.status" sortable={false} />
            <TextField source="memo" label="common.field.memo" sortable={false} />
          </StyledDatagrid>
        </List>
        <OutOrderItemCreate
          open={createDialog}
          setOpen={setCreateDialog}
          record={dicts}
        />
        <OutOrderItemEdit
          open={editDialog}
          setOpen={setEditDialog}
          record={select}
        />
        <PageDrawer
          title='AsnOrderItem Detail'
          drawerVal={drawerVal}
          setDrawerVal={setDrawerVal}
        >
        </PageDrawer>
      </Box>
    </>
  )
}
OutOrderItemList.Context = React.createContext()
export default OutOrderItemList;
rsf-admin/src/page/orders/outStock/OutOrderList.jsx
New file
@@ -0,0 +1,263 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useLocation, useNavigate } from 'react-router-dom';
import {
  List,
  DatagridConfigurable,
  SearchInput,
  TopToolbar,
  SelectColumnsButton,
  EditButton,
  FilterButton,
  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,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import AsnOrderModal from "./AsnOrderModal";
import EmptyData from "../../components/EmptyData";
import MyCreateButton from "../../components/MyCreateButton";
import MyExportButton from '../../components/MyExportButton';
import BillStatusField from '../../components/BillStatusField';
import ConfirmButton from '../../components/ConfirmButton';
import PageDrawer from "../../components/PageDrawer";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import ConstructionIcon from "@mui/icons-material/Construction";
import EditIcon from '@mui/icons-material/Edit';
import TaskIcon from '@mui/icons-material/Task';
import CloseIcon from '@mui/icons-material/Close';
import request from '@/utils/request';
import DictionarySelect from "../../components/DictionarySelect";
import ExitToAppIcon from '@mui/icons-material/ExitToApp';
import ImportButton from "../../components/ImportButton";
import DetailsIcon from '@mui/icons-material/Details';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
  '& .css-1vooibu-MuiSvgIcon-root': {
    height: '.9em'
  },
  '& .RaDatagrid-row': {
    cursor: 'auto'
  },
  '& .column-name': {
  },
  '& .opt': {
    width: 220
  },
  '& .wkType': {
    width: 110
  },
  '& .status': {
    width: 90
  },
}));
const filters = [
  <SearchInput source="condition" alwaysOn />,
  <TextInput source="code" label="table.field.asnOrder.code" />,
  <TextInput source="poCode" label="table.field.asnOrder.poCode" />,
  <NumberInput source="poId" label="table.field.asnOrder.poId" />,
  <TextInput source="type" label="table.field.asnOrder.type" />,
  <ReferenceInput source="wkType" reference="dictData" filter={{ dictTypeCode: 'sys_business_type' }} label="table.field.asnOrder.wkType">
    <AutocompleteInput label="table.field.asnOrder.wkType" optionValue="value" />
  </ReferenceInput>,
  <NumberInput source="anfme" label="table.field.asnOrder.anfme" />,
  <NumberInput source="qty" label="table.field.asnOrder.qty" />,
  <TextInput source="logisNo" label="table.field.asnOrder.logisNo" />,
  <DateInput source="arrTime" label="table.field.asnOrder.arrTime" />,
  <SelectInput source="rleStatus" label="table.field.asnOrder.rleStatus"
    choices={[
      { id: 0, name: ' 正常' },
      { id: 1, name: ' 已释放' },
    ]}
  />,
  <TextInput label="common.field.memo" source="memo" />,
  <DictionarySelect
    label='table.field.asnOrder.exceStatus'
    name="exceStatus"
    dictTypeCode="sys_asn_exce_status"
    alwaysOn
  />,
]
const OutOrderList = (props) => {
  const translate = useTranslate();
  const [createDialog, setCreateDialog] = useState(false);
  const [drawerVal, setDrawerVal] = useState(false);
  const [modalType, setmodalType] = useState(0);
  const [select, setSelect] = useState(0);
  const billReload = useRef();
  const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_business_type')) || [];
  return (
    <Box display="flex">
      <List
        resource="outStock"
        storeKey='outStock'
        sx={{
          flexGrow: 1,
          transition: (theme) =>
            theme.transitions.create(['all'], {
              duration: theme.transitions.duration.enteringScreen,
            }),
          marginRight: drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
        }}
        title={"menu.outStock"}
        empty={<EmptyData onClick={() => { setCreateDialog(true); setmodalType(0) }} />}
        filters={filters}
        filter={{deleted: 0, type: 'out'}}
        sort={{ field: "create_time", order: "desc" }}
        actions={(
          <TopToolbar>
            <FilterButton />
            <MyCreateButton onClick={() => { setCreateDialog(true); setmodalType(0) }} />
            <SelectColumnsButton preferenceKey='outStock' />
            <ImportButton value={'asnOrderItem'}  />
            <MyExportButton />
          </TopToolbar>
        )}
        perPage={DEFAULT_PAGE_SIZE}
      >
        <StyledDatagrid
          sx={{ width: '100%' }}
          preferenceKey='outStock'
          bulkActionButtons={
            <>
              <MyExportButton />
              {/* <BtnBulkExport></BtnBulkExport> */}
              <BulkDeleteButton mutationMode={OPERATE_MODE}
              />
            </>}
          rowClick={false}
          expandSingle={true}
          omit={['id', 'createTime', 'createBy', 'memo', 'poId', 'rleStatus$']}
        >
          <NumberField source="id" />
          <TextField source="code" label="table.field.outStock.code" />
          <TextField source="poCode" label="table.field.outStock.poCode" />
          <NumberField source="poId" label="table.field.outStock.poId" />
          <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="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 />
          <BillStatusField cellClassName="status" source="exceStatus" label="table.field.outStock.exceStatus" />
          <TextField source="memo" label="common.field.memo" sortable={false} />
          <WrapperField cellClassName="opt" label="common.field.opt" >
            <EditButton label="toolbar.detail" icon={(<DetailsIcon />)}></EditButton>
            <MyButton setCreateDialog={setCreateDialog} setmodalType={setmodalType} />
            {/* <CompleteButton /> */}
          </WrapperField>
        </StyledDatagrid>
      </List>
      <AsnOrderModal
        open={createDialog}
        setOpen={setCreateDialog}
        asnId={modalType}
        billReload={billReload}
      />
      <PageDrawer
        title='AsnOrder Detail'
        drawerVal={drawerVal}
        setDrawerVal={setDrawerVal}
      >
      </PageDrawer>
    </Box >
  )
}
export default OutOrderList;
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 CompleteButton = () => {
  const record = useRecordContext();
  const notify = useNotify();
  const refresh = useRefresh();
  const requestComplete = async () => {
    const { data: { code, data, msg } } = await request.post(`/asnOrder/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="success">
      <TaskIcon />
    </Button> : <ConfirmButton label={"toolbar.complete"} color="success" data={'当前收货数量小于计划数量,是否确认完成'} startIcon={<TaskIcon />} onConfirm={requestComplete} />)
  )
}
const CloseButton = () => {
  const record = useRecordContext();
  const notify = useNotify();
  const refresh = useRefresh();
  const requestClose = async () => {
    const { data: { code, data, msg } } = await request.post(`/asnOrder/close/${record.id}`);
    if (code === 200) {
      notify(msg);
      refresh()
    } else {
      notify(msg);
    }
  }
  return (
    <ConfirmButton label={"toolbar.close"} color="error" data={'确认是否关闭?'} startIcon={<CloseIcon />} onConfirm={requestClose} />
  )
}
rsf-admin/src/page/orders/outStock/PrintModal.jsx
New file
@@ -0,0 +1,310 @@
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,
    Grid,
    TextField,
    Box,
    Button,
    Radio,
    RadioGroup,
    FormControlLabel,
    FormControl,
    FormLabel,
    TableRow,
    TableCell,
    Tooltip,
    IconButton,
    styled
} from '@mui/material';
import DialogCloseButton from "@/page/components/DialogCloseButton";
import DictionarySelect from "@/page/components/DictionarySelect";
import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form";
import SaveIcon from '@mui/icons-material/Save';
import request from '@/utils/request';
import { Add, Edit, Delete } from '@mui/icons-material';
import _ from 'lodash';
import { DataGrid } from '@mui/x-data-grid';
import StatusSelectInput from "@/page/components/StatusSelectInput";
import { useReactToPrint } from "react-to-print";
import jsbarcode from 'jsbarcode'
import { el } from "date-fns/locale";
const PrintModal = ({ open, setOpen, rows }) => {
    const refresh = useRefresh();
    const translate = useTranslate();
    const notify = useNotify();
    const contentRef = useRef(null);
    const reactToPrintFn = useReactToPrint({ contentRef });
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const [value, setValue] = useState('temp1');
    const handleChange = (event) => {
        setValue(event.target.value);
    };
    const handlePrint = () => {
        // handleClose()
        reactToPrintFn()
    };
    return (
        <Dialog open={open} maxWidth="sm" fullWidth>
            <DialogCloseButton onClose={handleClose} />
            <DialogTitle>{translate('toolbar.print')}</DialogTitle>
            <DialogContent >
                <FormControl >
                    <RadioGroup
                        row
                        aria-labelledby="demo-controlled-radio-buttons-group"
                        name="controlled-radio-buttons-group"
                        value={value}
                        onChange={handleChange}
                        size="small"
                        sx={{ justifyContent: 'center' }}
                    >
                        <FormControlLabel value="temp1" control={<Radio />} label="模板1" size="small" />
                    </RadioGroup>
                </FormControl>
                <Box>
                    <div style={{ textAlign: 'center', display: 'flex', justifyContent: 'center' }}>
                        <table
                            className="contain"
                            style={{
                                overflow: 'hidden',
                                fontSize: 'small',
                                tableLayout: 'fixed',
                                width: '280px',
                                borderCollapse: 'collapse', // 合并边框
                                border: '1px solid black' // 设置表格整体边框
                            }}
                        >
                            <tbody>
                                <tr style={{ height: '74px' }}>
                                    <td
                                        align="center"
                                        colSpan={3}
                                        style={{ border: '1px solid black' }} // 设置单元格边框
                                    >
                                        商品编码
                                    </td>
                                    <td
                                        align="center"
                                        className="barcode"
                                        colSpan={9}
                                        style={{ border: '1px solid black' }}
                                    >
                                        <img className="template-code" src={'/img/barcode.jpeg'} style={{ width: '90%' }} alt="Barcode" />
                                        <div style={{ letterSpacing: '2px', marginTop: '1px', textAlign: 'center' }}>
                                            <span>{'xxxxxx'}</span>
                                        </div>
                                    </td>
                                </tr>
                                <tr style={{ height: '74px' }}>
                                    <td
                                        align="center"
                                        colSpan={3}
                                        style={{ border: '1px solid black' }}
                                    >
                                        商品
                                    </td>
                                    <td
                                        align="center"
                                        colSpan={5}
                                        style={{
                                            overflow: 'hidden',
                                            whiteSpace: 'nowrap',
                                            textOverflow: 'ellipsis',
                                            border: '1px solid black'
                                        }}
                                    >
                                        {'xxxxxxxx'}
                                    </td>
                                    <td
                                        align="center"
                                        colSpan={2}
                                        style={{ border: '1px solid black' }}
                                    >
                                        备注
                                    </td>
                                    <td
                                        align="center"
                                        colSpan={2}
                                        style={{ border: '1px solid black' }}
                                    >
                                        {'xx'}
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                    <style>{`
                    @media print {
                    .print-content {
                            display: block!important;
                        }
                    }`} </style>
                    <div ref={contentRef} className="print-content" style={{ textAlign: 'center', display: 'none' }}>
                        <PrintTemp key={'bb'} rows={rows} />
                    </div>
                </Box>
            </DialogContent>
            <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}>
                <Box sx={{ width: '100%', display: 'flex', justifyContent: 'space-between' }}>
                    <Button onClick={handlePrint} variant="contained" startIcon={<SaveIcon />}>
                        {translate('toolbar.confirm')}
                    </Button>
                </Box>
            </DialogActions>
        </Dialog >
    );
}
export default PrintModal;
const PrintTemp = ({ rows }) => {
    const notify = useNotify();
    const [data, setData] = useState([]);
    const http = async () => {
        const res = await request.post(`/asnOrderItem/many/${rows?.join()}`);
        if (res?.data?.code === 200) {
            let val = res.data.data.map((el => {
                return {
                    barcode: '/img/barcode.jpeg',
                    code: el.trackCode,
                    name: el.maktx,
                    memo: el.memo || ''
                }
            }))
            setData(val)
            setTimeout(() => {
                val.forEach((el) => {
                    jsbarcode(`#barcode${el.code}`, el.code, { height: 30 });
                });
            }, 10);
        } else {
            notify(res.data.msg);
        }
    }
    useEffect(() => {
        if (rows?.length > 0) {
            http();
        }
    }, [rows]);
    return (
        <>
            {data.map((item, index) => (
                <table
                    key={index}
                    className="contain"
                    style={{
                        overflow: 'hidden',
                        fontSize: 'small',
                        tableLayout: 'fixed',
                        width: '520px',
                        borderCollapse: 'collapse',
                        borderSpacing: 0,
                        margin: '0 auto',
                        marginTop: '10px',
                    }}
                >
                    <tbody>
                        <tr style={{ height: '74px' }}>
                            <td align="center" colSpan={3} style={{ border: '1px solid black' }} >
                                商品编码
                            </td>
                            <td
                                align="center"
                                className="barcode"
                                colSpan={9}
                                style={{ border: '1px solid black' }}
                            >
                                <img id={"barcode" + item.code} style={{ width: '70%', verticalAlign: 'middle' }} />
                                {/* <img className="template-code" src={item.barcode} style={{ width: '90%', verticalAlign: 'middle' }} alt="Barcode" /> */}
                                {/* <div style={{ letterSpacing: '2px', marginTop: '1px', textAlign: 'center' }}>
                                    <span>{item.code}</span>
                                </div> */}
                            </td>
                        </tr>
                        <tr style={{ height: '74px' }}>
                            <td
                                align="center"
                                colSpan={3}
                                style={{ border: '1px solid black' }}
                            >
                                商品
                            </td>
                            <td
                                align="center"
                                colSpan={5}
                                style={{
                                    overflow: 'hidden',
                                    whiteSpace: 'nowrap',
                                    textOverflow: 'ellipsis',
                                    border: '1px solid black'
                                }}
                            >
                                {item.name}
                            </td>
                            <td
                                align="center"
                                colSpan={2}
                                style={{ border: '1px solid black' }}
                            >
                                备注
                            </td>
                            <td
                                align="center"
                                colSpan={2}
                                style={{ border: '1px solid black' }}
                            >
                                {item.memo}
                            </td>
                        </tr>
                    </tbody>
                </table>
            ))}
        </>
    )
}
rsf-admin/src/page/orders/outStock/asnOrder.css
New file
@@ -0,0 +1,5 @@
.custom {
    color: rgb(0, 195, 255) !important;
  }
rsf-admin/src/page/orders/outStock/index.jsx
New file
@@ -0,0 +1,18 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    ListGuesser,
    EditGuesser,
    ShowGuesser,
} from "react-admin";
import OutOrderList from "./OutOrderList";
import OutOrderEdit from "./OutOrderEdit";
export default {
    list: OutOrderList,
    edit: OutOrderEdit,
    show: ShowGuesser,
    recordRepresentation: (record) => {
        return `${record.name}`
    }
};
rsf-admin/src/page/system/serialRule/SerialRuleEdit.jsx
@@ -61,8 +61,8 @@
                    defaultValues={{}}
                // validate={(values) => { }}
                >
                    <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
                        <Grid item xs={12} md={8}>
                    <Grid container width={{ xs: '100%', xl: '90%' }} rowSpacing={3} columnSpacing={3}>
                        <Grid item xs={16} md={10}>
                            <Typography variant="h6" gutterBottom>
                                {translate('common.edit.title.main')}
                            </Typography>
@@ -109,9 +109,8 @@
                                    parse={v => v}
                                />
                            </Stack>
                        </Grid>
                        <Grid item xs={12} md={4}>
                        <Grid item xs={8} md={2}>
                            <Typography variant="h6" gutterBottom>
                                {translate('common.edit.title.common')}
                            </Typography>
rsf-admin/src/page/system/serialRule/SerialRuleItemList.jsx
@@ -142,7 +142,7 @@
              setSelect(record)
              setEditDialog(true)
            }}
            omit={["id", "createTime", "createBy", "memo"]}
            omit={["id", "ruleId", "createTime", "createBy", "memo"]}
          >
            <NumberField source="id" />
            <NumberField
@@ -196,7 +196,7 @@
            />
            <WrapperField cellClassName="opt" label="common.field.opt">
              <Button onClick={() => {
                setSelect(record)
                // setSelect(record)
                setEditDialog(true)
              }} label={'ra.action.edit'}
              > </Button>
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutOrderItemController.java
New file
@@ -0,0 +1,185 @@
package com.vincent.rsf.server.manager.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.manager.entity.AsnOrderItem;
import com.vincent.rsf.server.manager.entity.Companys;
import com.vincent.rsf.server.manager.entity.excel.AsnOrderTemplate;
import com.vincent.rsf.server.manager.enums.CompanysType;
import com.vincent.rsf.server.manager.service.AsnOrderItemService;
import com.vincent.rsf.server.manager.service.CompanysService;
import com.vincent.rsf.server.system.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
@Api(tags = "出库单明细")
@RestController
public class OutOrderItemController extends BaseController {
    @Autowired
    private AsnOrderItemService asnOrderItemService;
    @Autowired
    private CompanysService companysService;
    @PreAuthorize("hasAuthority('manager:outOrderItem:list')")
    @ApiOperation("分页获取列表")
    @PostMapping("/outOrderItem/page")
    public R page(@RequestBody Map<String, Object> map) {
        BaseParam baseParam = buildParam(map, BaseParam.class);
        PageParam<AsnOrderItem, BaseParam> pageParam = new PageParam<>(baseParam, AsnOrderItem.class);
        return R.ok().add(asnOrderItemService.listByAsnId(pageParam, pageParam.buildWrapper(true)));
    }
    @PreAuthorize("hasAuthority('manager:outOrderItem:list')")
    @PostMapping("/outOrderItem/list")
    public R list(@RequestBody Map<String, Object> map) {
        return R.ok().add(asnOrderItemService.list());
    }
    @PreAuthorize("hasAuthority('manager:outOrderItem:list')")
    @PostMapping({"/outOrderItem/many/{ids}", "/outOrderItems/many/{ids}"})
    public R many(@PathVariable Long[] ids) {
        return R.ok().add(asnOrderItemService.listByIds(Arrays.asList(ids)));
    }
    @PreAuthorize("hasAuthority('manager:outOrderItem:list')")
    @GetMapping("/outOrderItem/{id}")
    public R get(@PathVariable("id") Long id) {
        return R.ok().add(asnOrderItemService.getById(id));
    }
    @PreAuthorize("hasAuthority('manager:outOrderItem:save')")
    @OperationLog("Create 出库单明细")
    @PostMapping("/outOrderItem/save")
    public R save(@RequestBody Map<String, Object> params) {
        if (Objects.isNull(params)) {
            throw new CoolException("信息不能为空!!");
        }
        params.put("createBy", getLoginUserId());
        params.put("updateBy", getLoginUserId());
        if (!asnOrderItemService.fieldsSave(params)) {
            return R.error("Save Fail");
        }
        return R.ok("Save Success");
    }
    @PreAuthorize("hasAuthority('manager:outOrderItem:update')")
    @OperationLog("Update 出库单明细")
    @PostMapping("/outOrderItem/update")
    public R update(@RequestBody AsnOrderItem asnOrderItem) {
        asnOrderItem.setUpdateBy(getLoginUserId());
        asnOrderItem.setUpdateTime(new Date());
        if (!Objects.isNull(asnOrderItem.getSplrName()) && StringUtils.isNotBlank(asnOrderItem.getSplrName())) {
            Companys companys = companysService.getOne(new LambdaQueryWrapper<Companys>()
                            .eq(Companys::getType, CompanysType.COMPANYS_TYPE_SUPPLIER.val)
                    .eq(Companys::getId, asnOrderItem.getSplrName()));
            if (!Objects.isNull(companys)) {
                asnOrderItem.setSplrCode(companys.getCode()).setSplrName(companys.getName());
            }
        }
        if (!asnOrderItemService.updateById(asnOrderItem)) {
            return R.error("Update Fail");
        }
        return R.ok("Update Success").add(asnOrderItem);
    }
    @PreAuthorize("hasAuthority('manager:outOrderItem:remove')")
    @OperationLog("Delete 出库单明细")
    @PostMapping("/outOrderItem/remove/{ids}")
    public R remove(@PathVariable Long[] ids) {
        if (!asnOrderItemService.removeByIds(Arrays.asList(ids))) {
            return R.error("Delete Fail");
        }
        return R.ok("Delete Success").add(ids);
    }
    @PreAuthorize("hasAuthority('manager:outOrderItem:list')")
    @PostMapping("/outOrderItem/query")
    public R query(@RequestParam(required = false) String condition) {
        List<KeyValVo> vos = new ArrayList<>();
        LambdaQueryWrapper<AsnOrderItem> wrapper = new LambdaQueryWrapper<>();
        if (!Cools.isEmpty(condition)) {
            wrapper.like(AsnOrderItem::getId, condition);
        }
        asnOrderItemService.page(new Page<>(1, 30), wrapper).getRecords().forEach(
                item -> vos.add(new KeyValVo(item.getId(), item.getId()))
        );
        return R.ok().add(vos);
    }
    @PreAuthorize("hasAuthority('manager:outOrderItem:list')")
    @PostMapping("/outOrderItem/export")
    @ApiOperation("导出出库单明细")
    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        List<AsnOrderItem> orderItems = new ArrayList<>();
        if (!Objects.isNull(map.get("ids"))) {
            List<Long> ids = JSONArray.parseArray(JSONObject.toJSONString(map.get("ids")), Long.class);
            if (!ids.isEmpty()) {
                orderItems = asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>()
                        .in(AsnOrderItem::getId, ids)
                        .eq(AsnOrderItem::getStatus, 1));
            } else {
                orderItems = asnOrderItemService.list(new LambdaQueryWrapper<>());
            }
        } else {
            orderItems = asnOrderItemService.list(new LambdaQueryWrapper<>());
        }
        ExcelUtil.build(ExcelUtil.create(orderItems, AsnOrderItem.class, true), response);
    }
    /**
     * ASN单据明细导入
     * @param file
     * @return
     */
    @PostMapping("/outOrderItem/import")
    @ApiOperation("ASN导入接口")
    @PreAuthorize("hasAuthority('manager:outOrderItem:update')")
    public R importExcel(@RequestParam(value = "file") MultipartFile file, @RequestParam String asnId) throws Exception {
        if (Objects.isNull(file)) {
            R.error("文件不能为空!!");
        }
        HashMap<String, Object> hashMap = new HashMap<>();
        return asnOrderItemService.excelImport(file, hashMap, getLoginUserId());
    }
    /**
     * @author Ryan
     * @description 下载模板
     * @param
     * @return
     * @time 2025/4/18 08:17
     */
    @PostMapping("/outOrderItem/template/download")
    @ApiOperation("下载收货单模板")
    @PreAuthorize("hasAuthority('manager:outOrderItem:update')")
    public void downloadTemplate(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        AsnOrderTemplate template = ExcelUtil.mockData(AsnOrderTemplate.class);
        List<AsnOrderTemplate> list = Arrays.asList(template);
        ExcelUtil.build(ExcelUtil.create(list, AsnOrderTemplate.class, true), response);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
New file
@@ -0,0 +1,174 @@
package com.vincent.rsf.server.manager.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.server.api.entity.enums.OrderType;
import com.vincent.rsf.server.api.entity.enums.OrderWorkType;
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.manager.entity.AsnOrder;
import com.vincent.rsf.server.manager.entity.AsnOrderItem;
import com.vincent.rsf.server.manager.entity.excel.AsnOrderTemplate;
import com.vincent.rsf.server.manager.enums.AsnExceStatus;
import com.vincent.rsf.server.manager.service.AsnOrderItemService;
import com.vincent.rsf.server.manager.service.AsnOrderService;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
@RestController
@Api(tags = "出库单据")
public class OutStockController extends BaseController {
    @Autowired
    private AsnOrderService asnOrderService;
    @Autowired
    private AsnOrderItemService asnOrderItemService;
    @PreAuthorize("hasAuthority('manager:outStock:list')")
    @PostMapping("/outStock/page")
    public R page(@RequestBody Map<String, Object> map) {
        BaseParam baseParam = buildParam(map, BaseParam.class);
        PageParam<AsnOrder, BaseParam> pageParam = new PageParam<>(baseParam, AsnOrder.class);
        return R.ok().add(asnOrderService.page(pageParam, pageParam.buildWrapper(true)));
    }
    @PreAuthorize("hasAuthority('manager:outStock:list')")
    @PostMapping("/outStock/list")
    public R list(@RequestBody Map<String, Object> map) {
        return R.ok().add(asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().eq(AsnOrder::getType, OrderType.ORDER_OUT.type)));
    }
    @PreAuthorize("hasAuthority('manager:outStock:list')")
    @PostMapping({"/outStock/many/{ids}", "/asnOrders/many/{ids}"})
    public R many(@PathVariable Long[] ids) {
        return R.ok().add(asnOrderService.listByIds(Arrays.asList(ids)));
    }
    @PreAuthorize("hasAuthority('manager:asnOrder:list')")
    @OperationLog("表单查询")
    @GetMapping("/outStock/{id}")
    public R get(@PathVariable("id") Long id) {
        return R.ok().add(asnOrderService.getById(id));
    }
    @PreAuthorize("hasAuthority('manager:outStock:save')")
    @OperationLog("Create 出库单据")
    @PostMapping("/outStock/save")
    @ApiOperation("保存")
    public R save(@RequestBody AsnOrder asnOrder) {
        asnOrder.setCreateBy(getLoginUserId())
                .setUpdateBy(getLoginUserId());
        if (!Objects.isNull(asnOrder.getCode())) {
            String code = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_OUT_STOCK_CODE, asnOrder);
            asnOrder.setCode(code);
        }
        if (!asnOrderService.save(asnOrder)) {
            return R.error("Save Fail");
        }
        return R.ok("Save Success").add(asnOrder);
    }
    @PreAuthorize("hasAuthority('manager:outStock:update')")
    @OperationLog("Update ;出库单据")
    @PostMapping("/outStock/update")
    @ApiOperation("更新")
    public R update(@RequestBody AsnOrder asnOrder) {
        asnOrder.setType(OrderType.ORDER_OUT.type)
                .setUpdateBy(getLoginUserId())
                .setUpdateTime(new Date());
        if (!asnOrderService.updateById(asnOrder)) {
            return R.error("Update Fail");
        }
        return R.ok("Update Success").add(asnOrder);
    }
    @PreAuthorize("hasAuthority('manager:outStock:remove')")
    @OperationLog("Delete 出库单据")
    @PostMapping("/outStock/remove/{ids}")
    public R remove(@PathVariable Long[] ids) {
        if (!asnOrderService.removeByIds(Arrays.asList(ids))) {
            return R.error("Delete Fail");
        }
        return R.ok("Delete Success").add(ids);
    }
    @PreAuthorize("hasAuthority('manager:outStock:list')")
    @PostMapping("/outStock/query")
    @ApiOperation("查询")
    public R query(@RequestParam(required = false) String condition) {
        List<KeyValVo> vos = new ArrayList<>();
        LambdaQueryWrapper<AsnOrder> wrapper = new LambdaQueryWrapper<>();
        if (!Cools.isEmpty(condition)) {
            wrapper.like(AsnOrder::getCode, condition);
        }
        asnOrderService.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:list')")
    @PostMapping("/outStock/export")
    @ApiOperation("导出")
    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        List<AsnOrder> 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 = asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().in(AsnOrder::getId, ids));
            } else {
                orders = asnOrderService.list(new LambdaQueryWrapper<>());
            }
        } else {
            orders = asnOrderService.list();
        }
        List<AsnOrderTemplate> orderTemplates = new ArrayList<>();
        for (AsnOrder order : orders) {
            List<AsnOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>().eq(AsnOrderItem::getAsnId, order.getId()));
            for (AsnOrderItem 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()))
                        .setExceStatus(AsnExceStatus.getExceStatus(order.getExceStatus()))
                        .setAnfme(item.getAnfme() + "")
                        .setMaktx(item.getMaktx())
                        .setMemo(item.getMemo())
                        .setMatnrCode(item.getMatnrCode())
                        .setPoCode(item.getPoCode())
                        .setSplrName(item.getSplrName())
                        .setPoId(order.getPoId() + "")
                        .setTrackCode(item.getTrackCode())
                        .setBarcode(item.getBarcode())
                        .setPackName(item.getPackName())
                        .setPlatItemId(item.getPlatItemId())
                        .setSplrBatch(item.getSplrBatch())
                        .setSplrCode(item.getSplrCode())
                        .setStockUnit(item.getStockUnit())
                        .setPurQty(item.getPurQty() + "")
                        .setPurUnit(item.getPurUnit());
                orderTemplates.add(template);
            }
        }
        ExcelUtil.build(ExcelUtil.create(orderTemplates, AsnOrderTemplate.class), response);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java
@@ -63,4 +63,10 @@
     * DO单据编码规则
     */
    public final static String SYS_DELIVERY_RULE_CODE = "sys_delivery_rule_code";
    /**
     * 出库单据号
     */
    public final static String SYS_OUT_STOCK_CODE = "sys_out_stock_code";
}