40个文件已添加
3个文件已删除
25个文件已修改
4663 ■■■■■ 已修改文件
rsf-admin/src/i18n/en.js 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/zh.js 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/ResourceContent.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/deviceSite/DeviceSiteList.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/deviceSite/InitModal.jsx 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/basicInfo/loc/LocList.jsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/deviceBind/DeviceBindCreate.jsx 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/deviceBind/DeviceBindEdit.jsx 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/deviceBind/DeviceBindList.jsx 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/deviceBind/DeviceBindPanel.jsx 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/deviceBind/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderList.jsx 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/outStock/OutOrderModal.jsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WaveCreate.jsx 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WaveEdit.jsx 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WaveItemCreate.jsx 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WaveItemEdit.jsx 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WaveItemList.jsx 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WaveList.jsx 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/WavePanel.jsx 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/orders/wave/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/warehouseAreas/WarehouseAreasCreate.jsx 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/warehouseAreas/WarehouseAreasEdit.jsx 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/warehouseAreas/WarehouseAreasList.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/waveItem/WaveItemCreate.jsx 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/waveItem/WaveItemEdit.jsx 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/waveItem/WaveItemList.jsx 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/waveItem/WaveItemPanel.jsx 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/page/waveItem/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/common/interceptor/DynamicFieldsInterceptor.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/DeviceBindController.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveController.java 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveItemController.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/DeviceSiteParame.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/AsnOrderItem.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeliveryItem.java 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeviceBind.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeviceSite.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WarehouseAreas.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Wave.java 249 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WaveItem.java 293 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/AsnExceStatus.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/WaveExceStatus.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/DeviceBindMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/WaveItemMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/WaveMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/DeviceBindService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WaveItemService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WaveService.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/DeviceBindServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/DeviceSiteServiceImpl.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveItemServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/GroupMergeUtil.java 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/DictTypeCode.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/delivery.sql 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/deliveryItem.sql 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/locItem.sql 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/wave.sql 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/java/waveItem.sql 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/resources/mapper/manager/DeviceBindMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/resources/mapper/manager/WaveItemMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-server/src/main/resources/mapper/manager/WaveMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rsf-admin/src/i18n/en.js
@@ -70,6 +70,7 @@
            loadMore: 'Load More Data',
            complete: 'Complete',
            deprecate: 'Deprecate',
            inputPlaceholder: 'Use commas to separate',
            resend: 'RESEND',
            selected: 'selected',
            batch: 'batch'
@@ -175,6 +176,9 @@
        outStockItem: 'Out Stock Item',
        inStockPoces: 'In Stock Pocess',
        outStockPoces: 'Out Stock Pocess',
        deviceBind: 'Device Bind',
        wave: 'Wave Manage',
    },
    table: {
        field: {
@@ -616,6 +620,7 @@
            },
            deliveryItem: {
                deliveryId: "deliveryId",
                deliveryCode: "Delivery Code",
                platItemId: "platItemId",
                matnrCode: "matnrCode",
                matnrName: "matnrName",
@@ -667,7 +672,6 @@
                deviceCode: "deviceCode",
                deviceSite: "deviceSite",
                flagInit: "flagInit",
                sites: "sites",
            },
            waitPakin: {
                code: "code",
@@ -717,6 +721,30 @@
                fieldsIndex: "fieldsIndex",
                qty: "qty",
                batch: "batch",
            },
            wave: {
                code: "code",
                type: "type",
                exceStatus: "exceStatus",
                anfme: "anfme",
                qty: "qty",
                orderNum: "orderNum",
            },
            waveItem: {
                waveId: "waveId",
                waveCode: "waveCode",
                matnrId: "matnrId",
                matnrName: "matnrName",
                matnrCode: "matnrCode",
                batch: "batch",
                splrBatch: "splrBatch",
                orderCode: "orderCode",
                orderItemId: "orderItemId",
                unit: "unit",
                trackCode: "trackCode",
                fieldsIndex: "fieldsIndex",
                anfme: "anfme",
                workQty: "workQty",
            },
            task: {
                taskCode: "TaskCode",
@@ -833,6 +861,18 @@
                model: "model",
                fieldsIndex: "fieldsIndex",
            },
            deviceBind: {
                currentRow: "currentRow",
                startRow: "startRow",
                endRow: "endRow",
                deviceQty: "deviceQty",
                startDeviceNo: "startDeviceNo",
                endDeviceNo: "endDeviceNo",
                staList: "staList",
                typeId: "typeId",
                beSimilar: "beSimilar",
                emptySimilar: "emptySimilar",
            },
        }
    },
    page: {
@@ -944,6 +984,7 @@
        asnCreate: "Create By Order",
        createTask: "createTask",
        recover: "recover",
        createWave: "Create Wave",
        order: 'Orders',
    },
rsf-admin/src/i18n/zh.js
@@ -58,6 +58,7 @@
            collapse: '折叠',
            collapseAll: '全部折叠',
            scope: '权限',
            inputPlaceholder: '站点可填多个,中间以英文逗号区分(,)',
            import: {
                title: '导入',
                stop: '停止导入',
@@ -176,6 +177,9 @@
        outStockItem: '出库单明细',
        inStockPoces: '入库流程',
        outStockPoces: '出库流程',
        deviceBind: '设备绑定',
        wave: '波次管理',
    },
    table: {
        field: {
@@ -331,6 +335,7 @@
            warehouseAreas: {
                uuid: "唯一编码",
                name: "库区名称",
                type: "库区类型",
                wareId: "所属仓库",
                code: "库区编码",
                shipperId: "货主",
@@ -361,8 +366,8 @@
            loc: {
                warehouseId: "所属仓库",
                areaId: "所属库区",
                code: "库位编码",
                type: "库位类型",
                code: "编码",
                type: "类型",
                name: "名称",
                flagLogic: "虚拟库位",
                fucAtrrs: "功能属性",
@@ -661,6 +666,7 @@
            },
            deliveryItem: {
                deliveryId: "主单ID",
                deliveryCode: "单号",
                platItemId: "行号",
                matnrCode: "物料编码",
                matnrName: "物料名称",
@@ -704,15 +710,14 @@
            },
            deviceSite: {
                type: "入出库类型",
                site: "站点编号",
                site: "作业站点",
                name: "名称",
                wcsCode: "wcs站点编号",
                target: "目标站点",
                label: "站点标签",
                device: "立库类型",
                deviceCode: "设备编号",
                deviceSite: "设备站点",
                flagInit: "是否初始化",
                sites: "作业站点",
            },
            waitPakin: {
                code: "编码",
@@ -762,6 +767,30 @@
                fieldsIndex: "扩展字段唯一标识",
                qty: "已完成",
                batch: "批次",
            },
            wave: {
                code: "波次号",
                type: "单据类型",
                exceStatus: "状态",
                anfme: "数量",
                qty: "完成数量",
                orderNum: "单据数",
            },
            waveItem: {
                waveId: "波次ID",
                waveCode: "波次号",
                matnrId: "物料ID",
                matnrName: "物料名称",
                matnrCode: "物料编码",
                batch: "批次",
                splrBatch: "供应商批次",
                orderCode: "源单号",
                orderItemId: "源单明细ID",
                unit: "单位",
                trackCode: "跟踪码",
                fieldsIndex: "动态扩展",
                anfme: "数量",
                workQty: "执行数",
            },
            task: {
                taskCode: "任务号",
@@ -878,6 +907,18 @@
                model: "型号",
                fieldsIndex: "动态索引",
            },
            deviceBind: {
                currentRow: "当前排号",
                startRow: "起始排号",
                endRow: "终止排号",
                deviceQty: "设备数量",
                startDeviceNo: "起始设备号",
                endDeviceNo: "终止设备号",
                staList: "站点集",
                typeId: "库区类型",
                beSimilar: "相似开关",
                emptySimilar: "空板靠近开关",
            },
        }
    },
    page: {
@@ -991,7 +1032,8 @@
        complete: "完成",
        close: "关闭",
        asnCreate: "通过单据创建",
        createTask: "生成任务",
        createTask: "下发任务",
        createWave: "生成波次",
        recover: "继续收货",
    },
};
rsf-admin/src/page/ResourceContent.js
@@ -43,6 +43,8 @@
import stock from './orders/stock';
import delivery from './orders/delivery';
import outStock from './orders/outStock';
import deviceBind from './deviceBind';
import wave from './orders/wave';
const ResourceContent = (node) => {
@@ -125,6 +127,11 @@
            return delivery;
        case 'outStock':
            return outStock;
        case 'deviceBind':
            return deviceBind;
        case 'wave':
            return wave;
        default:
            return {
                list: ListGuesser,
rsf-admin/src/page/basicInfo/deviceSite/DeviceSiteList.jsx
@@ -148,18 +148,17 @@
                    preferenceKey='deviceSite'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    rowClick={(id, resource, record) => false}
                    omit={['id', 'createTime', 'createBy', 'memo']}
                    omit={['id', 'createTime', 'createBy', 'memo', 'label']}
                >
                    <NumberField source="id" />
                    <TextField source="name" label="table.field.deviceSite.name" />
                    <NumberField source="type$" label="table.field.deviceSite.type" />
                    <TextField source="site" label="table.field.deviceSite.site" />
                    <TextField source="wcsCode" label="table.field.deviceSite.wcsCode" />
                    <TextField source="target" label="table.field.deviceSite.target" />
                    <TextField source="label" label="table.field.deviceSite.label" />
                    <TextField source="device$" label="table.field.deviceSite.device" />
                    <TextField source="deviceCode" label="table.field.deviceSite.deviceCode" />
                    <TextField source="deviceSite" label="table.field.deviceSite.deviceSite" />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
rsf-admin/src/page/basicInfo/deviceSite/InitModal.jsx
@@ -95,7 +95,7 @@
    return (
        <Dialog open={open} maxWidth="md" fullWidth>
        <Dialog open={open} maxWidth="lg" fullWidth>
            <Form onSubmit={handleSubmit}>
                <DialogCloseButton onClose={handleClose} />
                <DialogTitle>{translate('toolbar.siteInit')}</DialogTitle>
@@ -119,8 +119,6 @@
                                    dictTypeCode="sys_device_type"
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextInput
                                    label={translate("table.field.deviceSite.deviceCode")}
@@ -129,25 +127,33 @@
                                    type="number"
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextInput
                                    label={translate("table.field.deviceSite.deviceSite")}
                                    name="deviceSites"
                                    placeholder={translate('common.action.inputPlaceholder')}
                                    size="small"
                                    type="number"
                                // type="number"
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextInput
                                    label={translate("table.field.deviceSite.sites")}
                                    name="sites"
                                    label={translate("table.field.deviceSite.site")}
                                    name="site"
                                    placeholder={translate('common.action.inputPlaceholder')}
                                    size="small"
                                    type="number"
                                // type="number"
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <TextInput
                                    label={translate("table.field.deviceSite.target")}
                                    name="target"
                                    placeholder={translate('common.action.inputPlaceholder')}
                                    size="small"
                                // type="number"
                                />
                            </Grid>
                            <Grid item xs={4}>
                                <SelectInput
                                    label="table.field.deviceSite.flagInit"
rsf-admin/src/page/basicInfo/loc/LocList.jsx
@@ -66,20 +66,20 @@
    '& .column-name': {
    },
    '& .opt': {
        width: 200
        width: 180
    },
    '& .RaDatagrid-headerCell': {
        textAlign: 'left'
    },
    '& .RaDatagrid-rowCell': {
        textAlign: 'left'
    },
    // '& .RaDatagrid-headerCell': {
    //     textAlign: 'left'
    // },
    // '& .RaDatagrid-rowCell': {
    //     textAlign: 'left'
    // },
    '& .type .MuiTypography-root': {
        whiteSpace: 'nowrap',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        display: 'block',
        width: '200px',
        width: 'auto',
    },
}));
rsf-admin/src/page/deviceBind/DeviceBindCreate.jsx
New file
@@ -0,0 +1,174 @@
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 DeviceBindCreate = (props) => {
    const { open, setOpen } = props;
    const translate = useTranslate();
    const notify = useNotify();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
    };
    return (
        <>
            <CreateBase
                record={{}}
                transform={(data) => {
                    return data;
                }}
                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
            >
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                        }}
                        >
                            {translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
                        <DialogContent sx={{ mt: 2 }}>
                            <Grid container rowSpacing={2} columnSpacing={2}>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.currentRow"
                                        source="currentRow"
                                        autoFocus
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.startRow"
                                        source="startRow"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.endRow"
                                        source="endRow"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.deviceQty"
                                        source="deviceQty"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.startDeviceNo"
                                        source="startDeviceNo"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.endDeviceNo"
                                        source="endDeviceNo"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.deviceBind.staList"
                                        source="staList"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.deviceBind.typeId"
                                        source="typeId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.deviceBind.beSimilar"
                                        source="beSimilar"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.deviceBind.emptySimilar"
                                        source="emptySimilar"
                                        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 DeviceBindCreate;
rsf-admin/src/page/deviceBind/DeviceBindEdit.jsx
New file
@@ -0,0 +1,146 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    Edit,
    SimpleForm,
    FormDataConsumer,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    SaveButton,
    Toolbar,
    Labeled,
    NumberField,
    required,
    useRecordContext,
    DeleteButton,
} from 'react-admin';
import { useWatch, useFormContext } from "react-hook-form";
import { Stack, Grid, Box, Typography } from '@mui/material';
import * as Common from '@/utils/common';
import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
import EditBaseAside from "../components/EditBaseAside";
import CustomerTopToolBar from "../components/EditTopToolBar";
import MemoInput from "../components/MemoInput";
import StatusSelectInput from "../components/StatusSelectInput";
const FormToolbar = () => {
    const { getValues } = useFormContext();
    return (
        <Toolbar sx={{ justifyContent: 'space-between' }}>
            <SaveButton />
            <DeleteButton mutationMode="optimistic" />
        </Toolbar>
    )
}
const DeviceBindEdit = () => {
    const translate = useTranslate();
    return (
        <Edit
            redirect="list"
            mutationMode={EDIT_MODE}
            actions={<CustomerTopToolBar />}
            aside={<EditBaseAside />}
        >
            <SimpleForm
                shouldUnregister
                warnWhenUnsavedChanges
                toolbar={<FormToolbar />}
                mode="onTouched"
                defaultValues={{}}
            // validate={(values) => { }}
            >
                <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
                    <Grid item xs={12} md={8}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.main')}
                        </Typography>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.currentRow"
                                source="currentRow"
                                autoFocus
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.startRow"
                                source="startRow"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.endRow"
                                source="endRow"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.deviceQty"
                                source="deviceQty"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.startDeviceNo"
                                source="startDeviceNo"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.endDeviceNo"
                                source="endDeviceNo"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.deviceBind.staList"
                                source="staList"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.deviceBind.typeId"
                                source="typeId"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.deviceBind.beSimilar"
                                source="beSimilar"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.deviceBind.emptySimilar"
                                source="emptySimilar"
                                parse={v => v}
                            />
                        </Stack>
                    </Grid>
                    <Grid item xs={12} md={4}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.common')}
                        </Typography>
                        <StatusSelectInput />
                        <Box mt="2em" />
                        <MemoInput />
                    </Grid>
                </Grid>
            </SimpleForm>
        </Edit >
    )
}
export default DeviceBindEdit;
rsf-admin/src/page/deviceBind/DeviceBindList.jsx
New file
@@ -0,0 +1,170 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from 'react-router-dom';
import {
    List,
    DatagridConfigurable,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    EditButton,
    FilterButton,
    CreateButton,
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    useRecordContext,
    useTranslate,
    useNotify,
    useListContext,
    FunctionField,
    TextField,
    NumberField,
    DateField,
    BooleanField,
    ReferenceField,
    TextInput,
    DateTimeInput,
    DateInput,
    SelectInput,
    NumberInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import DeviceBindCreate from "./DeviceBindCreate";
import DeviceBindPanel from "./DeviceBindPanel";
import EmptyData from "../components/EmptyData";
import MyCreateButton from "../components/MyCreateButton";
import MyExportButton from '../components/MyExportButton';
import PageDrawer from "../components/PageDrawer";
import MyField from "../components/MyField";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
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 />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <NumberInput source="currentRow" label="table.field.deviceBind.currentRow" />,
    <NumberInput source="startRow" label="table.field.deviceBind.startRow" />,
    <NumberInput source="endRow" label="table.field.deviceBind.endRow" />,
    <NumberInput source="deviceQty" label="table.field.deviceBind.deviceQty" />,
    <NumberInput source="startDeviceNo" label="table.field.deviceBind.startDeviceNo" />,
    <NumberInput source="endDeviceNo" label="table.field.deviceBind.endDeviceNo" />,
    <TextInput source="staList" label="table.field.deviceBind.staList" />,
    <NumberInput source="typeId" label="table.field.deviceBind.typeId" />,
    <TextInput source="beSimilar" label="table.field.deviceBind.beSimilar" />,
    <TextInput source="emptySimilar" label="table.field.deviceBind.emptySimilar" />,
    <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 DeviceBindList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    return (
        <Box display="flex">
            <List
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.deviceBind"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='deviceBind' />
                        <MyExportButton />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='deviceBind'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    rowClick={(id, resource, record) => false}
                    expand={() => <DeviceBindPanel />}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'memo']}
                >
                    <NumberField source="id" />
                    <NumberField source="currentRow" label="table.field.deviceBind.currentRow" />
                    <NumberField source="startRow" label="table.field.deviceBind.startRow" />
                    <NumberField source="endRow" label="table.field.deviceBind.endRow" />
                    <NumberField source="deviceQty" label="table.field.deviceBind.deviceQty" />
                    <NumberField source="startDeviceNo" label="table.field.deviceBind.startDeviceNo" />
                    <NumberField source="endDeviceNo" label="table.field.deviceBind.endDeviceNo" />
                    <TextField source="staList" label="table.field.deviceBind.staList" />
                    <NumberField source="typeId" label="table.field.deviceBind.typeId" />
                    <TextField source="beSimilar" label="table.field.deviceBind.beSimilar" />
                    <TextField source="emptySimilar" label="table.field.deviceBind.emptySimilar" />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
                    <DateField source="updateTime" label="common.field.updateTime" showTime />
                    <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
                    <DateField source="createTime" label="common.field.createTime" showTime />
                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
                    <TextField source="memo" label="common.field.memo" sortable={false} />
                    <WrapperField cellClassName="opt" label="common.field.opt">
                        <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
                        <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
                    </WrapperField>
                </StyledDatagrid>
            </List>
            <DeviceBindCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
            <PageDrawer
                title='DeviceBind Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
        </Box>
    )
}
export default DeviceBindList;
rsf-admin/src/page/deviceBind/DeviceBindPanel.jsx
New file
@@ -0,0 +1,111 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material';
import {
    useTranslate,
    useRecordContext,
} from 'react-admin';
import PanelTypography from "../components/PanelTypography";
import * as Common from '@/utils/common'
const DeviceBindPanel = () => {
    const record = useRecordContext();
    if (!record) return null;
    const translate = useTranslate();
    return (
        <>
            <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}>
                <CardContent>
                    <Grid container spacing={2}>
                        <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}>
                            <Typography variant="h6" gutterBottom align="left" sx={{
                                maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' },
                                whiteSpace: 'nowrap',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                            }}>
                                {Common.camelToPascalWithSpaces(translate('table.field.deviceBind.id'))}: {record.id}
                            </Typography>
                            {/*  inherit, primary, secondary, textPrimary, textSecondary, error */}
                            <Typography variant="h6" gutterBottom align="right" >
                                ID: {record.id}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Grid container spacing={2}>
                        <Grid item xs={12} container alignContent="flex-end">
                            <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
                                {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Box height={20}>&nbsp;</Box>
                    <Grid container spacing={2}>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.currentRow"
                                property={record.currentRow}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.startRow"
                                property={record.startRow}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.endRow"
                                property={record.endRow}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.deviceQty"
                                property={record.deviceQty}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.startDeviceNo"
                                property={record.startDeviceNo}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.endDeviceNo"
                                property={record.endDeviceNo}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.staList"
                                property={record.staList}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.typeId"
                                property={record.typeId}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.beSimilar"
                                property={record.beSimilar}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.deviceBind.emptySimilar"
                                property={record.emptySimilar}
                            />
                        </Grid>
                    </Grid>
                </CardContent>
            </Card >
        </>
    );
};
export default DeviceBindPanel;
rsf-admin/src/page/deviceBind/index.jsx
New file
@@ -0,0 +1,18 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    ListGuesser,
    EditGuesser,
    ShowGuesser,
} from "react-admin";
import DeviceBindList from "./DeviceBindList";
import DeviceBindEdit from "./DeviceBindEdit";
export default {
    list: DeviceBindList,
    edit: DeviceBindEdit,
    show: ShowGuesser,
    recordRepresentation: (record) => {
        return `${record.id}`
    }
};
rsf-admin/src/page/orders/outStock/OutOrderList.jsx
@@ -55,6 +55,7 @@
import OutOrderCreate from "./OutOrderCreate";
import AddIcon from '@mui/icons-material/Add';
import OutOrderModal from "./OutOrderModal";
import PublicIcon from '@mui/icons-material/Public';
const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({
  '& .css-1vooibu-MuiSvgIcon-root': {
@@ -141,7 +142,7 @@
            <MyCreateButton onClick={() => { setManualDialog(true) }} />
            <SelectColumnsButton preferenceKey='outStock' />
            <ImportButton value={'asnOrderItem'} />
            <MyExportButton />
            {/* <MyExportButton /> */}
          </TopToolbar>
        )}
        perPage={DEFAULT_PAGE_SIZE}
@@ -150,11 +151,7 @@
          sx={{ width: '100%' }}
          preferenceKey='outStock'
          bulkActionButtons={
            <>
              <MyExportButton />
              <BulkDeleteButton mutationMode={OPERATE_MODE}
              />
            </>}
            <PublicTaskButton />}
          rowClick={false}
          expandSingle={true}
          omit={['id', 'createTime', 'createBy', 'memo', 'poId', 'rleStatus$']}
@@ -177,8 +174,7 @@
          <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} /> */}
            <CancelButton></CancelButton>
            <CancelButton />
          </WrapperField>
        </StyledDatagrid>
      </List>
@@ -200,6 +196,35 @@
  )
}
export default OutOrderList;
const PublicTaskButton = () => {
  const record = useRecordContext();
  const { selectedIds, onUnselectItems } = useListContext();
  const notify = useNotify();
  const refresh = useRefresh();
  const redirect = useRedirect();
  const pubClick = async (event) => {
    event.stopPropagation();
    onUnselectItems();
    const res = await request.post(`/outStock/generate/wave`, { ids: selectedIds });
    if (res?.data?.code === 200) {
      notify(res.data.msg);
      redirect("/wave")
    } else {
      notify(res.data.msg);
    }
    refresh();
  }
  return (
    <Button
      onClick={pubClick}
      label={"toolbar.createWave"}
      startIcon={<PublicIcon />}
    />);
}
const MyButton = ({ setCreateDialog, setmodalType }) => {
  const record = useRecordContext();
@@ -241,7 +266,6 @@
  const record = useRecordContext();
  const notify = useNotify();
  const refresh = useRefresh();
  console.log(record);
  const cancelOrder = async () => {
    const { data: { code, data, msg } } = await request.get(`/outStock/cancel/${record?.id}`);
rsf-admin/src/page/orders/outStock/OutOrderModal.jsx
@@ -54,14 +54,6 @@
    '& .RaDatagrid-row': {
        cursor: 'auto'
    },
    '& .column-name': {
    },
    '& .opt': {
        width: 220
    },
    '& .wkType': {
        width: 110
    },
    '& .status': {
        width: 90
    },
@@ -102,9 +94,10 @@
        const { filterValues, setFilters, refetch } = useListContext('deliveryItem');
        const [formValues, setFormValues] = useState(filterValues);
        const handleChange = (event) => {
            if (event.target == undefined || event.target == null) {return}
            setFormValues(formValues => ({
                ...formValues,
                [event.target.name]: event.target.value
              [event.target.name]: event.target.value
            }));
        };
@@ -125,10 +118,22 @@
                    </Stack>
                    <Stack>
                        <TextInput
                            source="deliveryCode"
                            label="table.field.deliveryItem.deliveryCode"
                            defaultValue={params?.matnrName}
                            onChange={handleChange}
                            resettable
                        />
                    </Stack>
                    <Stack>
                        <TextInput
                            source="matnrName"
                            label="table.field.deliveryItem.matnrName"
                            defaultValue={params?.matnrName}
                            onChange={handleChange}
                            resettable
                        />
                    </Stack>
                    <Stack>
@@ -136,6 +141,7 @@
                            source="matnrCode"
                            label="table.field.deliveryItem.matnrCode"
                            defaultValue={params?.matnrCode}
                            resettable
                            onChange={handleChange} />
                    </Stack>
                    <Stack>
@@ -143,6 +149,7 @@
                            source="splrName"
                            label="table.field.deliveryItem.splrName"
                            defaultValue={params?.splrName}
                            resettable
                            onChange={handleChange} />
                    </Stack>
                </Grid>
@@ -162,7 +169,7 @@
            aria-hidden
            fullWidth
            disableRestoreFocus
            maxWidth="lg"
            maxWidth="xl"
        >
            <DialogTitle id="form-dialog-title" sx={{
                position: 'sticky',
@@ -200,14 +207,16 @@
                            perPage={DEFAULT_PAGE_SIZE}
                        >
                            <StyledDatagrid
                                sx={{ height: '400' }}
                                preferenceKey='deliveryItem'
                                bulkActionButtons={<AddOutStockButton  setOpen={setOpen}/>}
                                bulkActionButtons={<AddOutStockButton setOpen={setOpen} />}
                                rowClick={(id, resource, record) => false}
                                expand={false}
                                expandSingle={true}
                                omit={['id', 'createTime', 'createBy', 'memo', 'workQty', 'startTime', 'endTime', 'updateBy', 'createTime']}
                            >
                                <NumberField source="id" />
                                <TextField source="deliveryCode" label="table.field.deliveryItem.deliveryCode" />
                                <TextField source="matnrCode" label="table.field.deliveryItem.matnrCode" />
                                <TextField source="matnrName" label="table.field.deliveryItem.matnrName" />
                                <TextField source="unit" label="table.field.deliveryItem.unit" />
@@ -236,8 +245,7 @@
    const { selectedIds, onUnselectItems } = useListContext();
    const notify = useNotify();
    const confirm = async (event) => {
        console.log(selectedIds);
        const res = await request.post(`/outStock/generate/orders`, {ids: selectedIds});
        const res = await request.post(`/outStock/generate/orders`, { ids: selectedIds });
        if (res?.data?.code === 200) {
            notify(res.data.msg);
        } else {
rsf-admin/src/page/orders/wave/WaveCreate.jsx
New file
@@ -0,0 +1,158 @@
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 WaveCreate = (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.wave.code"
                                        source="code"
                                        parse={v => v}
                                        autoFocus
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <SelectInput
                                        label="table.field.wave.type"
                                        source="type"
                                        choices={[
                                            { id: 0, name: '手动' },
                                            { id: 1, name: '自动' },
                                        ]}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <SelectInput
                                        label="table.field.wave.exceStatus"
                                        source="exceStatus"
                                        choices={[
                                            { id: 0, name: '初始化' },
                                            { id: 1, name: '生成任务' },
                                            { id: 2, name: '任务播种' },
                                            { id: 3, name: '完成' },
                                        ]}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.wave.anfme"
                                        source="anfme"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.wave.qty"
                                        source="qty"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.wave.orderNum"
                                        source="orderNum"
                                    />
                                </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 WaveCreate;
rsf-admin/src/page/orders/wave/WaveEdit.jsx
New file
@@ -0,0 +1,145 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    Edit,
    SimpleForm,
    FormDataConsumer,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    SaveButton,
    Toolbar,
    Labeled,
    NumberField,
    required,
    useRecordContext,
    DeleteButton,
} from 'react-admin';
import { useWatch, useFormContext } from "react-hook-form";
import { Stack, Grid, Box, Typography } from '@mui/material';
import * as Common from '@/utils/common';
import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
import EditBaseAside from "../../components/EditBaseAside";
import CustomerTopToolBar from "../../components/EditTopToolBar";
import MemoInput from "../../components/MemoInput";
import StatusSelectInput from "../../components/StatusSelectInput";
import WaveItemList from "./WaveItemList";
const FormToolbar = () => {
    const { getValues } = useFormContext();
    return (
        <Toolbar sx={{ justifyContent: 'space-between' }}>
            <SaveButton />
            <DeleteButton mutationMode="optimistic" />
        </Toolbar>
    )
}
const WaveEdit = () => {
    const translate = useTranslate();
    return (
        <Box>
            <Edit
                redirect="list"
                mutationMode={EDIT_MODE}
                actions={<CustomerTopToolBar />}
                aside={<EditBaseAside />}
            >
                <SimpleForm
                    shouldUnregister
                    warnWhenUnsavedChanges
                    toolbar={<FormToolbar />}
                    mode="onTouched"
                    defaultValues={{}}
                // validate={(values) => { }}
                >
                    <Grid container width={{ xs: '100%', xl: '100%' }} rowSpacing={3} columnSpacing={3}>
                        <Grid item xs={16} md={10} 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',
                            }
                        }}>
                            <Typography variant="h6" gutterBottom>
                                {translate('common.edit.title.main')}
                            </Typography>
                            <Stack direction='row' gap={2}>
                                <TextInput
                                    label="table.field.wave.code"
                                    source="code"
                                    parse={v => v}
                                    readOnly
                                />
                                <SelectInput
                                    label="table.field.wave.type"
                                    source="type"
                                    readOnly
                                    choices={[
                                        { id: 0, name: '手动' },
                                        { id: 1, name: '自动' },
                                    ]}
                                />
                                <SelectInput
                                    label="table.field.wave.exceStatus"
                                    source="exceStatus"
                                    readOnly
                                    choices={[
                                        { id: 0, name: '初始化' },
                                        { id: 1, name: '生成任务' },
                                        { id: 2, name: '任务播种' },
                                        { id: 3, name: '完成' },
                                    ]}
                                />
                            </Stack>
                            <Stack direction='row' gap={2}>
                                <NumberInput
                                    label="table.field.wave.anfme"
                                    readOnly
                                    source="anfme"
                                />
                                <NumberInput
                                    label="table.field.wave.qty"
                                    readOnly
                                    source="qty"
                                />
                                <NumberInput
                                    label="table.field.wave.orderNum"
                                    readOnly
                                    source="orderNum"
                                />
                            </Stack>
                        </Grid>
                        <Grid item xs={8} md={2}>
                            <Typography variant="h6" gutterBottom>
                                {translate('common.edit.title.common')}
                            </Typography>
                            <StatusSelectInput />
                            <Box mt="2em" />
                            <MemoInput />
                        </Grid>
                    </Grid>
                </SimpleForm>
            </Edit >
            <WaveItemList />
        </Box>
    )
}
export default WaveEdit;
rsf-admin/src/page/orders/wave/WaveItemCreate.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 WaveItemCreate = (props) => {
    const { open, setOpen } = props;
    const translate = useTranslate();
    const notify = useNotify();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
    };
    return (
        <>
            <CreateBase
                record={{}}
                transform={(data) => {
                    return data;
                }}
                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
            >
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                        }}
                        >
                            {translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
                        <DialogContent sx={{ mt: 2 }}>
                            <Grid container rowSpacing={2} columnSpacing={2}>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.waveId"
                                        source="waveId"
                                        autoFocus
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.waveCode"
                                        source="waveCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.matnrId"
                                        source="matnrId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.matnrName"
                                        source="matnrName"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.matnrCode"
                                        source="matnrCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.batch"
                                        source="batch"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.splrBatch"
                                        source="splrBatch"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.orderCode"
                                        source="orderCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.orderItemId"
                                        source="orderItemId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.unit"
                                        source="unit"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.trackCode"
                                        source="trackCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.fieldsIndex"
                                        source="fieldsIndex"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.anfme"
                                        source="anfme"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.workQty"
                                        source="workQty"
                                    />
                                </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 WaveItemCreate;
rsf-admin/src/page/orders/wave/WaveItemEdit.jsx
New file
@@ -0,0 +1,196 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    Edit,
    SimpleForm,
    FormDataConsumer,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    SaveButton,
    Toolbar,
    Labeled,
    NumberField,
    required,
    useRecordContext,
    useGetOne,
    DeleteButton,
} from 'react-admin';
import { useWatch, useFormContext } from "react-hook-form";
import { Stack, Grid, Box, Typography } from '@mui/material';
import * as Common from '@/utils/common';
import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
import EditBaseAside from "../../components/EditBaseAside";
import CustomerTopToolBar from "../../components/EditTopToolBar";
import MemoInput from "../../components/MemoInput";
import StatusSelectInput from "../../components/StatusSelectInput";
const FormToolbar = () => {
    const { getValues } = useFormContext();
    return (
        <Toolbar sx={{ justifyContent: 'space-between' }}>
            <SaveButton />
            <DeleteButton mutationMode="optimistic" />
        </Toolbar>
    )
}
const WaveItemEdit = (props) => {
    const { open, setOpen, record } = props;
    const translate = useTranslate();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const { data, isPending, } = useGetOne('waveItem', { 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="waveItem"
                    mutationMode={EDIT_MODE}
                    actions={<CustomerTopToolBar />}
                >
                    <Form
                        shouldUnregister
                        warnWhenUnsavedChanges
                        mode="onTouched"
                        defaultValues={{}}
                    >
                        <Grid container width={{ xs: '100%', xl: '100%' }} rowSpacing={3} columnSpacing={3}>
                            <Grid item xs={16} md={10}>
                                <Typography variant="h6" gutterBottom>
                                    {translate('common.edit.title.main')}
                                </Typography>
                                <Stack direction='row' gap={2}>
                                    <NumberInput
                                        label="table.field.waveItem.waveId"
                                        source="waveId"
                                        autoFocus
                                    />
                                    <TextInput
                                        label="table.field.waveItem.waveCode"
                                        source="waveCode"
                                        parse={v => v}
                                    />
                                    <NumberInput
                                        label="table.field.waveItem.matnrId"
                                        source="matnrId"
                                    />
                                    <TextInput
                                        label="table.field.waveItem.matnrName"
                                        source="matnrName"
                                        parse={v => v}
                                    />
                                </Stack>
                                <Stack direction='row' gap={2}>
                                    <TextInput
                                        label="table.field.waveItem.matnrCode"
                                        source="matnrCode"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.waveItem.batch"
                                        source="batch"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.waveItem.splrBatch"
                                        source="splrBatch"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.waveItem.orderCode"
                                        source="orderCode"
                                        parse={v => v}
                                    />
                                </Stack>
                                <Stack direction='row' gap={2}>
                                    <NumberInput
                                        label="table.field.waveItem.orderItemId"
                                        source="orderItemId"
                                    />
                                    <TextInput
                                        label="table.field.waveItem.unit"
                                        source="unit"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.waveItem.trackCode"
                                        source="trackCode"
                                        parse={v => v}
                                    />
                                    <TextInput
                                        label="table.field.waveItem.fieldsIndex"
                                        source="fieldsIndex"
                                        parse={v => v}
                                    />
                                </Stack>
                                <Stack direction='row' gap={2}>
                                    <NumberInput
                                        label="table.field.waveItem.anfme"
                                        source="anfme"
                                    />
                                    <NumberInput
                                        label="table.field.waveItem.workQty"
                                        source="workQty"
                                    />
                                </Stack>
                            </Grid>
                            <Grid item xs={8} md={2}>
                                <Typography variant="h6" gutterBottom>
                                    {translate('common.edit.title.common')}
                                </Typography>
                                <StatusSelectInput />
                                <Box mt="2em" />
                                <MemoInput />
                            </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 WaveItemEdit;
rsf-admin/src/page/orders/wave/WaveItemList.jsx
New file
@@ -0,0 +1,190 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from 'react-router-dom';
import {
    List,
    DatagridConfigurable,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    EditButton,
    FilterButton,
    CreateButton,
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    useRecordContext,
    useTranslate,
    useNotify,
    useListContext,
    FunctionField,
    TextField,
    NumberField,
    DateField,
    BooleanField,
    ReferenceField,
    TextInput,
    DateTimeInput,
    DateInput,
    SelectInput,
    NumberInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
    useGetRecordId,
    Button,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import WaveItemCreate from "./WaveItemCreate";
import EmptyData from "../../components/EmptyData";
import MyCreateButton from "../../components/MyCreateButton";
import MyExportButton from '../../components/MyExportButton';
import PageDrawer from "../../components/PageDrawer";
import WaveItemEdit from "./WaveItemEdit";
import MyField from "../../components/MyField";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
import ContentCreate from '@mui/icons-material/Create';
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 />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <NumberInput source="waveId" label="table.field.waveItem.waveId" />,
    <TextInput source="waveCode" label="table.field.waveItem.waveCode" />,
    <NumberInput source="matnrId" label="table.field.waveItem.matnrId" />,
    <TextInput source="maktx" label="table.field.waveItem.matnrName" />,
    <TextInput source="matnrCode" label="table.field.waveItem.matnrCode" />,
    <TextInput source="batch" label="table.field.waveItem.batch" />,
    <TextInput source="splrBatch" label="table.field.waveItem.splrBatch" />,
    <TextInput source="orderCode" label="table.field.waveItem.orderCode" />,
    <NumberInput source="orderItemId" label="table.field.waveItem.orderItemId" />,
    <TextInput source="unit" label="table.field.waveItem.unit" />,
    <TextInput source="trackCode" label="table.field.waveItem.trackCode" />,
    <TextInput source="fieldsIndex" label="table.field.waveItem.fieldsIndex" />,
    <NumberInput source="anfme" label="table.field.waveItem.anfme" />,
    <NumberInput source="workQty" label="table.field.waveItem.workQty" />,
    <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 WaveItemList = () => {
    const translate = useTranslate();
    const waveId = useGetRecordId();
    const [createDialog, setCreateDialog] = useState(false);
    const [editDialog, setEditDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    return (
        <Box display="flex">
            <List
                resource="waveItem"
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.waveItem"}
                empty={false}
                filters={filters}
                filter={{ waveId: waveId }}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <SelectColumnsButton preferenceKey='waveItem' />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='waveItem'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    rowClick={(id, resource, record) => false}
                    expand={false}
                    expandSingle={false}
                    omit={['id', 'createTime', 'matnrId', 'waveId', 'batch', 'orderItemId', 'batch', 'fieldsIndex', 'createBy', 'memo']}
                >
                    <NumberField source="id" />
                    <NumberField source="waveId" label="table.field.waveItem.waveId" />
                    <TextField source="waveCode" label="table.field.waveItem.waveCode" />
                    <NumberField source="matnrId" label="table.field.waveItem.matnrId" />
                    <TextField source="maktx" label="table.field.waveItem.matnrName" />
                    <TextField source="matnrCode" label="table.field.waveItem.matnrCode" />
                    <TextField source="batch" label="table.field.waveItem.batch" />
                    <TextField source="splrBatch" label="table.field.waveItem.splrBatch" />
                    <TextField source="orderCode" label="table.field.waveItem.orderCode" />
                    <NumberField source="orderItemId" label="table.field.waveItem.orderItemId" />
                    <TextField source="unit" label="table.field.waveItem.unit" />
                    <TextField source="trackCode" label="table.field.waveItem.trackCode" />
                    <TextField source="fieldsIndex" label="table.field.waveItem.fieldsIndex" />
                    <NumberField source="anfme" label="table.field.waveItem.anfme" />
                    <NumberField source="workQty" label="table.field.waveItem.workQty" />
                    <TextField source="updateBy$" label="common.field.updateBy" />
                    <DateField source="updateTime" label="common.field.updateTime" showTime />
                    <TextField source="createBy$" label="common.field.createBy" />
                    <DateField source="createTime" label="common.field.createTime" showTime />
                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
                    <TextField source="memo" label="common.field.memo" sortable={false} />
                </StyledDatagrid>
            </List>
            <WaveItemEdit
                open={editDialog}
                setOpen={setEditDialog}
            />
            <WaveItemCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
            <PageDrawer
                title='WaveItem Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
        </Box>
    )
}
export default WaveItemList;
const DetailButton = (setEditDialog) => {
    const record = useRecordContext();
    const editClick = (event) => {
        console.log('--========--->');
        event.stopPropagation()
        setEditDialog(true)
    }
    return (
        <Button  label="ra.action.edit" onClick={editClick} startIcon={<ContentCreate />}/>
    )
}
rsf-admin/src/page/orders/wave/WaveList.jsx
New file
@@ -0,0 +1,204 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from 'react-router-dom';
import {
    List,
    DatagridConfigurable,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    EditButton,
    FilterButton,
    CreateButton,
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    useRecordContext,
    useTranslate,
    useNotify,
    useListContext,
    FunctionField,
    TextField,
    NumberField,
    DateField,
    BooleanField,
    ReferenceField,
    TextInput,
    DateTimeInput,
    DateInput,
    SelectInput,
    NumberInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
    useRefresh,
    useRedirect,
    Button,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import WaveCreate from "./WaveCreate";
import WavePanel from "./WavePanel";
import EmptyData from "../../components/EmptyData";
import MyCreateButton from "../../components/MyCreateButton";
import MyExportButton from '../../components/MyExportButton';
import PageDrawer from "../../components/PageDrawer";
import MyField from "../../components/MyField";
import request from '@/utils/request';
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
import PublicIcon from '@mui/icons-material/Public';
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 />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <TextInput source="code" label="table.field.wave.code" />,
    <SelectInput source="type" label="table.field.wave.type"
        choices={[
            { id: 0, name: '手动' },
            { id: 1, name: '自动' },
        ]}
    />,
    <SelectInput source="exceStatus" label="table.field.wave.exceStatus"
        choices={[
            { id: 0, name: '初始化' },
            { id: 1, name: '生成任务' },
            { id: 2, name: '任务播种' },
            { id: 3, name: '完成' },
        ]}
    />,
    <NumberInput source="anfme" label="table.field.wave.anfme" />,
    <NumberInput source="qty" label="table.field.wave.qty" />,
    <NumberInput source="orderNum" label="table.field.wave.orderNum" />,
    <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 WaveList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    return (
        <Box display="flex">
            <List
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.wave"}
                empty={false}
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <SelectColumnsButton preferenceKey='wave' />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='wave'
                    bulkActionButtons={
                        <PublicTaskButton />
                    }
                    rowClick={(id, resource, record) => false}
                    expand={false}
                    expandSingle={false}
                    omit={['id', 'createTime', 'createBy', 'memo', 'createBy$']}
                >
                    <NumberField source="id" />
                    <TextField source="code" label="table.field.wave.code" />
                    <TextField source="type$" label="table.field.wave.type" sortable={false} />
                    <TextField source="exceStatus$" label="table.field.wave.exceStatus" sortable={false} />
                    <NumberField source="anfme" label="table.field.wave.anfme" />
                    <NumberField source="qty" label="table.field.wave.qty" />
                    <NumberField source="orderNum" label="table.field.wave.orderNum" />
                    <TextField source="updateBy$" label="common.field.updateBy" />
                    <DateField source="updateTime" label="common.field.updateTime" showTime />
                    <TextField source="createBy$" label="common.field.createBy" />
                    <DateField source="createTime" label="common.field.createTime" showTime />
                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
                    <TextField source="memo" label="common.field.memo" sortable={false} />
                    <WrapperField cellClassName="opt" label="common.field.opt">
                        <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
                        <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
                    </WrapperField>
                </StyledDatagrid>
            </List>
            <WaveCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
            <PageDrawer
                title='Wave Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
        </Box>
    )
}
export default WaveList;
const PublicTaskButton = () => {
    const record = useRecordContext();
    const { selectedIds, onUnselectItems } = useListContext();
    const notify = useNotify();
    const refresh = useRefresh();
    const redirect = useRedirect();
    const pubClick = async (event) => {
        event.stopPropagation();
        console.log('=========>');
        onUnselectItems();
        const res = await request.post(`/wave/public/task`, { ids: selectedIds });
        if (res?.data?.code === 200) {
            notify(res.data.msg);
            redirect("/task")
        } else {
            notify(res.data.msg);
        }
        refresh();
    }
    return (
        <Button
            onClick={pubClick}
            label={"toolbar.createTask"}
            startIcon={<PublicIcon />}
        />);
}
rsf-admin/src/page/orders/wave/WavePanel.jsx
New file
@@ -0,0 +1,87 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material';
import {
    useTranslate,
    useRecordContext,
} from 'react-admin';
import PanelTypography from "../../components/PanelTypography";
import * as Common from '@/utils/common'
const WavePanel = () => {
    const record = useRecordContext();
    if (!record) return null;
    const translate = useTranslate();
    return (
        <>
            <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}>
                <CardContent>
                    <Grid container spacing={2}>
                        <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}>
                            <Typography variant="h6" gutterBottom align="left" sx={{
                                maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' },
                                whiteSpace: 'nowrap',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                            }}>
                                {Common.camelToPascalWithSpaces(translate('table.field.wave.id'))}: {record.id}
                            </Typography>
                            {/*  inherit, primary, secondary, textPrimary, textSecondary, error */}
                            <Typography variant="h6" gutterBottom align="right" >
                                ID: {record.id}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Grid container spacing={2}>
                        <Grid item xs={12} container alignContent="flex-end">
                            <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
                                {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Box height={20}>&nbsp;</Box>
                    <Grid container spacing={2}>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.wave.code"
                                property={record.code}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.wave.type"
                                property={record.type$}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.wave.exceStatus"
                                property={record.exceStatus$}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.wave.anfme"
                                property={record.anfme}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.wave.qty"
                                property={record.qty}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.wave.orderNum"
                                property={record.orderNum}
                            />
                        </Grid>
                    </Grid>
                </CardContent>
            </Card >
        </>
    );
};
export default WavePanel;
rsf-admin/src/page/orders/wave/index.jsx
New file
@@ -0,0 +1,18 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    ListGuesser,
    EditGuesser,
    ShowGuesser,
} from "react-admin";
import WaveList from "./WaveList";
import WaveEdit from "./WaveEdit";
export default {
    list: WaveList,
    edit: WaveEdit,
    show: ShowGuesser,
    recordRepresentation: (record) => {
        return `${record.id}`
    }
};
rsf-admin/src/page/warehouseAreas/WarehouseAreasCreate.jsx
@@ -36,7 +36,7 @@
  const translate = useTranslate();
  const notify = useNotify();
  const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_ware_areas_type')) || [];
  const handleClose = (event, reason) => {
    if (reason !== "backdropClick") {
      setOpen(false);
@@ -91,32 +91,29 @@
            </DialogTitle>
            <DialogContent sx={{ mt: 2 }}>
              <Grid container rowSpacing={2} columnSpacing={2}>
                {/* <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.warehouseAreas.uuid"
                    source="uuid"
                    parse={(v) => v}
                    validate={[required()]}
                    autoFocus
                  />
                </Grid> */}
                <Grid item xs={6} display="flex" gap={1}>
                <Grid item xs={12} display="flex" gap={1}>
                  <TextInput
                    label="table.field.warehouseAreas.name"
                    source="name"
                    validate={[required()]}
                    parse={(v) => v}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <TextInput
                    label="table.field.warehouseAreas.code"
                    source="code"
                    validate={[required()]}
                    parse={(v) => v}
                  />
                  <AutocompleteInput
                    choices={dicts}
                    optionText="label"
                    label="table.field.asnOrder.type"
                    source="type"
                    optionValue="value"
                    parse={v => v}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                <Grid item xs={12} display="flex" gap={1}>
                  <ReferenceInput
                    source="warehouseId"
                    reference="warehouse"
@@ -128,8 +125,7 @@
                      filterToQuery={(val) => ({ name: val })}
                    />
                  </ReferenceInput>
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <ReferenceInput source="shipperId" reference="companys" filter={{ type: 'shipper' }}>
                    <AutocompleteInput
                      label="table.field.warehouseAreas.shipperId"
@@ -137,8 +133,6 @@
                      filterToQuery={(val) => ({ name: val })}
                    />
                  </ReferenceInput>
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <ReferenceInput source="supplierId" reference="companys" filter={{ type: 'supplier' }}>
                    <AutocompleteInput
                      label="table.field.warehouseAreas.supplierId"
@@ -147,7 +141,7 @@
                    />
                  </ReferenceInput>
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                <Grid item xs={12} display="flex" gap={1}>
                  <SelectInput
                    label="table.field.warehouseAreas.flagMinus"
                    source="flagMinus"
@@ -157,8 +151,6 @@
                      { id: 1, name: "是" },
                    ]}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <SelectInput
                    label="table.field.warehouseAreas.flagLabelMange"
                    source="flagLabelMange"
@@ -168,8 +160,6 @@
                      { id: 1, name: " 是" },
                    ]}
                  />
                </Grid>
                <Grid item xs={6} display="flex" gap={1}>
                  <SelectInput
                    label="table.field.warehouseAreas.flagMix"
                    source="flagMix"
rsf-admin/src/page/warehouseAreas/WarehouseAreasEdit.jsx
@@ -42,6 +42,7 @@
const WarehouseAreasEdit = () => {
    const translate = useTranslate();
    const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_ware_areas_type')) || [];
    return (
        <Edit
@@ -58,8 +59,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: '100%' }} rowSpacing={3} columnSpacing={3}>
                    <Grid item xs={16} md={10}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.main')}
                        </Typography>
@@ -72,23 +73,27 @@
                                autoFocus
                            />
                        </Stack> */}
                        <Stack direction='row' gap={2}>
                        <Stack xs={16} direction='row' gap={2}>
                            <TextInput
                                label="table.field.warehouseAreas.name"
                                source="name"
                                validate={[required()]}
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.warehouseAreas.code"
                                source="code"
                                validate={[required()]}
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <AutocompleteInput
                                choices={dicts}
                                optionText="label"
                                label="table.field.asnOrder.type"
                                source="type"
                                optionValue="value"
                                parse={v => v}
                            />
                            <ReferenceInput
                                source="warehouseId"
                                reference="warehouse"
@@ -113,8 +118,6 @@
                                    filterToQuery={(val) => ({ name: val })}
                                />
                            </ReferenceInput>
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <ReferenceInput
                                source="supplierId"
                                reference="companys"
@@ -126,8 +129,6 @@
                                    filterToQuery={(val) => ({ name: val })}
                                />
                            </ReferenceInput>
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <SelectInput
                                label="table.field.warehouseAreas.flagMinus"
                                source="flagMinus"
@@ -137,8 +138,6 @@
                                    { id: 1, name: '是' },
                                ]}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <SelectInput
                                label="table.field.warehouseAreas.flagLabelMange"
                                source="flagLabelMange"
@@ -148,8 +147,6 @@
                                    { id: 1, name: ' 是' },
                                ]}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <SelectInput
                                label="table.field.warehouseAreas.flagMix"
                                source="flagMix"
@@ -160,9 +157,8 @@
                                ]}
                            />
                        </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/warehouseAreas/WarehouseAreasList.jsx
@@ -114,9 +114,9 @@
const WarehouseAreasList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    const dicts = JSON.parse(localStorage.getItem('sys_dicts'))?.filter(dict => (dict.dictTypeCode == 'sys_ware_areas_type')) || [];
    return (
        <Box display="flex">
@@ -160,6 +160,7 @@
                    {/* <TextField source="uuid" label="table.field.warehouseAreas.uuid" /> */}
                    <TextField source="name" label="table.field.warehouseAreas.name" />
                    <TextField source="code" label="table.field.warehouseAreas.code" />
                    <TextField source="type$" label="table.field.warehouseAreas.type"/>
                    {/* <ReferenceField source="shipperId" label="table.field.warehouseAreas.shipperId" reference="shipper" link={false} sortable={false}>
                        <TextField source="name" />
                    </ReferenceField> */}
@@ -169,7 +170,6 @@
                    <TextField source="flagMinus$" label="table.field.warehouseAreas.flagMinus" sortable={false} />
                    <TextField source="flagLabelMange$" label="table.field.warehouseAreas.flagLabelMange" sortable={false} />
                    <TextField source="flagMix$" label="table.field.warehouseAreas.flagMix" sortable={false} />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
rsf-admin/src/page/waveItem/WaveItemCreate.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 WaveItemCreate = (props) => {
    const { open, setOpen } = props;
    const translate = useTranslate();
    const notify = useNotify();
    const handleClose = (event, reason) => {
        if (reason !== "backdropClick") {
            setOpen(false);
        }
    };
    const handleSuccess = async (data) => {
        setOpen(false);
        notify('common.response.success');
    };
    const handleError = async (error) => {
        notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } });
    };
    return (
        <>
            <CreateBase
                record={{}}
                transform={(data) => {
                    return data;
                }}
                mutationOptions={{ onSuccess: handleSuccess, onError: handleError }}
            >
                <Dialog
                    open={open}
                    onClose={handleClose}
                    aria-labelledby="form-dialog-title"
                    fullWidth
                    disableRestoreFocus
                    maxWidth="md"   // 'xs' | 'sm' | 'md' | 'lg' | 'xl'
                >
                    <Form>
                        <DialogTitle id="form-dialog-title" sx={{
                            position: 'sticky',
                            top: 0,
                            backgroundColor: 'background.paper',
                            zIndex: 1000
                        }}
                        >
                            {translate('create.title')}
                            <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}>
                                <DialogCloseButton onClose={handleClose} />
                            </Box>
                        </DialogTitle>
                        <DialogContent sx={{ mt: 2 }}>
                            <Grid container rowSpacing={2} columnSpacing={2}>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.waveId"
                                        source="waveId"
                                        autoFocus
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.waveCode"
                                        source="waveCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.matnrId"
                                        source="matnrId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.matnrName"
                                        source="matnrName"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.matnrCode"
                                        source="matnrCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.batch"
                                        source="batch"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.splrBatch"
                                        source="splrBatch"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.orderCode"
                                        source="orderCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.orderItemId"
                                        source="orderItemId"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.unit"
                                        source="unit"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.trackCode"
                                        source="trackCode"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <TextInput
                                        label="table.field.waveItem.fieldsIndex"
                                        source="fieldsIndex"
                                        parse={v => v}
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.anfme"
                                        source="anfme"
                                    />
                                </Grid>
                                <Grid item xs={6} display="flex" gap={1}>
                                    <NumberInput
                                        label="table.field.waveItem.workQty"
                                        source="workQty"
                                    />
                                </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 WaveItemCreate;
rsf-admin/src/page/waveItem/WaveItemEdit.jsx
New file
@@ -0,0 +1,176 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    Edit,
    SimpleForm,
    FormDataConsumer,
    useTranslate,
    TextInput,
    NumberInput,
    BooleanInput,
    DateInput,
    SelectInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    SaveButton,
    Toolbar,
    Labeled,
    NumberField,
    required,
    useRecordContext,
    DeleteButton,
} from 'react-admin';
import { useWatch, useFormContext } from "react-hook-form";
import { Stack, Grid, Box, Typography } from '@mui/material';
import * as Common from '@/utils/common';
import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting';
import EditBaseAside from "../components/EditBaseAside";
import CustomerTopToolBar from "../components/EditTopToolBar";
import MemoInput from "../components/MemoInput";
import StatusSelectInput from "../components/StatusSelectInput";
const FormToolbar = () => {
    const { getValues } = useFormContext();
    return (
        <Toolbar sx={{ justifyContent: 'space-between' }}>
            <SaveButton />
            <DeleteButton mutationMode="optimistic" />
        </Toolbar>
    )
}
const WaveItemEdit = () => {
    const translate = useTranslate();
    return (
        <Edit
            redirect="list"
            mutationMode={EDIT_MODE}
            actions={<CustomerTopToolBar />}
            aside={<EditBaseAside />}
        >
            <SimpleForm
                shouldUnregister
                warnWhenUnsavedChanges
                toolbar={<FormToolbar />}
                mode="onTouched"
                defaultValues={{}}
            // validate={(values) => { }}
            >
                <Grid container width={{ xs: '100%', xl: '80%' }} rowSpacing={3} columnSpacing={3}>
                    <Grid item xs={12} md={8}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.main')}
                        </Typography>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.waveItem.waveId"
                                source="waveId"
                                autoFocus
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.waveCode"
                                source="waveCode"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.waveItem.matnrId"
                                source="matnrId"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.matnrName"
                                source="matnrName"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.matnrCode"
                                source="matnrCode"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.batch"
                                source="batch"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.splrBatch"
                                source="splrBatch"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.orderCode"
                                source="orderCode"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.waveItem.orderItemId"
                                source="orderItemId"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.unit"
                                source="unit"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.trackCode"
                                source="trackCode"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <TextInput
                                label="table.field.waveItem.fieldsIndex"
                                source="fieldsIndex"
                                parse={v => v}
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.waveItem.anfme"
                                source="anfme"
                            />
                        </Stack>
                        <Stack direction='row' gap={2}>
                            <NumberInput
                                label="table.field.waveItem.workQty"
                                source="workQty"
                            />
                        </Stack>
                    </Grid>
                    <Grid item xs={12} md={4}>
                        <Typography variant="h6" gutterBottom>
                            {translate('common.edit.title.common')}
                        </Typography>
                        <StatusSelectInput />
                        <Box mt="2em" />
                        <MemoInput />
                    </Grid>
                </Grid>
            </SimpleForm>
        </Edit >
    )
}
export default WaveItemEdit;
rsf-admin/src/page/waveItem/WaveItemList.jsx
New file
@@ -0,0 +1,178 @@
import React, { useState, useRef, useEffect, useMemo, useCallback } from "react";
import { useNavigate } from 'react-router-dom';
import {
    List,
    DatagridConfigurable,
    SearchInput,
    TopToolbar,
    SelectColumnsButton,
    EditButton,
    FilterButton,
    CreateButton,
    ExportButton,
    BulkDeleteButton,
    WrapperField,
    useRecordContext,
    useTranslate,
    useNotify,
    useListContext,
    FunctionField,
    TextField,
    NumberField,
    DateField,
    BooleanField,
    ReferenceField,
    TextInput,
    DateTimeInput,
    DateInput,
    SelectInput,
    NumberInput,
    ReferenceInput,
    ReferenceArrayInput,
    AutocompleteInput,
    DeleteButton,
} from 'react-admin';
import { Box, Typography, Card, Stack } from '@mui/material';
import { styled } from '@mui/material/styles';
import WaveItemCreate from "./WaveItemCreate";
import WaveItemPanel from "./WaveItemPanel";
import EmptyData from "../components/EmptyData";
import MyCreateButton from "../components/MyCreateButton";
import MyExportButton from '../components/MyExportButton';
import PageDrawer from "../components/PageDrawer";
import MyField from "../components/MyField";
import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting';
import * as Common from '@/utils/common';
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 />,
    <DateInput label='common.time.after' source="timeStart" alwaysOn />,
    <DateInput label='common.time.before' source="timeEnd" alwaysOn />,
    <NumberInput source="waveId" label="table.field.waveItem.waveId" />,
    <TextInput source="waveCode" label="table.field.waveItem.waveCode" />,
    <NumberInput source="matnrId" label="table.field.waveItem.matnrId" />,
    <TextInput source="matnrName" label="table.field.waveItem.matnrName" />,
    <TextInput source="matnrCode" label="table.field.waveItem.matnrCode" />,
    <TextInput source="batch" label="table.field.waveItem.batch" />,
    <TextInput source="splrBatch" label="table.field.waveItem.splrBatch" />,
    <TextInput source="orderCode" label="table.field.waveItem.orderCode" />,
    <NumberInput source="orderItemId" label="table.field.waveItem.orderItemId" />,
    <TextInput source="unit" label="table.field.waveItem.unit" />,
    <TextInput source="trackCode" label="table.field.waveItem.trackCode" />,
    <TextInput source="fieldsIndex" label="table.field.waveItem.fieldsIndex" />,
    <NumberInput source="anfme" label="table.field.waveItem.anfme" />,
    <NumberInput source="workQty" label="table.field.waveItem.workQty" />,
    <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 WaveItemList = () => {
    const translate = useTranslate();
    const [createDialog, setCreateDialog] = useState(false);
    const [drawerVal, setDrawerVal] = useState(false);
    return (
        <Box display="flex">
            <List
                sx={{
                    flexGrow: 1,
                    transition: (theme) =>
                        theme.transitions.create(['all'], {
                            duration: theme.transitions.duration.enteringScreen,
                        }),
                    marginRight: !!drawerVal ? `${PAGE_DRAWER_WIDTH}px` : 0,
                }}
                title={"menu.waveItem"}
                empty={<EmptyData onClick={() => { setCreateDialog(true) }} />}
                filters={filters}
                sort={{ field: "create_time", order: "desc" }}
                actions={(
                    <TopToolbar>
                        <FilterButton />
                        <MyCreateButton onClick={() => { setCreateDialog(true) }} />
                        <SelectColumnsButton preferenceKey='waveItem' />
                        <MyExportButton />
                    </TopToolbar>
                )}
                perPage={DEFAULT_PAGE_SIZE}
            >
                <StyledDatagrid
                    preferenceKey='waveItem'
                    bulkActionButtons={() => <BulkDeleteButton mutationMode={OPERATE_MODE} />}
                    rowClick={(id, resource, record) => false}
                    expand={() => <WaveItemPanel />}
                    expandSingle={true}
                    omit={['id', 'createTime', 'createBy', 'memo']}
                >
                    <NumberField source="id" />
                    <NumberField source="waveId" label="table.field.waveItem.waveId" />
                    <TextField source="waveCode" label="table.field.waveItem.waveCode" />
                    <NumberField source="matnrId" label="table.field.waveItem.matnrId" />
                    <TextField source="matnrName" label="table.field.waveItem.matnrName" />
                    <TextField source="matnrCode" label="table.field.waveItem.matnrCode" />
                    <TextField source="batch" label="table.field.waveItem.batch" />
                    <TextField source="splrBatch" label="table.field.waveItem.splrBatch" />
                    <TextField source="orderCode" label="table.field.waveItem.orderCode" />
                    <NumberField source="orderItemId" label="table.field.waveItem.orderItemId" />
                    <TextField source="unit" label="table.field.waveItem.unit" />
                    <TextField source="trackCode" label="table.field.waveItem.trackCode" />
                    <TextField source="fieldsIndex" label="table.field.waveItem.fieldsIndex" />
                    <NumberField source="anfme" label="table.field.waveItem.anfme" />
                    <NumberField source="workQty" label="table.field.waveItem.workQty" />
                    <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
                    <DateField source="updateTime" label="common.field.updateTime" showTime />
                    <ReferenceField source="createBy" label="common.field.createBy" reference="user" link={false} sortable={false}>
                        <TextField source="nickname" />
                    </ReferenceField>
                    <DateField source="createTime" label="common.field.createTime" showTime />
                    <BooleanField source="statusBool" label="common.field.status" sortable={false} />
                    <TextField source="memo" label="common.field.memo" sortable={false} />
                    <WrapperField cellClassName="opt" label="common.field.opt">
                        <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} />
                        <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} />
                    </WrapperField>
                </StyledDatagrid>
            </List>
            <WaveItemCreate
                open={createDialog}
                setOpen={setCreateDialog}
            />
            <PageDrawer
                title='WaveItem Detail'
                drawerVal={drawerVal}
                setDrawerVal={setDrawerVal}
            >
            </PageDrawer>
        </Box>
    )
}
export default WaveItemList;
rsf-admin/src/page/waveItem/WaveItemPanel.jsx
New file
@@ -0,0 +1,135 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material';
import {
    useTranslate,
    useRecordContext,
} from 'react-admin';
import PanelTypography from "../components/PanelTypography";
import * as Common from '@/utils/common'
const WaveItemPanel = () => {
    const record = useRecordContext();
    if (!record) return null;
    const translate = useTranslate();
    return (
        <>
            <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}>
                <CardContent>
                    <Grid container spacing={2}>
                        <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}>
                            <Typography variant="h6" gutterBottom align="left" sx={{
                                maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' },
                                whiteSpace: 'nowrap',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                            }}>
                                {Common.camelToPascalWithSpaces(translate('table.field.waveItem.id'))}: {record.id}
                            </Typography>
                            {/*  inherit, primary, secondary, textPrimary, textSecondary, error */}
                            <Typography variant="h6" gutterBottom align="right" >
                                ID: {record.id}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Grid container spacing={2}>
                        <Grid item xs={12} container alignContent="flex-end">
                            <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
                                {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo}
                            </Typography>
                        </Grid>
                    </Grid>
                    <Box height={20}>&nbsp;</Box>
                    <Grid container spacing={2}>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.waveId"
                                property={record.waveId}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.waveCode"
                                property={record.waveCode}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.matnrId"
                                property={record.matnrId}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.matnrName"
                                property={record.matnrName}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.matnrCode"
                                property={record.matnrCode}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.batch"
                                property={record.batch}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.splrBatch"
                                property={record.splrBatch}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.orderCode"
                                property={record.orderCode}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.orderItemId"
                                property={record.orderItemId}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.unit"
                                property={record.unit}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.trackCode"
                                property={record.trackCode}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.fieldsIndex"
                                property={record.fieldsIndex}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.anfme"
                                property={record.anfme}
                            />
                        </Grid>
                        <Grid item xs={6}>
                            <PanelTypography
                                title="table.field.waveItem.workQty"
                                property={record.workQty}
                            />
                        </Grid>
                    </Grid>
                </CardContent>
            </Card >
        </>
    );
};
export default WaveItemPanel;
rsf-admin/src/page/waveItem/index.jsx
New file
@@ -0,0 +1,18 @@
import React, { useState, useRef, useEffect, useMemo } from "react";
import {
    ListGuesser,
    EditGuesser,
    ShowGuesser,
} from "react-admin";
import WaveItemList from "./WaveItemList";
import WaveItemEdit from "./WaveItemEdit";
export default {
    list: WaveItemList,
    edit: WaveItemEdit,
    show: ShowGuesser,
    recordRepresentation: (record) => {
        return `${record.id}`
    }
};
rsf-server/src/main/java/com/vincent/rsf/server/common/CodeBuilder.java
@@ -15,16 +15,16 @@
        generator.frontendPrefixPath = "rsf-admin/";
        generator.sqlOsType = SqlOsType.MYSQL;
        generator.url="192.168.4.24:3306/rsf";
        generator.username="root";
        generator.password="34821015";
        generator.url = "127.0.0.1:3306/rsf";
        generator.username = "root";
        generator.password = "34821015";
//        generator.url="47.97.1.152:51433;databasename=jkasrs";
//        generator.username="sa";
//        generator.password="Zoneyung@zy56$";
        generator.table="man_delivery_item";
        generator.tableDesc="综合单据明细";
        generator.packagePath="com.vincent.rsf.server.manager";
        generator.table = "man_device_bind";
        generator.tableDesc = "立体库站点绑定";
        generator.packagePath = "com.vincent.rsf.server.manager";
        generator.build();
    }
rsf-server/src/main/java/com/vincent/rsf/server/common/interceptor/DynamicFieldsInterceptor.java
@@ -1,11 +1,18 @@
package com.vincent.rsf.server.common.interceptor;
import com.vincent.rsf.framework.common.SpringUtils;
import com.vincent.rsf.server.common.utils.FieldsUtils;
import com.vincent.rsf.server.system.entity.Fields;
import com.vincent.rsf.server.system.service.FieldsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
 * @author Ryan
@@ -19,6 +26,14 @@
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        List<Fields> fields = FieldsUtils.getFieldsSta();
        List<String> fieldsKey = fields.stream().filter(item -> item.getFlagEnable() == 1).map(Fields::getFields).collect(Collectors.toList());
        fieldsKey.forEach(key -> {
            if (request.getParameterMap().keySet().contains(key)) {
                request.setAttribute(key, request.getParameterMap().get(key));
            }
        })
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/DeviceBindController.java
New file
@@ -0,0 +1,110 @@
package com.vincent.rsf.server.manager.controller;
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.common.utils.ExcelUtil;
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.manager.entity.DeviceBind;
import com.vincent.rsf.server.manager.service.DeviceBindService;
import com.vincent.rsf.server.system.controller.BaseController;
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
public class DeviceBindController extends BaseController {
    @Autowired
    private DeviceBindService deviceBindService;
    @PreAuthorize("hasAuthority('manager:deviceBind:list')")
    @PostMapping("/deviceBind/page")
    public R page(@RequestBody Map<String, Object> map) {
        BaseParam baseParam = buildParam(map, BaseParam.class);
        PageParam<DeviceBind, BaseParam> pageParam = new PageParam<>(baseParam, DeviceBind.class);
        return R.ok().add(deviceBindService.page(pageParam, pageParam.buildWrapper(true)));
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:list')")
    @PostMapping("/deviceBind/list")
    public R list(@RequestBody Map<String, Object> map) {
        return R.ok().add(deviceBindService.list());
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:list')")
    @PostMapping({"/deviceBind/many/{ids}", "/deviceBinds/many/{ids}"})
    public R many(@PathVariable Long[] ids) {
        return R.ok().add(deviceBindService.listByIds(Arrays.asList(ids)));
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:list')")
    @GetMapping("/deviceBind/{id}")
    public R get(@PathVariable("id") Long id) {
        return R.ok().add(deviceBindService.getById(id));
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:save')")
    @OperationLog("Create 立体库站点绑定")
    @PostMapping("/deviceBind/save")
    public R save(@RequestBody DeviceBind deviceBind) {
//        deviceBind.setCreateBy(getLoginUserId());
//        deviceBind.setCreateTime(new Date());
//        deviceBind.setUpdateBy(getLoginUserId());
//        deviceBind.setUpdateTime(new Date());
        if (!deviceBindService.save(deviceBind)) {
            return R.error("Save Fail");
        }
        return R.ok("Save Success").add(deviceBind);
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:update')")
    @OperationLog("Update 立体库站点绑定")
    @PostMapping("/deviceBind/update")
    public R update(@RequestBody DeviceBind deviceBind) {
//        deviceBind.setUpdateBy(getLoginUserId());
//        deviceBind.setUpdateTime(new Date());
        if (!deviceBindService.updateById(deviceBind)) {
            return R.error("Update Fail");
        }
        return R.ok("Update Success").add(deviceBind);
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:remove')")
    @OperationLog("Delete 立体库站点绑定")
    @PostMapping("/deviceBind/remove/{ids}")
    public R remove(@PathVariable Long[] ids) {
        if (!deviceBindService.removeByIds(Arrays.asList(ids))) {
            return R.error("Delete Fail");
        }
        return R.ok("Delete Success").add(ids);
    }
    @PreAuthorize("hasAuthority('manager:deviceBind:list')")
    @PostMapping("/deviceBind/query")
    public R query(@RequestParam(required = false) String condition) {
        List<KeyValVo> vos = new ArrayList<>();
        LambdaQueryWrapper<DeviceBind> wrapper = new LambdaQueryWrapper<>();
        if (!Cools.isEmpty(condition)) {
            wrapper.like(DeviceBind::getId, condition);
        }
        deviceBindService.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:deviceBind:list')")
    @PostMapping("/deviceBind/export")
    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        ExcelUtil.build(ExcelUtil.create(deviceBindService.list(), DeviceBind.class), response);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/OutStockController.java
@@ -80,7 +80,8 @@
            if (Objects.isNull(ruleCode) || StringUtils.isBlank(ruleCode)) {
                return R.error("编码规则错误:编码「SYS_OUT_STOCK_CODE」是未设置成功!!");
            }
            asnOrder.setCode(ruleCode);
            asnOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)
                    .setCode(ruleCode);
        }
        if (!outStockService.save(asnOrder)) {
            return R.error("Save Fail");
@@ -203,5 +204,14 @@
        return outStockService.genOutStock(ids);
    }
    @PreAuthorize("hasAuthority('manager:outStock:update')")
    @ApiOperation("出库单生成波次")
    @PostMapping("/outStock/generate/wave")
    public R generateWave(@RequestBody Map<String, Object> params) {
        if (Objects.isNull(params)) {
            return R.error("参数不能为空!!");
        }
        List<Long> ids = (List<Long>) params.get("ids");
        return outStockService.generateWaves(ids);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveController.java
New file
@@ -0,0 +1,121 @@
package com.vincent.rsf.server.manager.controller;
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.utils.ExcelUtil;
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.manager.entity.Wave;
import com.vincent.rsf.server.manager.service.WaveService;
import com.vincent.rsf.server.system.controller.BaseController;
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
public class WaveController extends BaseController {
    @Autowired
    private WaveService waveService;
    @PreAuthorize("hasAuthority('manager:wave:list')")
    @PostMapping("/wave/page")
    public R page(@RequestBody Map<String, Object> map) {
        BaseParam baseParam = buildParam(map, BaseParam.class);
        PageParam<Wave, BaseParam> pageParam = new PageParam<>(baseParam, Wave.class);
        return R.ok().add(waveService.page(pageParam, pageParam.buildWrapper(true)));
    }
    @PreAuthorize("hasAuthority('manager:wave:list')")
    @PostMapping("/wave/list")
    public R list(@RequestBody Map<String, Object> map) {
        return R.ok().add(waveService.list());
    }
    @PreAuthorize("hasAuthority('manager:wave:list')")
    @PostMapping({"/wave/many/{ids}", "/waves/many/{ids}"})
    public R many(@PathVariable Long[] ids) {
        return R.ok().add(waveService.listByIds(Arrays.asList(ids)));
    }
    @PreAuthorize("hasAuthority('manager:wave:list')")
    @GetMapping("/wave/{id}")
    public R get(@PathVariable("id") Long id) {
        return R.ok().add(waveService.getById(id));
    }
    @PreAuthorize("hasAuthority('manager:wave:save')")
    @OperationLog("Create 波次单据")
    @PostMapping("/wave/save")
    public R save(@RequestBody Wave wave) {
        wave.setCreateBy(getLoginUserId());
        wave.setUpdateBy(getLoginUserId());
        if (!waveService.save(wave)) {
            return R.error("Save Fail");
        }
        return R.ok("Save Success").add(wave);
    }
    @PreAuthorize("hasAuthority('manager:wave:update')")
    @OperationLog("Update 波次单据")
    @PostMapping("/wave/update")
    public R update(@RequestBody Wave wave) {
        wave.setUpdateBy(getLoginUserId());
        wave.setUpdateTime(new Date());
        if (!waveService.updateById(wave)) {
            return R.error("Update Fail");
        }
        return R.ok("Update Success").add(wave);
    }
    @PreAuthorize("hasAuthority('manager:wave:remove')")
    @OperationLog("Delete 波次单据")
    @PostMapping("/wave/remove/{ids}")
    public R remove(@PathVariable Long[] ids) {
        if (!waveService.removeByIds(Arrays.asList(ids))) {
            return R.error("Delete Fail");
        }
        return R.ok("Delete Success").add(ids);
    }
    @PreAuthorize("hasAuthority('manager:wave:list')")
    @PostMapping("/wave/query")
    public R query(@RequestParam(required = false) String condition) {
        List<KeyValVo> vos = new ArrayList<>();
        LambdaQueryWrapper<Wave> wrapper = new LambdaQueryWrapper<>();
        if (!Cools.isEmpty(condition)) {
            wrapper.like(Wave::getId, condition);
        }
        waveService.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:wave:list')")
    @PostMapping("/wave/export")
    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        ExcelUtil.build(ExcelUtil.create(waveService.list(), Wave.class), response);
    }
    @PreAuthorize("hasAuthority('manager:wave:update')")
    @ApiOperation("波次下发任务")
    @PostMapping("/wave/public/task")
    public R publicTask(@RequestBody Map<String, Object> map) {
        if (Cools.isEmpty(map)) {
            throw new CoolException("参数不能为空!!");
        }
        return waveService.publicTask(map);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WaveItemController.java
New file
@@ -0,0 +1,110 @@
package com.vincent.rsf.server.manager.controller;
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.common.utils.ExcelUtil;
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.manager.entity.WaveItem;
import com.vincent.rsf.server.manager.service.WaveItemService;
import com.vincent.rsf.server.system.controller.BaseController;
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
public class WaveItemController extends BaseController {
    @Autowired
    private WaveItemService waveItemService;
    @PreAuthorize("hasAuthority('manager:waveItem:list')")
    @PostMapping("/waveItem/page")
    public R page(@RequestBody Map<String, Object> map) {
        BaseParam baseParam = buildParam(map, BaseParam.class);
        PageParam<WaveItem, BaseParam> pageParam = new PageParam<>(baseParam, WaveItem.class);
        return R.ok().add(waveItemService.page(pageParam, pageParam.buildWrapper(true)));
    }
    @PreAuthorize("hasAuthority('manager:waveItem:list')")
    @PostMapping("/waveItem/list")
    public R list(@RequestBody Map<String, Object> map) {
        return R.ok().add(waveItemService.list());
    }
    @PreAuthorize("hasAuthority('manager:waveItem:list')")
    @PostMapping({"/waveItem/many/{ids}", "/waveItems/many/{ids}"})
    public R many(@PathVariable Long[] ids) {
        return R.ok().add(waveItemService.listByIds(Arrays.asList(ids)));
    }
    @PreAuthorize("hasAuthority('manager:waveItem:list')")
    @GetMapping("/waveItem/{id}")
    public R get(@PathVariable("id") Long id) {
        return R.ok().add(waveItemService.getById(id));
    }
    @PreAuthorize("hasAuthority('manager:waveItem:save')")
    @OperationLog("Create 波次明细")
    @PostMapping("/waveItem/save")
    public R save(@RequestBody WaveItem waveItem) {
        waveItem.setCreateBy(getLoginUserId());
        waveItem.setCreateTime(new Date());
        waveItem.setUpdateBy(getLoginUserId());
        waveItem.setUpdateTime(new Date());
        if (!waveItemService.save(waveItem)) {
            return R.error("Save Fail");
        }
        return R.ok("Save Success").add(waveItem);
    }
    @PreAuthorize("hasAuthority('manager:waveItem:update')")
    @OperationLog("Update 波次明细")
    @PostMapping("/waveItem/update")
    public R update(@RequestBody WaveItem waveItem) {
        waveItem.setUpdateBy(getLoginUserId());
        waveItem.setUpdateTime(new Date());
        if (!waveItemService.updateById(waveItem)) {
            return R.error("Update Fail");
        }
        return R.ok("Update Success").add(waveItem);
    }
    @PreAuthorize("hasAuthority('manager:waveItem:remove')")
    @OperationLog("Delete 波次明细")
    @PostMapping("/waveItem/remove/{ids}")
    public R remove(@PathVariable Long[] ids) {
        if (!waveItemService.removeByIds(Arrays.asList(ids))) {
            return R.error("Delete Fail");
        }
        return R.ok("Delete Success").add(ids);
    }
    @PreAuthorize("hasAuthority('manager:waveItem:list')")
    @PostMapping("/waveItem/query")
    public R query(@RequestParam(required = false) String condition) {
        List<KeyValVo> vos = new ArrayList<>();
        LambdaQueryWrapper<WaveItem> wrapper = new LambdaQueryWrapper<>();
        if (!Cools.isEmpty(condition)) {
            wrapper.like(WaveItem::getId, condition);
        }
        waveItemService.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:waveItem:list')")
    @PostMapping("/waveItem/export")
    public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception {
        ExcelUtil.build(ExcelUtil.create(waveItemService.list(), WaveItem.class), response);
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/params/DeviceSiteParame.java
@@ -20,7 +20,7 @@
    private List<Long> typeIds;
    @ApiModelProperty("作业站点")
    private String sites;
    private String site;
    @ApiModelProperty("设备编号")
    private String deviceCode;
@@ -28,4 +28,7 @@
    @ApiModelProperty("设备作业站点")
    private String deviceSites;
    @ApiModelProperty("目标站点")
    private String target;
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/AsnOrderItem.java
@@ -55,6 +55,7 @@
    @ApiModelProperty("字段索引")
    private String fieldsIndex;
    @ApiModelProperty("执行数量")
    private Double workQty;
@@ -229,7 +230,7 @@
     */
    @ApiModelProperty(value= "备注")
    private String memo;
//
    public AsnOrderItem() {}
    public AsnOrderItem(Long asnId,String asnCode,Long poDetlId, String matnrCode, String poCode,Long matnrId,String matnk,Double anfme,String stockUnit,Double purQty,String purUnit,Double qty,String splrCode,String splrName,String qrcode,String barcode,String packName,Integer status, Integer ntyStatus,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeliveryItem.java
@@ -59,10 +59,16 @@
    private String matnrCode;
    /**
     * 物料ID
     */
    @ApiModelProperty("物料ID")
    private Long matnrId;
    /**
     * 物料名称
     */
    @ApiModelProperty(value= "物料名称")
    private String matnrName;
    private String maktx;
    /**
     * 动态字段索引
@@ -118,6 +124,18 @@
    @ApiModelProperty(value= "供应商批次")
    private String splrBatch;
    @ApiModelProperty("批次")
    private String batch;
    @ApiModelProperty("跟踪码")
    private String trackCode;
    @ApiModelProperty("生产日期")
    private Date prodTime;
    @ApiModelProperty("包装")
    private String packName;
    /**
     * 状态 1: 正常  0: 冻结  
     */
@@ -171,11 +189,12 @@
    public DeliveryItem() {}
    public DeliveryItem(Long deliveryId,String platItemId,String matnrCode,String matnrName,String fieldsIndex,String unit,Double anfme,Double qty,Double nromQty,Double printQty,String splrName,String splrCode,String splrBatch,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
    public DeliveryItem(Long deliveryId,String platItemId, Long matnrId,String matnrCode,String matnrName,String fieldsIndex,String unit,Double anfme,Double qty,Double nromQty,Double printQty,String splrName,String splrCode,String splrBatch,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
        this.deliveryId = deliveryId;
        this.platItemId = platItemId;
        this.matnrId = matnrId;
        this.matnrCode = matnrCode;
        this.matnrName = matnrName;
        this.maktx = matnrName;
        this.fieldsIndex = fieldsIndex;
        this.unit = unit;
        this.anfme = anfme;
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeviceBind.java
New file
@@ -0,0 +1,133 @@
package com.vincent.rsf.server.manager.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.SpringUtils;
import com.vincent.rsf.server.system.service.UserService;
import com.vincent.rsf.server.system.entity.User;
import java.io.Serializable;
import java.util.Date;
@Data
@TableName("man_device_bind")
public class DeviceBind implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * id
     */
    @ApiModelProperty(value= "id")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 当前排号
     */
    @ApiModelProperty(value= "当前排号")
    private Integer currentRow;
    /**
     * 起始排号
     */
    @ApiModelProperty(value= "起始排号")
    private Integer startRow;
    /**
     * 终止排号
     */
    @ApiModelProperty(value= "终止排号")
    private Integer endRow;
    /**
     * 设备数量
     */
    @ApiModelProperty(value= "设备数量")
    private Integer deviceQty;
    /**
     * 起始设备号
     */
    @ApiModelProperty(value= "起始设备号")
    private Integer startDeviceNo;
    /**
     * 终止设备号
     */
    @ApiModelProperty(value= "终止设备号")
    private Integer endDeviceNo;
    /**
     * 站点list
     */
    @ApiModelProperty(value= "站点list")
    private String staList;
    /**
     * 库区类型
     */
    @ApiModelProperty(value= "库区类型")
    private Integer typeId;
    /**
     * 物料相似开关
     */
    @ApiModelProperty(value= "物料相似开关")
    private String beSimilar;
    /**
     * 空板靠近开关
     */
    @ApiModelProperty(value= "空板靠近开关")
    private String emptySimilar;
    public DeviceBind() {}
    public DeviceBind(Integer currentRow,Integer startRow,Integer endRow,Integer deviceQty,Integer startDeviceNo,Integer endDeviceNo,String staList,Integer typeId,String beSimilar,String emptySimilar) {
        this.currentRow = currentRow;
        this.startRow = startRow;
        this.endRow = endRow;
        this.deviceQty = deviceQty;
        this.startDeviceNo = startDeviceNo;
        this.endDeviceNo = endDeviceNo;
        this.staList = staList;
        this.typeId = typeId;
        this.beSimilar = beSimilar;
        this.emptySimilar = emptySimilar;
    }
//    DeviceBind deviceBind = new DeviceBind(
//            null,    // 当前排号
//            null,    // 起始排号
//            null,    // 终止排号
//            null,    // 设备数量
//            null,    // 起始设备号
//            null,    // 终止设备号
//            null,    // 站点list
//            null,    // 库区类型
//            null,    // 物料相似开关
//            null    // 空板靠近开关
//    );
//    public Boolean getStatusBool(){
//        if (null == this.status){ return null; }
//        switch (this.status){
//            case 1:
//                return true;
//            case 0:
//                return false;
//            default:
//                return null;
//        }
//    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/DeviceSite.java
@@ -64,8 +64,8 @@
    /**
     * wcs站点编号
     */
    @ApiModelProperty(value= "wcs站点编号")
    private String wcsCode;
    @ApiModelProperty(value= "目标位置")
    private String target;
    /**
     * 站点标签
@@ -154,7 +154,7 @@
        this.type = type;
        this.site = site;
        this.name = name;
        this.wcsCode = wcsCode;
        this.target = wcsCode;
        this.label = label;
        this.device = device;
        this.deviceCode = deviceCode;
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WarehouseAreas.java
@@ -3,13 +3,18 @@
import com.baomidou.mybatisplus.annotation.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.vincent.rsf.server.manager.service.CompanysService;
import com.vincent.rsf.server.manager.service.ShipperService;
import com.vincent.rsf.server.manager.service.WarehouseService;
import com.vincent.rsf.server.system.constant.DictTypeCode;
import com.vincent.rsf.server.system.entity.DictData;
import com.vincent.rsf.server.system.service.DictDataService;
import io.swagger.annotations.Api;
import org.apache.commons.lang3.StringUtils;
import org.springframework.format.annotation.DateTimeFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -24,7 +29,7 @@
import com.vincent.rsf.server.system.entity.User;
import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
import java.util.stream.Collectors;
@Data
@TableName("man_warehouse_areas")
@@ -48,7 +53,7 @@
    /**
     * 编号
     */
    @ApiModelProperty(value= "编号")
    @ApiModelProperty(value= "业务类型")
    private String type;
    /**
@@ -156,8 +161,8 @@
    public WarehouseAreas() {}
    public WarehouseAreas(String uuid,String name,String code,Long shipperId,Short supplierId,Short flagMinus,Short flagLabelMange,Short flagMix,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
        this.type = uuid;
    public WarehouseAreas(String type,String name,String code,Long shipperId,Short supplierId,Short flagMinus,Short flagLabelMange,Short flagMix,Integer status,Integer deleted,Integer tenantId,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) {
        this.type = type;
        this.name = name;
        this.code = code;
        this.shipperId = shipperId;
@@ -194,6 +199,18 @@
        return null;
    }
    public String getType$() {
        if (Cools.isEmpty(this.type)){
            return "";
        }
        DictDataService dictDataService = SpringUtils.getBean(DictDataService.class);
        DictData dictData = dictDataService.getOne(new LambdaQueryWrapper<DictData>().eq(DictData::getDictTypeCode, DictTypeCode.SYS_WARE_AREAS_TYPE).eq(DictData::getValue, this.type));
        if (Objects.isNull(dictData)) {
            return null;
        }
        return dictData.getLabel();
    }
    public String getSupplierId$() {
        CompanysService service = SpringUtils.getBean(CompanysService.class);
        Companys supplier = service.getById(this.supplierId);
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Wave.java
New file
@@ -0,0 +1,249 @@
package com.vincent.rsf.server.manager.entity;
import java.text.SimpleDateFormat;
import java.util.Date;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.SpringUtils;
import com.vincent.rsf.server.system.service.UserService;
import com.vincent.rsf.server.system.entity.User;
import java.io.Serializable;
import java.util.Date;
@Data
@TableName("man_wave")
@Accessors(chain = true)
public class Wave implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ID
     */
    @ApiModelProperty(value= "ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 波次号
     */
    @ApiModelProperty(value= "波次号")
    private String code;
    /**
     * 波次类型 0: 手动  1: 自动
     */
    @ApiModelProperty(value= "波次类型 0: 手动  1: 自动  ")
    private Short type;
    /**
     * 波次状态 0: 初始化  1: 生成任务  2: 任务播种  3: 完成
     */
    @ApiModelProperty(value= "波次状态 0: 初始化  1: 生成任务  2: 任务播种  3: 完成  ")
    private Short exceStatus;
    /**
     * 单据数量
     */
    @ApiModelProperty(value= "单据数量")
    private Double anfme;
    /**
     * 已完成数量
     */
    @ApiModelProperty(value= "已完成数量")
    private Double qty;
    /**
     * 单据数量
     */
    @ApiModelProperty(value= "单据数量")
    private Integer orderNum;
    /**
     * 状态 1: 正常  0: 禁用
     */
    @ApiModelProperty(value= "状态 1: 正常  0: 禁用  ")
    private Integer status;
    /**
     * 所属机构
     */
    @ApiModelProperty(value= "所属机构")
    private Long tenantId;
    /**
     * 添加时间
     */
    @ApiModelProperty(value= "添加时间")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * 添加人员
     */
    @ApiModelProperty(value= "添加人员")
    private Long createBy;
    /**
     * 修改时间
     */
    @ApiModelProperty(value= "修改时间")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
     * 修改人员
     */
    @ApiModelProperty(value= "修改人员")
    private Long updateBy;
    /**
     * 是否删除 1: 是  0: 否
     */
    @ApiModelProperty(value= "是否删除 1: 是  0: 否  ")
    @TableLogic
    private Integer deleted;
    /**
     * 备注
     */
    @ApiModelProperty(value= "备注")
    private String memo;
    public Wave() {}
    public Wave(String code,Short type,Short exceStatus,Double anfme,Double qty,Integer orderNum,Integer status,Long tenantId,Date createTime,Long createBy,Date updateTime,Long updateBy,Integer deleted,String memo) {
        this.code = code;
        this.type = type;
        this.exceStatus = exceStatus;
        this.anfme = anfme;
        this.qty = qty;
        this.orderNum = orderNum;
        this.status = status;
        this.tenantId = tenantId;
        this.createTime = createTime;
        this.createBy = createBy;
        this.updateTime = updateTime;
        this.updateBy = updateBy;
        this.deleted = deleted;
        this.memo = memo;
    }
//    Wave wave = new Wave(
//            null,    // 波次号
//            null,    // 波次类型
//            null,    // 波次状态
//            null,    // 单据数量
//            null,    // 已完成数量
//            null,    // 单据数量
//            null,    // 状态
//            null,    // 所属机构
//            null,    // 添加时间
//            null,    // 添加人员
//            null,    // 修改时间
//            null,    // 修改人员
//            null,    // 是否删除
//            null    // 备注
//    );
    public String getType$(){
        if (null == this.type){ return null; }
        switch (this.type){
            case 0:
                return "手动";
            case 1:
                return "自动";
            default:
                return String.valueOf(this.type);
        }
    }
    public String getExceStatus$(){
        if (null == this.exceStatus){ return null; }
        switch (this.exceStatus){
            case 0:
                return "初始化";
            case 1:
                return "生成任务";
            case 2:
                return "任务播种";
            case 3:
                return "完成";
            default:
                return String.valueOf(this.exceStatus);
        }
    }
    public String getStatus$(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return "正常";
            case 0:
                return "禁用";
            default:
                return String.valueOf(this.status);
        }
    }
    public String getCreateTime$(){
        if (Cools.isEmpty(this.createTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
    }
    public String getCreateBy$(){
        UserService service = SpringUtils.getBean(UserService.class);
        User user = service.getById(this.createBy);
        if (!Cools.isEmpty(user)){
            return String.valueOf(user.getNickname());
        }
        return null;
    }
    public String getUpdateTime$(){
        if (Cools.isEmpty(this.updateTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime);
    }
    public String getUpdateBy$(){
        UserService service = SpringUtils.getBean(UserService.class);
        User user = service.getById(this.updateBy);
        if (!Cools.isEmpty(user)){
            return String.valueOf(user.getNickname());
        }
        return null;
    }
    public Boolean getStatusBool(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return true;
            case 0:
                return false;
            default:
                return null;
        }
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/WaveItem.java
New file
@@ -0,0 +1,293 @@
package com.vincent.rsf.server.manager.entity;
import com.baomidou.mybatisplus.annotation.TableLogic;
import java.text.SimpleDateFormat;
import java.util.Date;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.vincent.rsf.framework.common.Cools;
import com.vincent.rsf.framework.common.SpringUtils;
import com.vincent.rsf.server.system.service.UserService;
import com.vincent.rsf.server.system.entity.User;
import java.io.Serializable;
import java.util.Date;
@Data
@Accessors(chain = true)
@TableName("man_wave_item")
public class WaveItem implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * ID
     */
    @ApiModelProperty(value= "ID")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 波次ID
     */
    @ApiModelProperty(value= "波次ID")
    private Long waveId;
    /**
     * 波次号
     */
    @ApiModelProperty(value= "波次号")
    private String waveCode;
    /**
     * 商品ID
     */
    @ApiModelProperty(value= "商品ID")
    private Long matnrId;
    /**
     * 物料名称
     */
    @ApiModelProperty(value= "物料名称")
    private String maktx;
    /**
     * 商品编号
     */
    @ApiModelProperty(value= "商品编号")
    private String matnrCode;
    /**
     * 批号
     */
    @ApiModelProperty(value= "批号")
    private String batch;
    /**
     * 供应商批次
     */
    @ApiModelProperty(value= "供应商批次")
    private String splrBatch;
    /**
     * 单据编码
     */
    @ApiModelProperty(value= "单据编码")
    private String orderCode;
    /**
     * 单据ID
     */
    @ApiModelProperty(value= "单据ID")
    private Long orderItemId;
    @ApiModelProperty("订单ID")
    private Long orderId;
    /**
     * 单位
     */
    @ApiModelProperty(value= "单位")
    private String unit;
    /**
     * 跟踪码
     */
    @ApiModelProperty(value= "跟踪码")
    private String trackCode;
    /**
     * 商品库存索引
     */
    @ApiModelProperty(value= "商品库存索引")
    private String fieldsIndex;
    /**
     * 数量
     */
    @ApiModelProperty(value= "数量")
    private Double anfme;
    @ApiModelProperty("已完成数量")
    private Double qty;
    /**
     * 工作数量
     */
    @ApiModelProperty(value= "工作数量")
    private Double workQty;
    /**
     * 所属机构
     */
    @ApiModelProperty(value= "所属机构")
    private Long tenantId;
    /**
     * 状态 1: 正常  0: 禁用
     */
    @ApiModelProperty(value= "状态 1: 正常  0: 禁用  ")
    private Integer status;
    /**
     * 是否删除 1: 是  0: 否
     */
    @ApiModelProperty(value= "是否删除 1: 是  0: 否  ")
    @TableLogic
    private Integer deleted;
    /**
     * 添加时间
     */
    @ApiModelProperty(value= "添加时间")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    /**
     * 添加人员
     */
    @ApiModelProperty(value= "添加人员")
    private Long createBy;
    /**
     * 修改时间
     */
    @ApiModelProperty(value= "修改时间")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    private Date updateTime;
    /**
     * 修改人员
     */
    @ApiModelProperty(value= "修改人员")
    private Long updateBy;
    /**
     * 备注
     */
    @ApiModelProperty(value= "备注")
    private String memo;
    public WaveItem() {}
    public WaveItem(Long waveId,String waveCode,Long matnrId,String matnrName,String matnrCode,String batch,String splrBatch,String orderCode, Long orderId,Long orderItemId,String unit,String trackCode,String fieldsIndex,Double anfme,Double workQty,Long tenantId,Integer status,Integer deleted,Date createTime,Long createBy,Date updateTime,Long updateBy,String memo) {
        this.waveId = waveId;
        this.waveCode = waveCode;
        this.matnrId = matnrId;
        this.maktx = matnrName;
        this.matnrCode = matnrCode;
        this.batch = batch;
        this.orderId = orderId;
        this.splrBatch = splrBatch;
        this.orderCode = orderCode;
        this.orderItemId = orderItemId;
        this.unit = unit;
        this.trackCode = trackCode;
        this.fieldsIndex = fieldsIndex;
        this.anfme = anfme;
        this.workQty = workQty;
        this.tenantId = tenantId;
        this.status = status;
        this.deleted = deleted;
        this.createTime = createTime;
        this.createBy = createBy;
        this.updateTime = updateTime;
        this.updateBy = updateBy;
        this.memo = memo;
    }
//    WaveItem waveItem = new WaveItem(
//            null,    // 波次ID
//            null,    // 波次号
//            null,    // 商品ID
//            null,    // 物料名称
//            null,    // 商品编号
//            null,    // 批号
//            null,    // 供应商批次
//            null,    // 单据编码
//            null,    // 单据ID
//            null,    // 单位
//            null,    // 跟踪码
//            null,    // 商品库存索引
//            null,    // 数量
//            null,    // 工作数量
//            null,    // 所属机构
//            null,    // 状态
//            null,    // 是否删除
//            null,    // 添加时间
//            null,    // 添加人员
//            null,    // 修改时间
//            null,    // 修改人员
//            null    // 备注
//    );
    public String getStatus$(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return "正常";
            case 0:
                return "禁用";
            default:
                return String.valueOf(this.status);
        }
    }
    public String getCreateTime$(){
        if (Cools.isEmpty(this.createTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
    }
    public String getCreateBy$(){
        UserService service = SpringUtils.getBean(UserService.class);
        User user = service.getById(this.createBy);
        if (!Cools.isEmpty(user)){
            return String.valueOf(user.getNickname());
        }
        return null;
    }
    public String getUpdateTime$(){
        if (Cools.isEmpty(this.updateTime)){
            return "";
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime);
    }
    public String getUpdateBy$(){
        UserService service = SpringUtils.getBean(UserService.class);
        User user = service.getById(this.updateBy);
        if (!Cools.isEmpty(user)){
            return String.valueOf(user.getNickname());
        }
        return null;
    }
    public Boolean getStatusBool(){
        if (null == this.status){ return null; }
        switch (this.status){
            case 1:
                return true;
            case 0:
                return false;
            default:
                return null;
        }
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/AsnExceStatus.java
@@ -14,8 +14,14 @@
    ASN_EXCE_STATUS_EXCE_ING("1", "执行中"),
    ASN_EXCE_STATUS_TASK_DONE("2", "已完成"),
    ASN_EXCE_STATUS_TASK_CANCEL("3", "取消"),
    ASN_EXCE_STATUS_TASK_CLOSE("4", "已关闭")
            ;
    ASN_EXCE_STATUS_TASK_CLOSE("4", "已关闭"),
    OUT_STOCK_STATUS_TASK_INIT("5", "初始化"),
    OUT_STOCK_STATUS_TASK_EXCE("6", "待处理"),
    OUT_STOCK_STATUS_TASK_WAVE("7", "生成波次"),
    OUT_STOCK_STATUS_TASK_WORKING("8", "作业中")
    ;
    AsnExceStatus(String val, String desc) {
        this.val = Short.parseShort(val);
        this.desc = desc;
@@ -35,6 +41,10 @@
            return AsnExceStatus.ASN_EXCE_STATUS_TASK_CANCEL.desc;
        } else if (val.equals(AsnExceStatus.ASN_EXCE_STATUS_TASK_CLOSE.val)) {
            return AsnExceStatus.ASN_EXCE_STATUS_TASK_CLOSE.desc;
        } else if (val.equals(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)) {
            return AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.desc;
        } else if (val.equals(AsnExceStatus.OUT_STOCK_STATUS_TASK_EXCE.val)) {
            return AsnExceStatus.OUT_STOCK_STATUS_TASK_EXCE.desc;
        } else {
            return null;
        }
@@ -51,6 +61,10 @@
            return AsnExceStatus.ASN_EXCE_STATUS_TASK_CANCEL.val;
        } else if (desc.equals(AsnExceStatus.ASN_EXCE_STATUS_TASK_CLOSE.desc)) {
            return AsnExceStatus.ASN_EXCE_STATUS_TASK_CLOSE.val;
        }else if (desc.equals(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.desc)) {
            return AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val;
        } else if (desc.equals(AsnExceStatus.OUT_STOCK_STATUS_TASK_EXCE.desc)) {
            return AsnExceStatus.OUT_STOCK_STATUS_TASK_EXCE.val;
        } else {
            return null;
        }
rsf-server/src/main/java/com/vincent/rsf/server/manager/enums/WaveExceStatus.java
New file
@@ -0,0 +1,27 @@
package com.vincent.rsf.server.manager.enums;
/**
 * @author Ryan
 * @version 1.0
 * @title WaveExceStatus
 * @description
 * @create 2025/4/24 17:56
 */
public enum WaveExceStatus {
    //波次执行状态
    WAVE_EXCE_STATUS_INIT("0", "初始化"),
    WAVE_EXCE_STATUS_TASK("1", "生成任务"),
    WAVE_EXCE_STATUS_SPEED("2", "播种任务"),
    WAVE_EXCE_STATUS_DONE("3", "完成"),
    ;
    WaveExceStatus(String val, String desc) {
        this.val = Short.parseShort(val);
        this.desc = desc;
    }
    public Short val;
    public String desc;
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/DeviceBindMapper.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.mapper;
import com.vincent.rsf.server.manager.entity.DeviceBind;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface DeviceBindMapper extends BaseMapper<DeviceBind> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/WaveItemMapper.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.mapper;
import com.vincent.rsf.server.manager.entity.WaveItem;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface WaveItemMapper extends BaseMapper<WaveItem> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/WaveMapper.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.mapper;
import com.vincent.rsf.server.manager.entity.Wave;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface WaveMapper extends BaseMapper<Wave> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/DeviceBindService.java
New file
@@ -0,0 +1,8 @@
package com.vincent.rsf.server.manager.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.vincent.rsf.server.manager.entity.DeviceBind;
public interface DeviceBindService extends IService<DeviceBind> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/OutStockService.java
@@ -14,4 +14,6 @@
    R cancelOutOrder(String id);
    R genOutStock(List<Long> ids);
    R generateWaves(List<Long> ids);
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WaveItemService.java
New file
@@ -0,0 +1,8 @@
package com.vincent.rsf.server.manager.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.vincent.rsf.server.manager.entity.WaveItem;
public interface WaveItemService extends IService<WaveItem> {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WaveService.java
New file
@@ -0,0 +1,19 @@
package com.vincent.rsf.server.manager.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.vincent.rsf.framework.common.R;
import com.vincent.rsf.server.manager.entity.Wave;
import java.util.Map;
public interface WaveService extends IService<Wave> {
    /**
     * @author Ryan
     * @description 波次任务下发
     * @param
     * @return
     * @time 2025/4/25 16:24
     */
    R publicTask(Map<String, Object> map);
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/DeviceBindServiceImpl.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.service.impl;
import com.vincent.rsf.server.manager.mapper.DeviceBindMapper;
import com.vincent.rsf.server.manager.entity.DeviceBind;
import com.vincent.rsf.server.manager.service.DeviceBindService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service("deviceBindService")
public class DeviceBindServiceImpl extends ServiceImpl<DeviceBindMapper, DeviceBind> implements DeviceBindService {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/DeviceSiteServiceImpl.java
@@ -37,25 +37,32 @@
        if (Objects.isNull(param.getDeviceSites()) || StringUtils.isBlank(param.getDeviceSites())) {
            throw new CoolException("初始化失败: 设备作业站点不能为空!!");
        }
        if (Objects.isNull(param.getSites()) || StringUtils.isBlank(param.getSites())) {
        if (Objects.isNull(param.getSite()) || StringUtils.isBlank(param.getSite())) {
            throw new CoolException("初始化失败: 作业站点不能为空!!");
        }
        if (Objects.isNull(param.getTypeIds()) || param.getTypeIds().isEmpty()) {
            throw new CoolException("初始化失败: 作业类型不能为空!!");
        }
        List<String> sites = Arrays.asList(StringUtils.split(param.getSites(), ","));
        if (Objects.isNull(param.getTarget()) || param.getTarget().isEmpty()) {
            throw new CoolException("初始化失败: 目标站点不能为空!!");
        }
        List<String> sites = Arrays.asList(StringUtils.split(param.getSite(), ","));
        List<String> dvSites = Arrays.asList(StringUtils.split(param.getDeviceSites(), ","));
        List<String> targets = Arrays.asList(StringUtils.split(param.getTarget(), ","));
        List<DeviceSite> deviceSites =  new ArrayList<>();
        for (String site : sites) {
            for (String deviceSite : dvSites) {
                for (Long id : param.getTypeIds()) {
                    DeviceSite site1 = new DeviceSite();
                    site1.setType(id + "")
                            .setSite(site)
                            .setDevice(param.getDeviceType())
                            .setDeviceSite(deviceSite)
                            .setDeviceCode(param.getDeviceCode());
                    deviceSites.add(site1);
                    for (String target : targets) {
                        DeviceSite site1 = new DeviceSite();
                        site1.setType(id + "")
                                .setSite(site)
                                .setDevice(param.getDeviceType())
                                .setDeviceSite(deviceSite)
                                .setTarget(target)
                                .setDeviceCode(param.getDeviceCode());
                        deviceSites.add(site1);
                    }
                }
            }
        }
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/OutStockServiceImpl.java
@@ -6,18 +6,14 @@
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.api.entity.dto.PoItemsDto;
import com.vincent.rsf.server.api.service.ReceiveMsgService;
import com.vincent.rsf.server.api.service.ReportMsgService;
import com.vincent.rsf.server.manager.controller.params.AsnOrderAndItemsParams;
import com.vincent.rsf.server.manager.controller.params.BatchUpdateParam;
import com.vincent.rsf.server.manager.entity.*;
import com.vincent.rsf.server.manager.enums.AsnExceStatus;
import com.vincent.rsf.server.manager.enums.WaveExceStatus;
import com.vincent.rsf.server.manager.mapper.AsnOrderMapper;
import com.vincent.rsf.server.manager.mapper.PurchaseMapper;
import com.vincent.rsf.server.manager.service.*;
import com.vincent.rsf.server.manager.utils.GroupMergeUtil;
import com.vincent.rsf.server.system.constant.SerialRuleCode;
import com.vincent.rsf.server.system.mapper.SerialRuleMapper;
import com.vincent.rsf.server.system.utils.SerialRuleUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
@@ -25,7 +21,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
@@ -45,15 +40,16 @@
    private AsnOrderLogService asnOrderLogService;
    @Autowired
    private AsnOrderItemLogService asnOrderItemLogService;
    @Autowired
    private DeliveryItemService deliveryItemService;
    @Autowired
    private DeliveryService deliveryService;
    @Autowired
    private MatnrService matnrService;
    @Autowired
    private WaveService waveService;
    @Autowired
    private WaveItemService waveItemService;
    /**
@@ -157,6 +153,7 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R cancelOutOrder(String id) {
        //TODO 出库单取消流程,QMS(单据取消)->DO单->出库单->波次->判断是否全单据->全单据下发取消任务至WCS,非全单数据取消删除流程所有关联数据
        if (Cools.isEmpty(id)) {
            throw new CoolException("参数不能为空!!");
        }
@@ -205,7 +202,7 @@
            if (Objects.isNull(ruleCode) || StringUtils.isBlank(ruleCode)) {
                throw new CoolException("编码规则错误:请检查 「SYS_OUT_STOCK_CODE」编码是否设置成功");
            }
            order.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val)
            order.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)
                    .setCode(ruleCode)
                    .setPoId(delivery.getId())
                    .setId(null)
@@ -219,6 +216,9 @@
                BeanUtils.copyProperties(item, orderItem);
                orderItem.setId(null)
                        .setPoCode(order.getPoCode())
                        .setMaktx(item.getMaktx())
                        .setMatnrCode(item.getMatnrCode())
                        .setFieldsIndex(item.getFieldsIndex())
                        .setAsnId(order.getId())
                        .setAsnCode(order.getCode())
                        .setPlatItemId(item.getPlatItemId())
@@ -238,4 +238,118 @@
        });
        return R.ok();
    }
    /**
     * @param
     * @return
     * @author Ryan
     * @description 生成波次
     * @time 2025/4/24 15:04
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R generateWaves(List<Long> ids) {
        if (Objects.isNull(ids) || ids.isEmpty()) {
            throw new CoolException("参数不能为空!!");
        }
        List<AsnOrder> orders = this.list(new LambdaQueryWrapper<AsnOrder>()
                .in(AsnOrder::getId, ids)
                .eq(AsnOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val));
        if (orders.isEmpty()) {
            throw new CoolException("当前单据状态不能执行波次生成操作!!");
        }
        double sum = orders.stream().mapToDouble(AsnOrder::getAnfme).sum();
        Wave wave = new Wave();
        String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_WAVE_TYPE, null);
        if (Objects.isNull(ruleCode) || StringUtils.isBlank(ruleCode)) {
            throw new CoolException("编码规则错误:请要查看「SYS_WAVE_TYPE」是否设置成功!!");
        }
        wave.setOrderNum(ids.size())
                .setType(Short.parseShort("1"))
                .setCode(ruleCode)
                .setExceStatus(WaveExceStatus.WAVE_EXCE_STATUS_INIT.val)
                .setAnfme(sum);
        if (!waveService.save(wave)) {
            throw new CoolException("波次保存失败!!");
        }
        List<Long> list = orders.stream().map(AsnOrder::getId).collect(Collectors.toList());
        List<AsnOrderItem> orderItems = asnOrderItemService
                .list(new LambdaQueryWrapper<AsnOrderItem>()
                        .in(AsnOrderItem::getAsnId, list));
        if (orderItems.isEmpty()) {
            throw new CoolException("单据不存在!!");
        }
        List<WaveItem> waveItems = mergeWave(orderItems, wave);
        if (!waveItemService.saveBatch(waveItems)) {
            throw new CoolException("波次明细保存失败!!");
        }
        double sum1 = waveItems.stream().mapToDouble(WaveItem::getAnfme).sum();
        wave.setAnfme(sum1);
        if (!waveService.saveOrUpdate(wave)) {
            throw new CoolException("主单修改失败!!");
        }
        if (!this.update(new LambdaUpdateWrapper<AsnOrder>()
                .set(AsnOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_WAVE.val)
                .in(AsnOrder::getId, ids))) {
            throw new CoolException("执行状态修改修改失败!!");
        }
        return R.ok("操作完成!!");
    }
    /**
     * @param
     * @param wave
     * @return
     * @author Ryan
     * @description 合并生成波次
     * @time 2025/4/25 10:07
     */
    private List<WaveItem> mergeWave(List<AsnOrderItem> orderItems, Wave wave) {
        List<WaveItem> items = new ArrayList<>();
        orderItems.forEach(order -> {
            WaveItem item = new WaveItem();
            BeanUtils.copyProperties(order, item);
            item.setOrderItemId(order.getId())
                    .setId(null)
                    .setOrderCode(order.getAsnCode())
                    .setOrderId(order.getAsnId())
                    .setMatnrId(order.getMatnrId())
                    .setMaktx(order.getMaktx())
                    .setWaveId(wave.getId())
                    .setWaveCode(wave.getCode());
            items.add(item);
        });
        List<WaveItem> waveItems = GroupMergeUtil.groupAndMerge(items,
                (p1, p2) -> new WaveItem(
                        p1.getWaveId(),
                        p1.getWaveCode(),
                        p1.getMatnrId(),
                        p1.getMaktx(),
                        p1.getMatnrCode(),
                        p1.getBatch(),
                        p1.getSplrBatch(),
                        p1.getOrderCode(),
                        p1.getOrderId(),
                        p1.getOrderItemId(),
                        p1.getUnit(),
                        p1.getTrackCode(),
                        p1.getFieldsIndex(),
                        p1.getAnfme() + p2.getAnfme(),
                        p1.getWorkQty(),
                        p1.getTenantId(),
                        p1.getStatus(),
                        p1.getDeleted(),
                        p1.getCreateTime(),
                        p1.getCreateBy(),
                        p1.getUpdateTime(),
                        p1.getUpdateBy(),
                        p1.getMemo()
                ),
                WaveItem::getSplrBatch, WaveItem::getMatnrCode, WaveItem::getFieldsIndex
        );
        return waveItems;
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveItemServiceImpl.java
New file
@@ -0,0 +1,12 @@
package com.vincent.rsf.server.manager.service.impl;
import com.vincent.rsf.server.manager.mapper.WaveItemMapper;
import com.vincent.rsf.server.manager.entity.WaveItem;
import com.vincent.rsf.server.manager.service.WaveItemService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service("waveItemService")
public class WaveItemServiceImpl extends ServiceImpl<WaveItemMapper, WaveItem> implements WaveItemService {
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WaveServiceImpl.java
New file
@@ -0,0 +1,64 @@
package com.vincent.rsf.server.manager.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
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.manager.entity.AsnOrder;
import com.vincent.rsf.server.manager.entity.AsnOrderItem;
import com.vincent.rsf.server.manager.enums.WaveExceStatus;
import com.vincent.rsf.server.manager.mapper.WaveMapper;
import com.vincent.rsf.server.manager.entity.Wave;
import com.vincent.rsf.server.manager.service.AsnOrderItemService;
import com.vincent.rsf.server.manager.service.AsnOrderService;
import com.vincent.rsf.server.manager.service.WaveService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Service("waveService")
public class WaveServiceImpl extends ServiceImpl<WaveMapper, Wave> implements WaveService {
    @Autowired
    private AsnOrderItemService asnOrderItemService;
    @Autowired
    private AsnOrderService asnOrderService;
    /**
     * @author Ryan
     * @description 波次任务下发
     * @param
     * @return
     * @time 2025/4/25 16:24
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R publicTask(Map<String, Object> map) {
        List<Long> ids = (List<Long>) map.get("ids");
        if (Objects.isNull(ids) || ids.isEmpty()) {
            throw new CoolException("参数不能为空!!");
        }
        List<Wave> waves = this.list(new LambdaQueryWrapper<Wave>().in(Wave::getId, ids));
        if (Objects.isNull(waves) || waves.isEmpty()) {
            throw new CoolException("波次数据不存在!!");
        }
        List<AsnOrder> orders = asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().eq(AsnOrder::getId, ids));
        asnOrderItemService.list(new LambdaQueryWrapper<AsnOrderItem>().eq(AsnOrderItem::getAsnId, ids));
        if (!this.update(new LambdaUpdateWrapper<Wave>().set(Wave::getExceStatus, WaveExceStatus.WAVE_EXCE_STATUS_TASK).in(Wave::getId,ids))) {
            throw new CoolException("波次状态修改失败!!");
        }
        return R.ok();
    }
}
rsf-server/src/main/java/com/vincent/rsf/server/manager/utils/GroupMergeUtil.java
New file
@@ -0,0 +1,154 @@
package com.vincent.rsf.server.manager.utils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.JsonObject;
import lombok.Data;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
 * @author Ryan
 * @description 根据对象多个属性合并处理
 * @param
 * @return
 * @time 2025/4/25 10:55
 */
public class GroupMergeUtil {
    /**
     * 根据多个属性分组合并对象列表
     *
     * @param list       待分组的对象列表
     * @param mergers    合并函数,定义如何合并同一组中的对象
     * @param keyGetters 分组属性的getter方法数组
     * @param <T>        对象类型
     * @param <K>        分组键类型
     * @return 分组合并后的对象列表
     */
    @SafeVarargs
    public static <T, K> List<T> groupAndMerge(
            List<T> list,
            BinaryOperator<T> mergers,
            Function<T, K>... keyGetters) {
        return new ArrayList<>(list.stream()
                .collect(Collectors.toMap(
                        item -> new GroupingKeys<>(Arrays.stream(keyGetters)
                                .map(getter -> getter.apply(item))
                                .toArray()),
                        Function.identity(),
                        mergers))
                .values());
    }
    /**
     * @author Ryan
     * @description 用于存储多个分组键的内部类
     * @param
     * @return
     * @time 2025/4/25 10:56
     */
    private static class GroupingKeys<K> {
        private final Object[] keys;
        private final int hashCode;
        public GroupingKeys(Object[] keys) {
            this.keys = keys;
            this.hashCode = Arrays.deepHashCode(keys);
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            GroupingKeys<?> that = (GroupingKeys<?>) o;
            return Arrays.deepEquals(keys, that.keys);
        }
        @Override
        public int hashCode() {
            return hashCode;
        }
    }
    // 示例使用
//    public static void main(String[] args) {
//        // 示例数据类
//        @Data
//        class Product {
//         private String category;
//            private String subCategory;
//            private String abc;
//
//            private String dbc;
//
//            private String brand;
//            private int quantity;
//            private double price;
//
//            public Product(String category, String abc, String dbc, String subCategory, String brand, int quantity, double price) {
//                this.category = category;
//                this.subCategory = subCategory;
//                this.abc = abc;
//                this.dbc = dbc;
//                this.brand = brand;
//                this.quantity = quantity;
//                this.price = price;
//            }
//
//            // getters...
////            public String getCategory() { return category; }
////            public String getSubCategory() { return subCategory; }
////            public String getBrand() { return brand; }
////            public int getQuantity() { return quantity; }
////            public double getPrice() { return price; }
//
//            @Override
//            public String toString() {
//                return String.format("[%s, %s, %s] - Qty: %d, Price: %.2f",
//                        category, subCategory, brand, quantity, price);
//            }
//        }
//
//        // 创建测试数据
//        List<Product> products = Arrays.asList(
//                new Product("Electronics", "Phone", "Apple", 10, 999.99),
//                new Product("Electronics", "Phone", "Samsung", 15, 899.99),
//                new Product("Electronics", "Phone", "Apple", 5, 999.99),
//                new Product("Electronics", "Tablet", "Apple", 8, 799.99),
//                new Product("Clothing", "Shirt", "Nike", 20, 29.99),
//                new Product("Clothing", "Shirt", "Nike", 10, 29.99),
//                new Product("Clothing", "Pants", "Adidas", 12, 49.99)
//        );
//
//        // 按 category, subCategory, brand 分组,合并 quantity 相加,price 取平均值
//        List<Product> mergedProducts = groupAndMerge(
//                products,
//                (p1, p2) -> new Product(
//                        p1.category,
//                        p1.subCategory,
//                        p1.brand,
//                        p1.quantity + p2.quantity,
//                        (p1.price + p2.price) / 2  // 这里简单平均,实际业务可能不同
//                ),
//                Product::getCategory,
//                Product::getSubCategory,
//                Product::getBrand
//        );
//
////        // 打印结果
////        System.out.println("合并后的产品列表:");
////        System.out.println(JSONArray.toJSONString(mergedProducts));
////        mergedProducts.forEach(item -> {
////            System.out.println(JSONObject.toJSONString(item));
////        });
////        mergedProducts.forEach(System.out::println);
//    }
}
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/DictTypeCode.java
@@ -54,4 +54,9 @@
     */
    public final static String DICT_ASN_EXCE_STATUS = "sys_asn_exce_status";
    /**
     * 仓库库区类型
     */
    public final static String SYS_WARE_AREAS_TYPE = "sys_ware_areas_type";
}
rsf-server/src/main/java/com/vincent/rsf/server/system/constant/SerialRuleCode.java
@@ -69,4 +69,9 @@
     */
    public final static String SYS_OUT_STOCK_CODE = "sys_out_stock_code";
    /**
     * 波次类型
     */
    public final static String SYS_WAVE_TYPE = "sys_wave_type";
}
rsf-server/src/main/java/delivery.sql
File was deleted
rsf-server/src/main/java/deliveryItem.sql
File was deleted
rsf-server/src/main/java/locItem.sql
File was deleted
rsf-server/src/main/java/wave.sql
New file
@@ -0,0 +1,27 @@
-- save wave record
-- mysql
insert into `sys_menu` ( `name`, `parent_id`, `route`, `component`, `type`, `sort`, `tenant_id`, `status`) values ( 'menu.wave', '0', '/manager/wave', 'wave', '0' , '0', '1' , '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Query 波次单据', '', '1', 'manager:wave:list', '0', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Create 波次单据', '', '1', 'manager:wave:save', '1', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Update 波次单据', '', '1', 'manager:wave:update', '2', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Delete 波次单据', '', '1', 'manager:wave:remove', '3', '1', '1');
-- locale menu name
wave: 'Wave',
-- locale field
wave: {
    code: "code",
    type: "type",
    exceStatus: "exceStatus",
    anfme: "anfme",
    qty: "qty",
    orderNum: "orderNum",
},
-- ResourceContent
import wave from './wave';
case 'wave':
    return wave;
rsf-server/src/main/java/waveItem.sql
New file
@@ -0,0 +1,35 @@
-- save waveItem record
-- mysql
insert into `sys_menu` ( `name`, `parent_id`, `route`, `component`, `type`, `sort`, `tenant_id`, `status`) values ( 'menu.waveItem', '0', '/manager/waveItem', 'waveItem', '0' , '0', '1' , '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Query 波次明细', '', '1', 'manager:waveItem:list', '0', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Create 波次明细', '', '1', 'manager:waveItem:save', '1', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Update 波次明细', '', '1', 'manager:waveItem:update', '2', '1', '1');
insert into `sys_menu` ( `name`, `parent_id`, `type`, `authority`, `sort`, `tenant_id`, `status`) values ( 'Delete 波次明细', '', '1', 'manager:waveItem:remove', '3', '1', '1');
-- locale menu name
waveItem: 'WaveItem',
-- locale field
waveItem: {
    waveId: "waveId",
    waveCode: "waveCode",
    matnrId: "matnrId",
    matnrName: "matnrName",
    matnrCode: "matnrCode",
    batch: "batch",
    splrBatch: "splrBatch",
    orderCode: "orderCode",
    orderItemId: "orderItemId",
    unit: "unit",
    trackCode: "trackCode",
    fieldsIndex: "fieldsIndex",
    anfme: "anfme",
    workQty: "workQty",
},
-- ResourceContent
import waveItem from './waveItem';
case 'waveItem':
    return waveItem;
rsf-server/src/main/resources/mapper/manager/DeviceBindMapper.xml
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vincent.rsf.server.manager.mapper.DeviceBindMapper">
</mapper>
rsf-server/src/main/resources/mapper/manager/WaveItemMapper.xml
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vincent.rsf.server.manager.mapper.WaveItemMapper">
</mapper>
rsf-server/src/main/resources/mapper/manager/WaveMapper.xml
New file
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.vincent.rsf.server.manager.mapper.WaveMapper">
</mapper>